Merge "Fix ImageWriter::ComputeEagerResolvedStringsCallback()."
diff --git a/Android.mk b/Android.mk
index d11d011..76c3aa5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -101,9 +101,11 @@
include $(art_path)/dex2oat/Android.mk
include $(art_path)/disassembler/Android.mk
include $(art_path)/oatdump/Android.mk
+include $(art_path)/imgdiag/Android.mk
include $(art_path)/patchoat/Android.mk
include $(art_path)/dalvikvm/Android.mk
include $(art_path)/tools/Android.mk
+include $(art_path)/tools/dexfuzz/Android.mk
include $(art_path)/sigchainlib/Android.mk
@@ -313,11 +315,7 @@
# $(1): input jar or apk target location
define declare-oat-target-target
-ifneq (,$(filter $(1),$(addprefix system/app/,$(addsuffix .apk,$(PRODUCT_DEX_PREOPT_PACKAGES_IN_DATA)))))
-OUT_OAT_FILE := $(call dalvik-cache-out,$(1)/classes.dex)
-else
OUT_OAT_FILE := $(PRODUCT_OUT)/$(basename $(1)).odex
-endif
ifeq ($(ONE_SHOT_MAKEFILE),)
# ONE_SHOT_MAKEFILE is empty for a top level build and we don't want
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 39e78fa..0f756ef 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -14,12 +14,27 @@
# limitations under the License.
#
-ifndef ANDROID_COMMON_MK
-ANDROID_COMMON_MK = true
+ifndef ART_ANDROID_COMMON_MK
+ART_ANDROID_COMMON_MK = true
-ART_TARGET_SUPPORTED_ARCH := arm arm64 mips x86 x86_64
+ART_TARGET_SUPPORTED_ARCH := arm arm64 mips mips64 x86 x86_64
ART_HOST_SUPPORTED_ARCH := x86 x86_64
+ART_COVERAGE := false
+
+ifeq ($(ART_COVERAGE),true)
+# https://gcc.gnu.org/onlinedocs/gcc/Cross-profiling.html
+GCOV_PREFIX := /data/local/tmp/gcov
+# GCOV_PREFIX_STRIP is an integer that defines how many levels should be
+# stripped off the beginning of the path. We want the paths in $GCOV_PREFIX to
+# be relative to $ANDROID_BUILD_TOP so we can just adb pull from the top and not
+# have to worry about placing things ourselves.
+GCOV_PREFIX_STRIP := $(shell echo $(ANDROID_BUILD_TOP) | grep -o / | wc -l)
+GCOV_ENV := GCOV_PREFIX=$(GCOV_PREFIX) GCOV_PREFIX_STRIP=$(GCOV_PREFIX_STRIP)
+else
+GCOV_ENV :=
+endif
+
ifeq (,$(filter $(TARGET_ARCH),$(ART_TARGET_SUPPORTED_ARCH)))
$(warning unsupported TARGET_ARCH=$(TARGET_ARCH))
endif
@@ -81,4 +96,4 @@
2ND_ART_HOST_OUT_SHARED_LIBRARIES := $(2ND_HOST_OUT_SHARED_LIBRARIES)
endif
-endif # ANDROID_COMMON_MK
+endif # ART_ANDROID_COMMON_MK
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index 5dd9f15..3000cdf 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -14,10 +14,11 @@
# limitations under the License.
#
-ifndef ANDROID_COMMON_BUILD_MK
-ANDROID_COMMON_BUILD_MK = true
+ifndef ART_ANDROID_COMMON_BUILD_MK
+ART_ANDROID_COMMON_BUILD_MK = true
include art/build/Android.common.mk
+include art/build/Android.common_utils.mk
# These can be overridden via the environment or by editing to
# enable/disable certain build configuration.
@@ -59,42 +60,11 @@
endif
#
-# Used to enable SEA mode
-#
-ART_SEA_IR_MODE := false
-ifneq ($(wildcard art/SEA_IR_ART),)
-$(info Enabling ART_SEA_IR_MODE because of existence of art/SEA_IR_ART)
-ART_SEA_IR_MODE := true
-endif
-ifeq ($(WITH_ART_SEA_IR_MODE), true)
-ART_SEA_IR_MODE := true
-endif
-
-#
-# Used to enable portable mode
-#
-ART_USE_PORTABLE_COMPILER := false
-ifneq ($(wildcard art/USE_PORTABLE_COMPILER),)
-$(info Enabling ART_USE_PORTABLE_COMPILER because of existence of art/USE_PORTABLE_COMPILER)
-ART_USE_PORTABLE_COMPILER := true
-endif
-ifeq ($(WITH_ART_USE_PORTABLE_COMPILER),true)
-$(info Enabling ART_USE_PORTABLE_COMPILER because WITH_ART_USE_PORTABLE_COMPILER=true)
-ART_USE_PORTABLE_COMPILER := true
-endif
-
-#
# Used to change the default GC. Valid values are CMS, SS, GSS. The default is CMS.
#
art_default_gc_type ?= CMS
art_default_gc_type_cflags := -DART_DEFAULT_GC_TYPE_IS_$(art_default_gc_type)
-ifeq ($(ART_USE_PORTABLE_COMPILER),true)
- LLVM_ROOT_PATH := external/llvm
- # Don't fail a dalvik minimal host build.
- -include $(LLVM_ROOT_PATH)/llvm.mk
-endif
-
ART_HOST_CFLAGS :=
ART_TARGET_CFLAGS :=
@@ -113,9 +83,19 @@
else
ART_TARGET_CLANG := false
endif
+
+ifeq ($(TARGET_ARCH)|$(ART_TARGET_CLANG),mips|true)
+ # b/18807290, Clang generated mips assembly code for array.cc
+ # cannot be compiled by gas.
+ # b/18789639, Clang assembler cannot compile inlined assembly code in
+ # valgrind_malloc_space-inl.h:192:5: error: used $at without ".set noat"
+ $(warning Clang is disabled for the mips target)
+endif
ART_TARGET_CLANG_arm :=
ART_TARGET_CLANG_arm64 :=
-ART_TARGET_CLANG_mips :=
+# TODO: Enable clang mips when b/18807290 and b/18789639 are fixed.
+ART_TARGET_CLANG_mips := false
+ART_TARGET_CLANG_mips64 := false
ART_TARGET_CLANG_x86 :=
ART_TARGET_CLANG_x86_64 :=
@@ -131,14 +111,13 @@
ART_TARGET_CLANG_CFLAGS_arm :=
ART_TARGET_CLANG_CFLAGS_arm64 :=
ART_TARGET_CLANG_CFLAGS_mips :=
+ART_TARGET_CLANG_CFLAGS_mips64 :=
ART_TARGET_CLANG_CFLAGS_x86 :=
ART_TARGET_CLANG_CFLAGS_x86_64 :=
# These are necessary for Clang ARM64 ART builds. TODO: remove.
ART_TARGET_CLANG_CFLAGS_arm64 += \
- -Wno-implicit-exception-spec-mismatch \
- -DNVALGRIND \
- -Wno-unused-value
+ -DNVALGRIND
# FIXME: upstream LLVM has a vectorizer bug that needs to be fixed
ART_TARGET_CLANG_CFLAGS_arm64 += \
@@ -168,7 +147,7 @@
# Suggest final: Have to move to a more recent GCC.
# -Wsuggest-final-types
-
+ART_TARGET_CLANG_CFLAGS := $(art_clang_cflags)
ifeq ($(ART_HOST_CLANG),true)
# Bug: 15446488. We don't omit the frame pointer to work around
# clang/libunwind bugs that cause SEGVs in run-test-004-ThreadStress.
@@ -176,10 +155,14 @@
else
ART_HOST_CFLAGS += $(art_gcc_cflags)
endif
-ifeq ($(ART_TARGET_CLANG),true)
- ART_TARGET_CFLAGS += $(art_clang_cflags)
-else
+ifneq ($(ART_TARGET_CLANG),true)
ART_TARGET_CFLAGS += $(art_gcc_cflags)
+else
+ # TODO: if we ever want to support GCC/Clang mix for multi-target products, this needs to be
+ # split up.
+ ifeq ($(ART_TARGET_CLANG_$(TARGET_ARCH)),false)
+ ART_TARGET_CFLAGS += $(art_gcc_cflags)
+ endif
endif
# Clear local variables now their use has ended.
@@ -194,7 +177,6 @@
external/valgrind/main \
external/vixl/src \
external/zlib \
- frameworks/compile/mclinker/include
# Base set of cflags used by all things ART.
art_cflags := \
@@ -207,7 +189,6 @@
-Wstrict-aliasing \
-fstrict-aliasing \
-Wunreachable-code \
- -Wno-conversion-null \
-Wredundant-decls \
-Wshadow \
-fvisibility=protected \
@@ -229,14 +210,18 @@
art_cflags += -DART_SMALL_MODE=1
endif
-ifeq ($(ART_SEA_IR_MODE),true)
- art_cflags += -DART_SEA_IR_MODE=1
-endif
-
ifeq ($(ART_USE_OPTIMIZING_COMPILER),true)
art_cflags += -DART_USE_OPTIMIZING_COMPILER=1
endif
+ifeq ($(ART_HEAP_POISONING),true)
+ art_cflags += -DART_HEAP_POISONING=1
+endif
+
+ifeq ($(ART_USE_READ_BARRIER),true)
+ art_cflags += -DART_USE_READ_BARRIER=1
+endif
+
# Cflags for non-debug ART and ART tools.
art_non_debug_cflags := \
-O3
@@ -253,10 +238,14 @@
ifeq ($(HOST_OS),linux)
# Larger frame-size for host clang builds today
- ifndef SANITIZE_HOST
- art_host_non_debug_cflags += -Wframe-larger-than=2700
+ ifneq ($(ART_COVERAGE),true)
+ ifneq ($(NATIVE_COVERAGE),true)
+ ifndef SANITIZE_HOST
+ art_host_non_debug_cflags += -Wframe-larger-than=2700
+ endif
+ art_target_non_debug_cflags += -Wframe-larger-than=1728
+ endif
endif
- art_target_non_debug_cflags += -Wframe-larger-than=1728
endif
ifndef LIBART_IMG_HOST_BASE_ADDRESS
@@ -325,11 +314,9 @@
LOCAL_CFLAGS += $(ART_TARGET_NON_DEBUG_CFLAGS)
endif
- # TODO: Also set when ART_TARGET_CLANG_$(arch)!=false and ART_TARGET_CLANG==true
+ LOCAL_CLANG_CFLAGS := $(ART_TARGET_CLANG_CFLAGS)
$(foreach arch,$(ART_SUPPORTED_ARCH),
- ifeq ($$(ART_TARGET_CLANG_$(arch)),true)
- LOCAL_CFLAGS_$(arch) += $$(ART_TARGET_CLANG_CFLAGS_$(arch))
- endif)
+ LOCAL_CLANG_CFLAGS_$(arch) += $$(ART_TARGET_CLANG_CFLAGS_$(arch)))
# Clear locally used variables.
art_target_cflags_ndebug_or_debug :=
@@ -357,4 +344,4 @@
ART_BUILD_DEBUG := true
endif
-endif # ANDROID_COMMON_BUILD_MK
+endif # ART_ANDROID_COMMON_BUILD_MK
diff --git a/build/Android.common_path.mk b/build/Android.common_path.mk
index 281d189..e0c0b0c 100644
--- a/build/Android.common_path.mk
+++ b/build/Android.common_path.mk
@@ -14,8 +14,8 @@
# limitations under the License.
#
-ifndef ANDROID_COMMON_PATH_MK
-ANDROID_COMMON_PATH_MK := true
+ifndef ART_ANDROID_COMMON_PATH_MK
+ART_ANDROID_COMMON_PATH_MK := true
include art/build/Android.common.mk
@@ -88,4 +88,4 @@
HOST_CORE_DEX_FILES := $(foreach jar,$(HOST_CORE_JARS), $(call intermediates-dir-for,JAVA_LIBRARIES,$(jar),t,COMMON)/javalib.jar)
TARGET_CORE_DEX_FILES := $(foreach jar,$(TARGET_CORE_JARS),$(call intermediates-dir-for,JAVA_LIBRARIES,$(jar), ,COMMON)/javalib.jar)
-endif # ANDROID_COMMON_PATH_MK
+endif # ART_ANDROID_COMMON_PATH_MK
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index 2493565..da50d53 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -14,8 +14,8 @@
# limitations under the License.
#
-ifndef ANDROID_COMMON_TEST_MK
-ANDROID_COMMON_TEST_MK = true
+ifndef ART_ANDROID_COMMON_TEST_MK
+ART_ANDROID_COMMON_TEST_MK = true
include art/build/Android.common_path.mk
@@ -25,21 +25,7 @@
# List of known broken tests that we won't attempt to execute. The test name must be the full
# rule name such as test-art-host-oat-optimizing-HelloWorld64.
-ART_TEST_KNOWN_BROKEN := \
- test-art-target-run-test-gcstress-optimizing-prebuild-004-SignalTest32 \
- test-art-target-run-test-gcstress-optimizing-norelocate-004-SignalTest32 \
- test-art-target-run-test-gcstress-default-prebuild-004-SignalTest32 \
- test-art-target-run-test-gcstress-default-norelocate-004-SignalTest32 \
- test-art-target-run-test-gcstress-optimizing-relocate-004-SignalTest32 \
- test-art-target-run-test-gcstress-default-relocate-004-SignalTest32 \
- test-art-target-run-test-gcstress-optimizing-no-prebuild-004-SignalTest32 \
- test-art-target-run-test-gcstress-default-no-prebuild-004-SignalTest32 \
- test-art-host-run-test-gcstress-default-prebuild-114-ParallelGC32 \
- test-art-host-run-test-gcstress-interpreter-prebuild-114-ParallelGC32 \
- test-art-host-run-test-gcstress-optimizing-prebuild-114-ParallelGC32 \
- test-art-host-run-test-gcstress-default-prebuild-114-ParallelGC64 \
- test-art-host-run-test-gcstress-interpreter-prebuild-114-ParallelGC64 \
- test-art-host-run-test-gcstress-optimizing-prebuild-114-ParallelGC64
+ART_TEST_KNOWN_BROKEN :=
# Failing valgrind tests.
# Note: *all* 64b tests involving the runtime do not work currently. b/15170219.
@@ -197,4 +183,4 @@
endif
endef
-endif # ANDROID_COMMON_TEST_MK
+endif # ART_ANDROID_COMMON_TEST_MK
diff --git a/build/Android.common_utils.mk b/build/Android.common_utils.mk
new file mode 100644
index 0000000..8069c3a
--- /dev/null
+++ b/build/Android.common_utils.mk
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+ifndef ART_ANDROID_COMMON_UTILS_MK
+ART_ANDROID_COMMON_UTILS_MK = true
+
+#
+# Convert a string into an uppercase string.
+#
+# $(1): a string which should be made uppercase
+art-string-to-uppercase = $(shell echo $(1) | tr '[:lower:]' '[:upper:]')
+
+endif # ART_ANDROID_COMMON_UTILS_MK
diff --git a/build/Android.executable.mk b/build/Android.executable.mk
index 86f445f..dfea6e1 100644
--- a/build/Android.executable.mk
+++ b/build/Android.executable.mk
@@ -20,9 +20,6 @@
ART_TARGET_EXECUTABLES ?=
ART_EXECUTABLES_CFLAGS :=
-ifeq ($(ART_USE_PORTABLE_COMPILER),true)
- ART_EXECUTABLES_CFLAGS += -DART_USE_PORTABLE_COMPILER=1
-endif
# $(1): executable ("d" will be appended for debug version)
# $(2): source
@@ -50,12 +47,13 @@
art_target_or_host := $(5)
art_ndebug_or_debug := $(6)
art_multilib := $(7)
+ art_out_binary_name :=
include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $$(art_source)
- LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime $$(art_c_includes)
+ LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime art/cmdline $$(art_c_includes)
LOCAL_SHARED_LIBRARIES += $$(art_shared_libraries)
LOCAL_WHOLE_STATIC_LIBRARIES += libsigchain
@@ -66,9 +64,9 @@
endif
LOCAL_CFLAGS := $(ART_EXECUTABLES_CFLAGS)
- # Mac OS linker doesn't understand --export-dynamic/--version-script.
+ # Mac OS linker doesn't understand --export-dynamic.
ifneq ($$(HOST_OS)-$$(art_target_or_host),darwin-host)
- LOCAL_LDFLAGS := -Wl,--version-script,art/sigchainlib/version-script.txt -Wl,--export-dynamic
+ LOCAL_LDFLAGS := -Wl,--export-dynamic
endif
ifeq ($$(art_target_or_host),target)
@@ -94,21 +92,115 @@
endif
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
+ LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_utils.mk
LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.executable.mk
ifeq ($$(art_target_or_host),target)
LOCAL_MODULE_TARGET_ARCH := $(ART_SUPPORTED_ARCH)
endif
- LOCAL_MULTILIB := $$(art_multilib)
- include external/libcxx/libcxx.mk
+ LOCAL_MULTILIB := $$(art_multilib)
+ art_out_binary_name := $$(LOCAL_MODULE)
+
+ # If multilib=both (potentially building both 32-bit and 64-bit), need to provide stem.
+ ifeq ($$(art_multilib),both)
+ # Set up a 32-bit/64-bit stem if we are building both binaries.
+ # In this case, the 32-bit binary has an additional 32-bit suffix.
+ LOCAL_MODULE_STEM_32 := $$(LOCAL_MODULE)32
+ LOCAL_MODULE_STEM_64 := $$(LOCAL_MODULE)
+
+ # Remember the binary names so we can add them to the global art executables list later.
+ art_out_binary_name := $$(LOCAL_MODULE_STEM_32) $$(LOCAL_MODULE_STEM_64)
+
+ # For single-architecture targets, remove any binary name suffixes.
+ ifeq ($$(art_target_or_host),target)
+ ifeq (,$(TARGET_2ND_ARCH))
+ LOCAL_MODULE_STEM_32 := $$(LOCAL_MODULE)
+ art_out_binary_name := $$(LOCAL_MODULE)
+ endif
+ endif
+
+ # For single-architecture hosts, remove any binary name suffixes.
+ ifeq ($$(art_target_or_host),host)
+ ifeq (,$(HOST_2ND_ARCH))
+ LOCAL_MODULE_STEM_32 := $$(LOCAL_MODULE)
+ art_out_binary_name := $$(LOCAL_MODULE)
+ endif
+ endif
+ endif
+
+ LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
+
ifeq ($$(art_target_or_host),target)
include $(BUILD_EXECUTABLE)
- ART_TARGET_EXECUTABLES := $(ART_TARGET_EXECUTABLES) $(TARGET_OUT_EXECUTABLES)/$$(LOCAL_MODULE)
+ ART_TARGET_EXECUTABLES := $(ART_TARGET_EXECUTABLES) $$(foreach name,$$(art_out_binary_name),$(TARGET_OUT_EXECUTABLES)/$$(name))
else # host
LOCAL_IS_HOST_MODULE := true
include $(BUILD_HOST_EXECUTABLE)
- ART_HOST_EXECUTABLES := $(ART_HOST_EXECUTABLES) $(HOST_OUT_EXECUTABLES)/$$(LOCAL_MODULE)
+ ART_HOST_EXECUTABLES := $(ART_HOST_EXECUTABLES) $$(foreach name,$$(art_out_binary_name),$(HOST_OUT_EXECUTABLES)/$$(name))
endif
+ # Clear out local variables now that we're done with them.
+ art_executable :=
+ art_source :=
+ art_shared_libraries :=
+ art_c_includes :=
+ art_target_or_host :=
+ art_ndebug_or_debug :=
+ art_multilib :=
+ art_out_binary_name :=
+
+endef
+
+#
+# Build many art executables from multiple variations (debug/ndebug, host/target, 32/64bit).
+# By default only either 32-bit or 64-bit is built (but not both -- see multilib arg).
+# All other variations are gated by ANDROID_BUILD_(TARGET|HOST)_[N]DEBUG.
+# The result must be eval-uated.
+#
+# $(1): executable name
+# $(2): source files
+# $(3): library dependencies (common); debug prefix is added on as necessary automatically.
+# $(4): library dependencies (target only)
+# $(5): library dependencies (host only)
+# $(6): extra include directories
+# $(7): multilib (default: empty), valid values: {,32,64,both})
+define build-art-multi-executable
+ $(foreach debug_flavor,ndebug debug,
+ $(foreach target_flavor,host target,
+ art-multi-binary-name := $(1)
+ art-multi-source-files := $(2)
+ art-multi-lib-dependencies := $(3)
+ art-multi-lib-dependencies-target := $(4)
+ art-multi-lib-dependencies-host := $(5)
+ art-multi-include-extra := $(6)
+ art-multi-multilib := $(7)
+
+ # Add either -host or -target specific lib dependencies to the lib dependencies.
+ art-multi-lib-dependencies += $$(art-multi-lib-dependencies-$(target_flavor))
+
+ # Replace libart- prefix with libartd- for debug flavor.
+ ifeq ($(debug_flavor),debug)
+ art-multi-lib-dependencies := $$(subst libart-,libartd-,$$(art-multi-lib-dependencies))
+ endif
+
+ # Build the env guard var name, e.g. ART_BUILD_HOST_NDEBUG.
+ art-multi-env-guard := $$(call art-string-to-uppercase,ART_BUILD_$(target_flavor)_$(debug_flavor))
+
+ # Build the art executable only if the corresponding env guard was set.
+ ifeq ($$($$(art-multi-env-guard)),true)
+ $$(eval $$(call build-art-executable,$$(art-multi-binary-name),$$(art-multi-source-files),$$(art-multi-lib-dependencies),$$(art-multi-include-extra),$(target_flavor),$(debug_flavor),$$(art-multi-multilib)))
+ endif
+
+ # Clear locals now they've served their purpose.
+ art-multi-binary-name :=
+ art-multi-source-files :=
+ art-multi-lib-dependencies :=
+ art-multi-lib-dependencies-target :=
+ art-multi-lib-dependencies-host :=
+ art-multi-include-extra :=
+ art-multi-multilib :=
+ art-multi-env-guard :=
+ )
+ )
endef
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 10b0400..06d258d 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -65,10 +65,24 @@
# TODO: document why this is needed.
ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
+# The imgdiag test has dependencies on core.oat since it needs to load it during the test.
+# For the host, also add the installed tool (in the base size, that should suffice). For the
+# target, just the module is fine, the sync will happen late enough.
+ART_GTEST_imgdiag_test_HOST_DEPS := \
+ $(HOST_CORE_IMAGE_default_no-pic_64) \
+ $(HOST_CORE_IMAGE_default_no-pic_32) \
+ $(HOST_OUT_EXECUTABLES)/imgdiagd
+ART_GTEST_imgdiag_test_TARGET_DEPS := \
+ $(TARGET_CORE_IMAGE_default_no-pic_64) \
+ $(TARGET_CORE_IMAGE_default_no-pic_32) \
+ imgdiagd
+
# The path for which all the source files are relative, not actually the current directory.
LOCAL_PATH := art
RUNTIME_GTEST_COMMON_SRC_FILES := \
+ cmdline/cmdline_parser_test.cc \
+ imgdiag/imgdiag_test.cc \
runtime/arch/arch_test.cc \
runtime/arch/instruction_set_test.cc \
runtime/arch/instruction_set_features_test.cc \
@@ -77,6 +91,7 @@
runtime/arch/arm/instruction_set_features_arm_test.cc \
runtime/arch/arm64/instruction_set_features_arm64_test.cc \
runtime/arch/mips/instruction_set_features_mips_test.cc \
+ runtime/arch/mips64/instruction_set_features_mips64_test.cc \
runtime/arch/x86/instruction_set_features_x86_test.cc \
runtime/arch/x86_64/instruction_set_features_x86_64_test.cc \
runtime/barrier_test.cc \
@@ -89,10 +104,8 @@
runtime/base/scoped_flock_test.cc \
runtime/base/stringprintf_test.cc \
runtime/base/timing_logger_test.cc \
+ runtime/base/variant_map_test.cc \
runtime/base/unix_file/fd_file_test.cc \
- runtime/base/unix_file/null_file_test.cc \
- runtime/base/unix_file/random_access_file_utils_test.cc \
- runtime/base/unix_file/string_file_test.cc \
runtime/class_linker_test.cc \
runtime/dex_file_test.cc \
runtime/dex_file_verifier_test.cc \
@@ -105,6 +118,7 @@
runtime/gc/accounting/card_table_test.cc \
runtime/gc/accounting/space_bitmap_test.cc \
runtime/gc/heap_test.cc \
+ runtime/gc/reference_queue_test.cc \
runtime/gc/space/dlmalloc_space_base_test.cc \
runtime/gc/space/dlmalloc_space_static_test.cc \
runtime/gc/space/dlmalloc_space_random_test.cc \
@@ -112,6 +126,7 @@
runtime/gc/space/rosalloc_space_static_test.cc \
runtime/gc/space/rosalloc_space_random_test.cc \
runtime/gc/space/large_object_space_test.cc \
+ runtime/gc/task_processor_test.cc \
runtime/gtest_test.cc \
runtime/handle_scope_test.cc \
runtime/indenter_test.cc \
@@ -147,6 +162,7 @@
compiler/image_test.cc \
compiler/jni/jni_compiler_test.cc \
compiler/oat_test.cc \
+ compiler/optimizing/bounds_check_elimination_test.cc \
compiler/optimizing/codegen_test.cc \
compiler/optimizing/dead_code_elimination_test.cc \
compiler/optimizing/constant_folding_test.cc \
@@ -169,18 +185,11 @@
compiler/output_stream_test.cc \
compiler/utils/arena_allocator_test.cc \
compiler/utils/dedupe_set_test.cc \
+ compiler/utils/swap_space_test.cc \
compiler/utils/arm/managed_register_arm_test.cc \
compiler/utils/arm64/managed_register_arm64_test.cc \
compiler/utils/x86/managed_register_x86_test.cc \
-ifeq ($(ART_SEA_IR_MODE),true)
-COMPILER_GTEST_COMMON_SRC_FILES += \
- compiler/utils/scoped_hashtable_test.cc \
- compiler/sea_ir/types/type_data_test.cc \
- compiler/sea_ir/types/type_inference_visitor_test.cc \
- compiler/sea_ir/ir/regions_test.cc
-endif
-
RUNTIME_GTEST_TARGET_SRC_FILES := \
$(RUNTIME_GTEST_COMMON_SRC_FILES)
@@ -199,23 +208,19 @@
compiler/utils/x86_64/assembler_x86_64_test.cc
ART_TEST_CFLAGS :=
-ifeq ($(ART_USE_PORTABLE_COMPILER),true)
- ART_TEST_CFLAGS += -DART_USE_PORTABLE_COMPILER=1
-endif
include $(CLEAR_VARS)
LOCAL_MODULE := libart-gtest
LOCAL_MODULE_TAGS := optional
LOCAL_CPP_EXTENSION := cc
-LOCAL_CFLAGS := $(ART_TARGET_CFLAGS)
LOCAL_SRC_FILES := runtime/common_runtime_test.cc compiler/common_compiler_test.cc
LOCAL_C_INCLUDES := $(ART_C_INCLUDES) art/runtime art/compiler
LOCAL_SHARED_LIBRARIES := libartd libartd-compiler libdl
LOCAL_STATIC_LIBRARIES += libgtest
-LOCAL_CLANG := $(ART_TARGET_CLANG)
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.gtest.mk
-include external/libcxx/libcxx.mk
+$(eval $(call set-target-local-clang-vars))
+$(eval $(call set-target-local-cflags-vars,debug))
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
@@ -232,7 +237,6 @@
LOCAL_CLANG := $(ART_HOST_CLANG)
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.gtest.mk
-include external/libcxx/libcxx.mk
include $(BUILD_HOST_SHARED_LIBRARY)
# Variables holding collections of gtest pre-requisits used to run a number of gtests.
@@ -273,7 +277,7 @@
$(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID
$(hide) adb shell chmod 755 $(ART_TARGET_NATIVETEST_DIR)/$(TARGET_$(2)ARCH)/$(1)
$(hide) $$(call ART_TEST_SKIP,$$@) && \
- (adb shell "LD_LIBRARY_PATH=$(3) ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
+ (adb shell "$(GCOV_ENV) LD_LIBRARY_PATH=$(3) ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
$(ART_TARGET_NATIVETEST_DIR)/$(TARGET_$(2)ARCH)/$(1) && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID" \
&& (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID /tmp/ \
&& $$(call ART_TEST_PASSED,$$@)) \
@@ -364,7 +368,6 @@
endif
LOCAL_CFLAGS := $$(ART_TEST_CFLAGS)
- include external/libcxx/libcxx.mk
ifeq ($$(art_target_or_host),target)
$$(eval $$(call set-target-local-clang-vars))
$$(eval $$(call set-target-local-cflags-vars,debug))
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 523d143..8d49565 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -34,6 +34,8 @@
# $(1): compiler - default, optimizing or interpreter.
# $(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.
# 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
@@ -44,13 +46,17 @@
core_pic_infix :=
core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY)
+ # With the optimizing compiler, we want to rerun dex2oat whenever there is
+ # a dex2oat change to catch regressions early.
+ ifeq ($(ART_USE_OPTIMIZING_COMPILER), true)
+ core_dex2oat_dependency := $(DEX2OAT)
+ endif
+
ifeq ($(1),default)
core_compile_options += --compiler-backend=Quick
endif
ifeq ($(1),optimizing)
core_compile_options += --compiler-backend=Optimizing
- # With the optimizing compiler, we want to rerun dex2oat whenever there is
- # a dex2oat change to catch regressions early.
core_dex2oat_dependency := $(DEX2OAT)
core_infix := -optimizing
endif
@@ -78,25 +84,30 @@
$$(error found $(2) expected pic or no-pic)
endif
- core_image_name := $($(3)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(CORE_IMG_SUFFIX)
- core_oat_name := $($(3)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(CORE_OAT_SUFFIX)
+ core_image_name := $($(3)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(4)$(CORE_IMG_SUFFIX)
+ core_oat_name := $($(3)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(4)$(CORE_OAT_SUFFIX)
# Using the bitness suffix makes it easier to add as a dependency for the run-test mk.
ifeq ($(3),)
- HOST_CORE_IMAGE_$(1)_$(2)_64 := $$(core_image_name)
+ $(4)HOST_CORE_IMAGE_$(1)_$(2)_64 := $$(core_image_name)
else
- HOST_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
+ $(4)HOST_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
endif
- HOST_CORE_IMG_OUTS += $$(core_image_name)
- HOST_CORE_OAT_OUTS += $$(core_oat_name)
+ $(4)HOST_CORE_IMG_OUTS += $$(core_image_name)
+ $(4)HOST_CORE_OAT_OUTS += $$(core_oat_name)
+ # If we have a wrapper, make the target phony.
+ ifneq ($(4),)
+.PHONY: $$(core_image_name)
+ endif
$$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
$$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
$$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
$$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency)
@echo "host dex2oat: $$@ ($$?)"
@mkdir -p $$(dir $$@)
- $$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
+ $$(hide) $(4) $$(DEX2OAT)$(5) --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) \
@@ -117,20 +128,29 @@
endef # create-core-oat-host-rules
# $(1): compiler - default, optimizing or interpreter.
+# $(2): wrapper.
+# $(3): dex2oat suffix.
define create-core-oat-host-rule-combination
- $(call create-core-oat-host-rules,$(1),no-pic,)
- $(call create-core-oat-host-rules,$(1),pic,)
+ $(call create-core-oat-host-rules,$(1),no-pic,,$(2),$(3))
+ $(call create-core-oat-host-rules,$(1),pic,,$(2),$(3))
ifneq ($(HOST_PREFER_32_BIT),true)
- $(call create-core-oat-host-rules,$(1),no-pic,2ND_)
- $(call create-core-oat-host-rules,$(1),pic,2ND_)
+ $(call create-core-oat-host-rules,$(1),no-pic,2ND_,$(2),$(3))
+ $(call create-core-oat-host-rules,$(1),pic,2ND_,$(2),$(3))
endif
endef
-$(eval $(call create-core-oat-host-rule-combination,default))
-$(eval $(call create-core-oat-host-rule-combination,optimizing))
-$(eval $(call create-core-oat-host-rule-combination,interpreter))
+$(eval $(call create-core-oat-host-rule-combination,default,,))
+$(eval $(call create-core-oat-host-rule-combination,optimizing,,))
+$(eval $(call create-core-oat-host-rule-combination,interpreter,,))
+valgrindHOST_CORE_IMG_OUTS :=
+valgrindHOST_CORE_OAT_OUTS :=
+$(eval $(call create-core-oat-host-rule-combination,default,valgrind,32))
+$(eval $(call create-core-oat-host-rule-combination,optimizing,valgrind,32))
+$(eval $(call create-core-oat-host-rule-combination,interpreter,valgrind,32))
+
+valgrind-test-art-host-dex2oat-host: $(valgrindHOST_CORE_IMG_OUTS)
define create-core-oat-target-rules
core_compile_options :=
@@ -140,20 +160,18 @@
core_pic_infix :=
core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY)
+ # With the optimizing compiler, we want to rerun dex2oat whenever there is
+ # a dex2oat change to catch regressions early.
+ ifeq ($(ART_USE_OPTIMIZING_COMPILER), true)
+ core_dex2oat_dependency := $(DEX2OAT)
+ endif
+
ifeq ($(1),default)
core_compile_options += --compiler-backend=Quick
endif
ifeq ($(1),optimizing)
- ifeq ($($(3)TARGET_ARCH),arm64)
- # TODO: Enable image generation on arm64 once the backend
- # is on par with other architectures.
- core_compile_options += --compiler-backend=Quick
- else
- core_compile_options += --compiler-backend=Optimizing
- # With the optimizing compiler, we want to rerun dex2oat whenever there is
- # a dex2oat change to catch regressions early.
- core_dex2oat_dependency := $(DEX2OAT)
- endif
+ core_compile_options += --compiler-backend=Optimizing
+ core_dex2oat_dependency := $(DEX2OAT)
core_infix := -optimizing
endif
ifeq ($(1),interpreter)
@@ -180,29 +198,34 @@
$$(error found $(2) expected pic or no-pic)
endif
- core_image_name := $($(3)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(CORE_IMG_SUFFIX)
- core_oat_name := $($(3)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(CORE_OAT_SUFFIX)
+ 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)
# Using the bitness suffix makes it easier to add as a dependency for the run-test mk.
ifeq ($(3),)
ifdef TARGET_2ND_ARCH
- TARGET_CORE_IMAGE_$(1)_$(2)_64 := $$(core_image_name)
+ $(4)TARGET_CORE_IMAGE_$(1)_$(2)_64 := $$(core_image_name)
else
- TARGET_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
+ $(4)TARGET_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
endif
else
- TARGET_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
+ $(4)TARGET_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
endif
- TARGET_CORE_IMG_OUTS += $$(core_image_name)
- TARGET_CORE_OAT_OUTS += $$(core_oat_name)
+ $(4)TARGET_CORE_IMG_OUTS += $$(core_image_name)
+ $(4)TARGET_CORE_OAT_OUTS += $$(core_oat_name)
+ # If we have a wrapper, make the target phony.
+ ifneq ($(4),)
+.PHONY: $$(core_image_name)
+ endif
$$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
$$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
$$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
$$(core_image_name): $$(TARGET_CORE_DEX_FILES) $$(core_dex2oat_dependency)
@echo "target dex2oat: $$@ ($$?)"
@mkdir -p $$(dir $$@)
- $$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_XMS) --runtime-arg -Xmx$(DEX2OAT_XMX) \
+ $$(hide) $(4) $$(DEX2OAT)$(5) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+ --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
--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) \
@@ -223,16 +246,28 @@
endef # create-core-oat-target-rules
# $(1): compiler - default, optimizing or interpreter.
+# $(2): wrapper.
+# $(3): dex2oat suffix.
define create-core-oat-target-rule-combination
- $(call create-core-oat-target-rules,$(1),no-pic,)
- $(call create-core-oat-target-rules,$(1),pic,)
+ $(call create-core-oat-target-rules,$(1),no-pic,,$(2),$(3))
+ $(call create-core-oat-target-rules,$(1),pic,,$(2),$(3))
ifdef TARGET_2ND_ARCH
- $(call create-core-oat-target-rules,$(1),no-pic,2ND_)
- $(call create-core-oat-target-rules,$(1),pic,2ND_)
+ $(call create-core-oat-target-rules,$(1),no-pic,2ND_,$(2),$(3))
+ $(call create-core-oat-target-rules,$(1),pic,2ND_,$(2),$(3))
endif
endef
-$(eval $(call create-core-oat-target-rule-combination,default))
-$(eval $(call create-core-oat-target-rule-combination,optimizing))
-$(eval $(call create-core-oat-target-rule-combination,interpreter))
+$(eval $(call create-core-oat-target-rule-combination,default,,))
+$(eval $(call create-core-oat-target-rule-combination,optimizing,,))
+$(eval $(call create-core-oat-target-rule-combination,interpreter,,))
+
+valgrindTARGET_CORE_IMG_OUTS :=
+valgrindTARGET_CORE_OAT_OUTS :=
+$(eval $(call create-core-oat-target-rule-combination,default,valgrind,32))
+$(eval $(call create-core-oat-target-rule-combination,optimizing,valgrind,32))
+$(eval $(call create-core-oat-target-rule-combination,interpreter,valgrind,32))
+
+valgrind-test-art-host-dex2oat-target: $(valgrindTARGET_CORE_IMG_OUTS)
+
+valgrind-test-art-host-dex2oat: valgrind-test-art-host-dex2oat-host valgrind-test-art-host-dex2oat-target
diff --git a/cmdline/README.md b/cmdline/README.md
new file mode 100644
index 0000000..8cac77f
--- /dev/null
+++ b/cmdline/README.md
@@ -0,0 +1,245 @@
+Cmdline
+===================
+
+Introduction
+-------------
+This directory contains the classes that do common command line tool initialization and parsing. The
+long term goal is eventually for all `art` command-line tools to be using these helpers.
+
+----------
+
+
+## Cmdline Parser
+-------------
+
+The `CmdlineParser` class provides a fluent interface using a domain-specific language to quickly
+generate a type-safe value parser that process a user-provided list of strings (`argv`). Currently,
+it can parse a string into a `VariantMap`, although in the future it might be desirable to parse
+into any struct of any field.
+
+To use, create a `CmdlineParser::Builder` and then chain the `Define` methods together with
+`WithType` and `IntoXX` methods.
+
+### Quick Start
+For example, to save the values into a user-defined variant map:
+
+```
+struct FruitVariantMap : VariantMap {
+ static const Key<int> Apple;
+ static const Key<double> Orange;
+ static const Key<bool> Help;
+};
+// Note that some template boilerplate has been avoided for clarity.
+// See variant_map_test.cc for how to completely define a custom map.
+
+using FruitParser = CmdlineParser<FruitVariantMap, FruitVariantMap::Key>;
+
+FruitParser MakeParser() {
+ auto&& builder = FruitParser::Builder();
+ builder.
+ .Define("--help")
+ .IntoKey(FruitVariantMap::Help)
+ Define("--apple:_")
+ .WithType<int>()
+ .IntoKey(FruitVariantMap::Apple)
+ .Define("--orange:_")
+ .WithType<double>()
+ .WithRange(0.0, 1.0)
+ .IntoKey(FruitVariantMap::Orange);
+
+ return builder.Build();
+}
+
+int main(char** argv, int argc) {
+ auto parser = MakeParser();
+ auto result = parser.parse(argv, argc));
+ if (result.isError()) {
+ std::cerr << result.getMessage() << std::endl;
+ return EXIT_FAILURE;
+ }
+ auto map = parser.GetArgumentsMap();
+ std::cout << "Help? " << map.GetOrDefault(FruitVariantMap::Help) << std::endl;
+ std::cout << "Apple? " << map.GetOrDefault(FruitVariantMap::Apple) << std::endl;
+ std::cout << "Orange? " << map.GetOrDefault(FruitVariantMap::Orange) << std::endl;
+
+ return EXIT_SUCCESS;
+}
+```
+
+In the above code sample, we define a parser which is capable of parsing something like `--help
+--apple:123 --orange:0.456` . It will error out automatically if invalid flags are given, or if the
+appropriate flags are given but of the the wrong type/range. So for example, `--foo` will not parse
+(invalid argument), neither will `--apple:fruit` (fruit is not an int) nor `--orange:1234` (1234 is
+out of range of [0.0, 1.0])
+
+### Argument Definitions in Detail
+#### Define method
+The 'Define' method takes one or more aliases for the argument. Common examples might be `{"-h",
+"--help"}` where both `--help` and `-h` are aliases for the same argument.
+
+The simplest kind of argument just tests for presence, but we often want to parse out a particular
+type of value (such as an int or double as in the above `FruitVariantMap` example). To do that, a
+_wildcard_ must be used to denote the location within the token that the type will be parsed out of.
+
+For example with `-orange:_` the parse would know to check all tokens in an `argv` list for the
+`-orange:` prefix and then strip it, leaving only the remains to be parsed.
+
+#### WithType method (optional)
+After an argument definition is provided, the parser builder needs to know what type the argument
+will be in order to provide the type safety and make sure the rest of the argument definition is
+correct as early as possible (in essence, everything but the parsing of the argument name is done at
+compile time).
+
+Everything that follows a `WithType<T>()` call is thus type checked to only take `T` values.
+
+If this call is omitted, the parser generator assumes you are building a `Unit` type (i.e. an
+argument that only cares about presence).
+
+#### WithRange method (optional)
+Some values will not make sense outside of a `[min, max]` range, so this is an option to quickly add
+a range check without writing custom code. The range check is performed after the main parsing
+happens and happens for any type implementing the `<=` operators.
+
+#### WithValueMap (optional)
+When parsing an enumeration, it might be very convenient to map a list of possible argument string
+values into its runtime value.
+
+With something like
+```
+ .Define("-hello:_")
+ .WithValueMap({"world", kWorld},
+ {"galaxy", kGalaxy})
+```
+It will parse either `-hello:world` or `-hello:galaxy` only (and error out on other variations of
+`-hello:whatever`), converting it to the type-safe value of `kWorld` or `kGalaxy` respectively.
+
+This is meant to be another shorthand (like `WithRange`) to avoid writing a custom type parser. In
+general it takes a variadic number of `pair<const char* /*arg name*/, T /*value*/>`.
+
+#### WithValues (optional)
+When an argument definition has multiple aliases with no wildcards, it might be convenient to
+quickly map them into discrete values.
+
+For example:
+```
+ .Define({"-xinterpret", "-xnointerpret"})
+ .WithValues({true, false}
+```
+It will parse `-xinterpret` as `true` and `-xnointerpret` as `false`.
+
+In general, it uses the position of the argument alias to map into the WithValues position value.
+
+(Note that this method will not work when the argument definitions have a wildcard because there is
+no way to position-ally match that).
+
+#### AppendValues (optional)
+By default, the argument is assumed to appear exactly once, and if the user specifies it more than
+once, only the latest value is taken into account (and all previous occurrences of the argument are
+ignored).
+
+In some situations, we may want to accumulate the argument values instead of discarding the previous
+ones.
+
+For example
+```
+ .Define("-D")
+ .WithType<std::vector<std::string>)()
+ .AppendValues()
+```
+Will parse something like `-Dhello -Dworld -Dbar -Dbaz` into `std::vector<std::string>{"hello",
+"world", "bar", "baz"}`.
+
+### Setting an argument parse target (required)
+To complete an argument definition, the parser generator also needs to know where to save values.
+Currently, only `IntoKey` is supported, but that may change in the future.
+
+#### IntoKey (required)
+This specifies that when a value is parsed, it will get saved into a variant map using the specific
+key.
+
+For example,
+```
+ .Define("-help")
+ .IntoKey(Map::Help)
+```
+will save occurrences of the `-help` argument by doing a `Map.Set(Map::Help, ParsedValue("-help"))`
+where `ParsedValue` is an imaginary function that parses the `-help` argment into a specific type
+set by `WithType`.
+
+### Ignoring unknown arguments
+This is highly discouraged, but for compatibility with `JNI` which allows argument ignores, there is
+an option to ignore any argument tokens that are not known to the parser. This is done with the
+`Ignore` function which takes a list of argument definition names.
+
+It's semantically equivalent to making a series of argument definitions that map to `Unit` but don't
+get saved anywhere. Values will still get parsed as normal, so it will *not* ignore known arguments
+with invalid values, only user-arguments for which it could not find a matching argument definition.
+
+### Parsing custom types
+Any type can be parsed from a string by specializing the `CmdlineType` class and implementing the
+static interface provided by `CmdlineTypeParser`. It is recommended to inherit from
+`CmdlineTypeParser` since it already provides default implementations for every method.
+
+The `Parse` method should be implemented for most types. Some types will allow appending (such as an
+`std::vector<std::string>` and are meant to be used with `AppendValues` in which case the
+`ParseAndAppend` function should be implemented.
+
+For example:
+```
+template <>
+struct CmdlineType<double> : CmdlineTypeParser<double> {
+ Result Parse(const std::string& str) {
+ char* end = nullptr;
+ errno = 0;
+ double value = strtod(str.c_str(), &end);
+
+ if (*end != '\0') {
+ return Result::Failure("Failed to parse double from " + str);
+ }
+ if (errno == ERANGE) {
+ return Result::OutOfRange(
+ "Failed to parse double from " + str + "; overflow/underflow occurred");
+ }
+
+ return Result::Success(value);
+ }
+
+ static const char* Name() { return "double"; }
+ // note: Name() is just here for more user-friendly errors,
+ // but in the future we will use non-standard ways of getting the type name
+ // at compile-time and this will no longer be required
+};
+```
+Will parse any non-append argument definitions with a type of `double`.
+
+For an appending example:
+```
+template <>
+struct CmdlineType<std::vector<std::string>> : CmdlineTypeParser<std::vector<std::string>> {
+ Result ParseAndAppend(const std::string& args,
+ std::vector<std::string>& existing_value) {
+ existing_value.push_back(args);
+ return Result::SuccessNoValue();
+ }
+ static const char* Name() { return "std::vector<std::string>"; }
+};
+```
+Will parse multiple instances of the same argument repeatedly into the `existing_value` (which will
+be default-constructed to `T{}` for the first occurrence of the argument).
+
+#### What is a `Result`?
+`Result` is a typedef for `CmdlineParseResult<T>` and it acts similar to a poor version of
+`Either<Left, Right>` in Haskell. In particular, it would be similar to `Either< int ErrorCode,
+Maybe<T> >`.
+
+There are helpers like `Result::Success(value)`, `Result::Failure(string message)` and so on to
+quickly construct these without caring about the type.
+
+When successfully parsing a single value, `Result::Success(value)` should be used, and when
+successfully parsing an appended value, use `Result::SuccessNoValue()` and write back the new value
+into `existing_value` as an out-parameter.
+
+When many arguments are parsed, the result is collapsed down to a `CmdlineResult` which acts as a
+`Either<int ErrorCode, Unit>` where the right side simply indicates success. When values are
+successfully stored, the parser will automatically save it into the target destination as a side
+effect.
diff --git a/cmdline/cmdline.h b/cmdline/cmdline.h
new file mode 100644
index 0000000..2967e27
--- /dev/null
+++ b/cmdline/cmdline.h
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_CMDLINE_CMDLINE_H_
+#define ART_CMDLINE_CMDLINE_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include "runtime.h"
+#include "base/stringpiece.h"
+#include "noop_compiler_callbacks.h"
+#include "base/logging.h"
+
+#if !defined(NDEBUG)
+#define DBG_LOG LOG(INFO)
+#else
+#define DBG_LOG LOG(DEBUG)
+#endif
+
+namespace art {
+
+// TODO: Move to <runtime/utils.h> and remove all copies of this function.
+static bool LocationToFilename(const std::string& location, InstructionSet isa,
+ std::string* filename) {
+ bool has_system = false;
+ bool has_cache = false;
+ // image_location = /system/framework/boot.art
+ // system_image_filename = /system/framework/<image_isa>/boot.art
+ std::string system_filename(GetSystemImageFilename(location.c_str(), isa));
+ if (OS::FileExists(system_filename.c_str())) {
+ has_system = true;
+ }
+
+ bool have_android_data = false;
+ bool dalvik_cache_exists = false;
+ bool is_global_cache = false;
+ std::string dalvik_cache;
+ GetDalvikCache(GetInstructionSetString(isa), false, &dalvik_cache,
+ &have_android_data, &dalvik_cache_exists, &is_global_cache);
+
+ std::string cache_filename;
+ if (have_android_data && dalvik_cache_exists) {
+ // Always set output location even if it does not exist,
+ // so that the caller knows where to create the image.
+ //
+ // image_location = /system/framework/boot.art
+ // *image_filename = /data/dalvik-cache/<image_isa>/boot.art
+ std::string error_msg;
+ if (GetDalvikCacheFilename(location.c_str(), dalvik_cache.c_str(),
+ &cache_filename, &error_msg)) {
+ has_cache = true;
+ }
+ }
+ if (has_system) {
+ *filename = system_filename;
+ return true;
+ } else if (has_cache) {
+ *filename = cache_filename;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static Runtime* StartRuntime(const char* boot_image_location,
+ InstructionSet instruction_set) {
+ CHECK(boot_image_location != nullptr);
+
+ RuntimeOptions options;
+
+ // We are more like a compiler than a run-time. We don't want to execute code.
+ {
+ static NoopCompilerCallbacks callbacks;
+ options.push_back(std::make_pair("compilercallbacks", &callbacks));
+ }
+
+ // Boot image location.
+ {
+ std::string boot_image_option;
+ boot_image_option += "-Ximage:";
+ boot_image_option += boot_image_location;
+ options.push_back(std::make_pair(boot_image_option.c_str(), nullptr));
+ }
+
+ // Instruction set.
+ options.push_back(
+ std::make_pair("imageinstructionset",
+ reinterpret_cast<const void*>(GetInstructionSetString(instruction_set))));
+
+ if (!Runtime::Create(options, false)) {
+ fprintf(stderr, "Failed to create runtime\n");
+ return nullptr;
+ }
+
+ // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
+ // give it away now and then switch to a more manageable ScopedObjectAccess.
+ Thread::Current()->TransitionFromRunnableToSuspended(kNative);
+
+ return Runtime::Current();
+}
+
+struct CmdlineArgs {
+ enum ParseStatus {
+ kParseOk, // Parse successful. Do not set the error message.
+ kParseUnknownArgument, // Unknown argument. Do not set the error message.
+ kParseError, // Parse ok, but failed elsewhere. Print the set error message.
+ };
+
+ bool Parse(int argc, char** argv) {
+ // Skip over argv[0].
+ argv++;
+ argc--;
+
+ if (argc == 0) {
+ fprintf(stderr, "No arguments specified\n");
+ PrintUsage();
+ return false;
+ }
+
+ std::string error_msg;
+ for (int i = 0; i < argc; i++) {
+ const StringPiece option(argv[i]);
+ if (option.starts_with("--boot-image=")) {
+ boot_image_location_ = option.substr(strlen("--boot-image=")).data();
+ } else if (option.starts_with("--instruction-set=")) {
+ StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
+ instruction_set_ = GetInstructionSetFromString(instruction_set_str.data());
+ if (instruction_set_ == kNone) {
+ fprintf(stderr, "Unsupported instruction set %s\n", instruction_set_str.data());
+ PrintUsage();
+ return false;
+ }
+ } else if (option.starts_with("--output=")) {
+ output_name_ = option.substr(strlen("--output=")).ToString();
+ const char* filename = output_name_.c_str();
+ out_.reset(new std::ofstream(filename));
+ if (!out_->good()) {
+ fprintf(stderr, "Failed to open output filename %s\n", filename);
+ PrintUsage();
+ return false;
+ }
+ os_ = out_.get();
+ } else {
+ ParseStatus parse_status = ParseCustom(option, &error_msg);
+
+ if (parse_status == kParseUnknownArgument) {
+ fprintf(stderr, "Unknown argument %s\n", option.data());
+ }
+
+ if (parse_status != kParseOk) {
+ fprintf(stderr, "%s\n", error_msg.c_str());
+ PrintUsage();
+ return false;
+ }
+ }
+ }
+
+ DBG_LOG << "will call parse checks";
+
+ {
+ ParseStatus checks_status = ParseChecks(&error_msg);
+ if (checks_status != kParseOk) {
+ fprintf(stderr, "%s\n", error_msg.c_str());
+ PrintUsage();
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ virtual std::string GetUsage() const {
+ std::string usage;
+
+ usage += // Required.
+ " --boot-image=<file.art>: provide the image location for the boot class path.\n"
+ " Do not include the arch as part of the name, it is added automatically.\n"
+ " Example: --boot-image=/system/framework/boot.art\n"
+ "\n";
+ usage += StringPrintf( // Optional.
+ " --instruction-set=(arm|arm64|mips|mips64|x86|x86_64): for locating the image\n"
+ " file based on the image location set.\n"
+ " Example: --instruction-set=x86\n"
+ " Default: %s\n"
+ "\n",
+ GetInstructionSetString(kRuntimeISA));
+ usage += // Optional.
+ " --output=<file> may be used to send the output to a file.\n"
+ " Example: --output=/tmp/oatdump.txt\n"
+ "\n";
+
+ return usage;
+ }
+
+ // Specified by --boot-image.
+ const char* boot_image_location_ = nullptr;
+ // Specified by --instruction-set.
+ InstructionSet instruction_set_ = kRuntimeISA;
+ // Specified by --output.
+ std::ostream* os_ = &std::cout;
+ std::unique_ptr<std::ofstream> out_; // If something besides cout is used
+ std::string output_name_;
+
+ virtual ~CmdlineArgs() {}
+
+ bool ParseCheckBootImage(std::string* error_msg) {
+ if (boot_image_location_ == nullptr) {
+ *error_msg = "--boot-image must be specified";
+ return false;
+ }
+
+ DBG_LOG << "boot image location: " << boot_image_location_;
+
+ // Checks for --boot-image location.
+ {
+ std::string boot_image_location = boot_image_location_;
+ size_t file_name_idx = boot_image_location.rfind("/");
+ if (file_name_idx == std::string::npos) { // Prevent a InsertIsaDirectory check failure.
+ *error_msg = "Boot image location must have a / in it";
+ return false;
+ }
+
+ // Don't let image locations with the 'arch' in it through, since it's not a location.
+ // This prevents a common error "Could not create an image space..." when initing the Runtime.
+ if (file_name_idx != std::string::npos) {
+ std::string no_file_name = boot_image_location.substr(0, file_name_idx);
+ size_t ancestor_dirs_idx = no_file_name.rfind("/");
+
+ std::string parent_dir_name;
+ if (ancestor_dirs_idx != std::string::npos) {
+ parent_dir_name = no_file_name.substr(ancestor_dirs_idx + 1);
+ } else {
+ parent_dir_name = no_file_name;
+ }
+
+ DBG_LOG << "boot_image_location parent_dir_name was " << parent_dir_name;
+
+ if (GetInstructionSetFromString(parent_dir_name.c_str()) != kNone) {
+ *error_msg = "Do not specify the architecture as part of the boot image location";
+ return false;
+ }
+ }
+
+ // Check that the boot image location points to a valid file name.
+ std::string file_name;
+ if (!LocationToFilename(boot_image_location, instruction_set_, &file_name)) {
+ *error_msg = StringPrintf("No corresponding file for location '%s' exists",
+ file_name.c_str());
+ return false;
+ }
+
+ DBG_LOG << "boot_image_filename does exist: " << file_name;
+ }
+
+ return true;
+ }
+
+ void PrintUsage() {
+ fprintf(stderr, "%s", GetUsage().c_str());
+ }
+
+ protected:
+ virtual ParseStatus ParseCustom(const StringPiece& option ATTRIBUTE_UNUSED,
+ std::string* error_msg ATTRIBUTE_UNUSED) {
+ return kParseUnknownArgument;
+ }
+
+ virtual ParseStatus ParseChecks(std::string* error_msg ATTRIBUTE_UNUSED) {
+ return kParseOk;
+ }
+};
+
+template <typename Args = CmdlineArgs>
+struct CmdlineMain {
+ int Main(int argc, char** argv) {
+ InitLogging(argv);
+ std::unique_ptr<Args> args = std::unique_ptr<Args>(CreateArguments());
+ args_ = args.get();
+
+ DBG_LOG << "Try to parse";
+
+ if (args_ == nullptr || !args_->Parse(argc, argv)) {
+ return EXIT_FAILURE;
+ }
+
+ bool needs_runtime = NeedsRuntime();
+ std::unique_ptr<Runtime> runtime;
+
+
+ if (needs_runtime) {
+ std::string error_msg;
+ if (!args_->ParseCheckBootImage(&error_msg)) {
+ fprintf(stderr, "%s\n", error_msg.c_str());
+ args_->PrintUsage();
+ return EXIT_FAILURE;
+ }
+ runtime.reset(CreateRuntime(args.get()));
+ if (runtime == nullptr) {
+ return EXIT_FAILURE;
+ }
+ if (!ExecuteWithRuntime(runtime.get())) {
+ return EXIT_FAILURE;
+ }
+ } else {
+ if (!ExecuteWithoutRuntime()) {
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (!ExecuteCommon()) {
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+ }
+
+ // Override this function to create your own arguments.
+ // Usually will want to return a subtype of CmdlineArgs.
+ virtual Args* CreateArguments() {
+ return new Args();
+ }
+
+ // Override this function to do something else with the runtime.
+ virtual bool ExecuteWithRuntime(Runtime* runtime) {
+ CHECK(runtime != nullptr);
+ // Do nothing
+ return true;
+ }
+
+ // Does the code execution need a runtime? Sometimes it doesn't.
+ virtual bool NeedsRuntime() {
+ return true;
+ }
+
+ // Do execution without having created a runtime.
+ virtual bool ExecuteWithoutRuntime() {
+ return true;
+ }
+
+ // Continue execution after ExecuteWith[out]Runtime
+ virtual bool ExecuteCommon() {
+ return true;
+ }
+
+ virtual ~CmdlineMain() {}
+
+ protected:
+ Args* args_ = nullptr;
+
+ private:
+ Runtime* CreateRuntime(CmdlineArgs* args) {
+ CHECK(args != nullptr);
+
+ return StartRuntime(args->boot_image_location_, args->instruction_set_);
+ }
+};
+} // namespace art
+
+#endif // ART_CMDLINE_CMDLINE_H_
diff --git a/cmdline/cmdline_parse_result.h b/cmdline/cmdline_parse_result.h
new file mode 100644
index 0000000..d6ac341
--- /dev/null
+++ b/cmdline/cmdline_parse_result.h
@@ -0,0 +1,138 @@
+/*
+ * 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_CMDLINE_CMDLINE_PARSE_RESULT_H_
+#define ART_CMDLINE_CMDLINE_PARSE_RESULT_H_
+
+#include "cmdline_result.h"
+#include "detail/cmdline_parser_detail.h"
+
+namespace art {
+// Result of a type-parsing attempt. If successful holds the strongly-typed value,
+// otherwise it holds either a usage or a failure string message that should be displayed back
+// to the user.
+//
+// CmdlineType::Parse/CmdlineType::ParseAndAppend must return this type.
+template <typename T>
+struct CmdlineParseResult : CmdlineResult {
+ using CmdlineResult::CmdlineResult;
+
+ // Create an error result with the usage error code and the specified message.
+ static CmdlineParseResult Usage(const std::string& message) {
+ return CmdlineParseResult(kUsage, message);
+ }
+
+ // Create an error result with the failure error code and no message.
+ static CmdlineParseResult<T> Failure() {
+ return CmdlineParseResult(kFailure);
+ }
+
+ // Create an error result with the failure error code and no message.
+ static CmdlineParseResult<T> Failure(const std::string& message) {
+ return CmdlineParseResult(kFailure, message);
+ }
+
+ // Create a successful result which holds the specified value.
+ static CmdlineParseResult<T> Success(const T& value) {
+ return CmdlineParseResult(value);
+ }
+
+ // Create a successful result, taking over the value.
+ static CmdlineParseResult<T> Success(T&& value) {
+ return CmdlineParseResult(std::forward<T>(value));
+ }
+
+ // Create succesful result, without any values. Used when a value was successfully appended
+ // into an existing object.
+ static CmdlineParseResult<T> SuccessNoValue() {
+ return CmdlineParseResult(T {});
+ }
+
+ // Create an error result with the OutOfRange error and the specified message.
+ static CmdlineParseResult<T> OutOfRange(const std::string& message) {
+ return CmdlineParseResult(kOutOfRange, message);
+ }
+
+ // Create an error result with the OutOfRange code and a custom message
+ // which is printed from the actual/min/max values.
+ // Values are converted to string using the ostream<< operator.
+ static CmdlineParseResult<T> OutOfRange(const T& value,
+ const T& min,
+ const T& max) {
+ return CmdlineParseResult(kOutOfRange,
+ "actual: " + art::detail::ToStringAny(value) +
+ ", min: " + art::detail::ToStringAny(min) +
+ ", max: " + art::detail::ToStringAny(max));
+ }
+
+ // Get a read-only reference to the underlying value.
+ // The result must have been successful and must have a value.
+ const T& GetValue() const {
+ assert(IsSuccess());
+ assert(has_value_);
+ return value_;
+ }
+
+ // Get a mutable reference to the underlying value.
+ // The result must have been successful and must have a value.
+ T& GetValue() {
+ assert(IsSuccess());
+ assert(has_value_);
+ return value_;
+ }
+
+ // Take over the value.
+ // The result must have been successful and must have a value.
+ T&& ReleaseValue() {
+ assert(IsSuccess());
+ assert(has_value_);
+ return std::move(value_);
+ }
+
+ // Whether or not the result has a value (e.g. created with Result::Success).
+ // Error results never have values, success results commonly, but not always, have values.
+ bool HasValue() const {
+ return has_value_;
+ }
+
+ // Cast an error-result from type T2 to T1.
+ // Safe since error-results don't store a typed value.
+ template <typename T2>
+ static CmdlineParseResult<T> CastError(const CmdlineParseResult<T2>& other) {
+ assert(other.IsError());
+ return CmdlineParseResult<T>(other.GetStatus());
+ }
+
+ // Make sure copying is allowed
+ CmdlineParseResult(const CmdlineParseResult& other) = default;
+ // Make sure moving is cheap
+ CmdlineParseResult(CmdlineParseResult&& other) = default;
+
+ private:
+ explicit CmdlineParseResult(const T& value)
+ : CmdlineResult(kSuccess), value_(value), has_value_(true) {}
+ explicit CmdlineParseResult(T&& value)
+ : CmdlineResult(kSuccess), value_(std::forward<T>(value)), has_value_(true) {}
+ explicit CmdlineParseResult()
+ : CmdlineResult(kSuccess), value_(), has_value_(false) {}
+
+ T value_;
+ bool has_value_ = false;
+};
+
+} // namespace art
+
+#endif // ART_CMDLINE_CMDLINE_PARSE_RESULT_H_
diff --git a/cmdline/cmdline_parser.h b/cmdline/cmdline_parser.h
new file mode 100644
index 0000000..a555356
--- /dev/null
+++ b/cmdline/cmdline_parser.h
@@ -0,0 +1,635 @@
+/*
+ * 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_CMDLINE_CMDLINE_PARSER_H_
+#define ART_CMDLINE_CMDLINE_PARSER_H_
+
+#define CMDLINE_NDEBUG 1 // Do not output any debugging information for parsing.
+
+#include "cmdline/detail/cmdline_parser_detail.h"
+#include "cmdline/detail/cmdline_parse_argument_detail.h"
+#include "cmdline/detail/cmdline_debug_detail.h"
+
+#include "cmdline_type_parser.h"
+#include "token_range.h"
+#include "cmdline_types.h"
+#include "cmdline_result.h"
+#include "cmdline_parse_result.h"
+
+#include "runtime/base/variant_map.h"
+#include "utils.h"
+
+#include <vector>
+#include <memory>
+
+namespace art {
+// Build a parser for command line arguments with a small domain specific language.
+// Each parsed type must have a specialized CmdlineType<T> in order to do the string->T parsing.
+// Each argument must also have a VariantMap::Key<T> in order to do the T storage.
+template <typename TVariantMap,
+ template <typename TKeyValue> class TVariantMapKey>
+struct CmdlineParser {
+ template <typename TArg>
+ struct ArgumentBuilder;
+
+ struct Builder; // Build the parser.
+ struct UntypedArgumentBuilder; // Build arguments which weren't yet given a type.
+
+ private:
+ // Forward declare some functions that we need to use before fully-defining structs.
+ template <typename TArg>
+ static ArgumentBuilder<TArg> CreateArgumentBuilder(Builder& parent);
+ static void AppendCompletedArgument(Builder& builder, detail::CmdlineParseArgumentAny* arg);
+
+ // Allow argument definitions to save their values when they are parsed,
+ // without having a dependency on CmdlineParser or any of the builders.
+ //
+ // A shared pointer to the save destination is saved into the load/save argument callbacks.
+ //
+ // This also allows the underlying storage (i.e. a variant map) to be released
+ // to the user, without having to recreate all of the callbacks.
+ struct SaveDestination {
+ SaveDestination() : variant_map_(new TVariantMap()) {}
+
+ // Save value to the variant map.
+ template <typename TArg>
+ void SaveToMap(const TVariantMapKey<TArg>& key, TArg& value) {
+ variant_map_->Set(key, value);
+ }
+
+ // Get the existing value from a map, creating the value if it did not already exist.
+ template <typename TArg>
+ TArg& GetOrCreateFromMap(const TVariantMapKey<TArg>& key) {
+ auto* ptr = variant_map_->Get(key);
+ if (ptr == nullptr) {
+ variant_map_->Set(key, TArg());
+ ptr = variant_map_->Get(key);
+ assert(ptr != nullptr);
+ }
+
+ return *ptr;
+ }
+
+ protected:
+ // Release the map, clearing it as a side-effect.
+ // Future saves will be distinct from previous saves.
+ TVariantMap&& ReleaseMap() {
+ return std::move(*variant_map_);
+ }
+
+ // Get a read-only reference to the variant map.
+ const TVariantMap& GetMap() {
+ return *variant_map_;
+ }
+
+ // Clear all potential save targets.
+ void Clear() {
+ variant_map_->Clear();
+ }
+
+ private:
+ // Don't try to copy or move this. Just don't.
+ SaveDestination(const SaveDestination&) = delete;
+ SaveDestination(SaveDestination&&) = delete;
+ SaveDestination& operator=(const SaveDestination&) = delete;
+ SaveDestination& operator=(SaveDestination&&) = delete;
+
+ std::shared_ptr<TVariantMap> variant_map_;
+
+ // Allow the parser to change the underlying pointers when we release the underlying storage.
+ friend struct CmdlineParser;
+ };
+
+ public:
+ // Builder for the argument definition of type TArg. Do not use this type directly,
+ // it is only a separate type to provide compile-time enforcement against doing
+ // illegal builds.
+ template <typename TArg>
+ struct ArgumentBuilder {
+ // Add a range check to this argument.
+ ArgumentBuilder<TArg>& WithRange(const TArg& min, const TArg& max) {
+ argument_info_.has_range_ = true;
+ argument_info_.min_ = min;
+ argument_info_.max_ = max;
+
+ return *this;
+ }
+
+ // Map the list of names into the list of values. List of names must not have
+ // any wildcards '_' in it.
+ //
+ // Do not use if a value map has already been set.
+ ArgumentBuilder<TArg>& WithValues(std::initializer_list<TArg> value_list) {
+ SetValuesInternal(value_list);
+ return *this;
+ }
+
+ // When used with a single alias, map the alias into this value.
+ // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
+ ArgumentBuilder<TArg> WithValue(const TArg& value) {
+ return WithValues({ value });
+ }
+
+ // Map the parsed string values (from _) onto a concrete value. If no wildcard
+ // has been specified, then map the value directly from the arg name (i.e.
+ // if there are multiple aliases, then use the alias to do the mapping).
+ //
+ // Do not use if a values list has already been set.
+ ArgumentBuilder<TArg>& WithValueMap(
+ std::initializer_list<std::pair<const char*, TArg>> key_value_list) {
+ assert(!argument_info_.has_value_list_);
+
+ argument_info_.has_value_map_ = true;
+ argument_info_.value_map_ = key_value_list;
+
+ return *this;
+ }
+
+ // If this argument is seen multiple times, successive arguments mutate the same value
+ // instead of replacing it with a new value.
+ ArgumentBuilder<TArg>& AppendValues() {
+ argument_info_.appending_values_ = true;
+
+ return *this;
+ }
+
+ // Convenience type alias for the variant map key type definition.
+ using MapKey = TVariantMapKey<TArg>;
+
+ // Write the results of this argument into the key.
+ // To look up the parsed arguments, get the map and then use this key with VariantMap::Get
+ CmdlineParser::Builder& IntoKey(const MapKey& key) {
+ // Only capture save destination as a pointer.
+ // This allows the parser to later on change the specific save targets.
+ auto save_destination = save_destination_;
+ save_value_ = [save_destination, &key](TArg& value) {
+ save_destination->SaveToMap(key, value);
+ CMDLINE_DEBUG_LOG << "Saved value into map '"
+ << detail::ToStringAny(value) << "'" << std::endl;
+ };
+
+ load_value_ = [save_destination, &key]() -> TArg& {
+ TArg& value = save_destination->GetOrCreateFromMap(key);
+ CMDLINE_DEBUG_LOG << "Loaded value from map '" << detail::ToStringAny(value) << "'"
+ << std::endl;
+
+ return value;
+ };
+
+ save_value_specified_ = true;
+ load_value_specified_ = true;
+
+ CompleteArgument();
+ return parent_;
+ }
+
+ // Ensure we always move this when returning a new builder.
+ ArgumentBuilder(ArgumentBuilder&&) = default;
+
+ protected:
+ // Used by builder to internally ignore arguments by dropping them on the floor after parsing.
+ CmdlineParser::Builder& IntoIgnore() {
+ save_value_ = [](TArg& value) {
+ CMDLINE_DEBUG_LOG << "Ignored value '" << detail::ToStringAny(value) << "'" << std::endl;
+ };
+ load_value_ = []() -> TArg& {
+ assert(false && "Should not be appending values to ignored arguments");
+ return *reinterpret_cast<TArg*>(0); // Blow up.
+ };
+
+ save_value_specified_ = true;
+ load_value_specified_ = true;
+
+ CompleteArgument();
+ return parent_;
+ }
+
+ void SetValuesInternal(const std::vector<TArg>&& value_list) {
+ assert(!argument_info_.has_value_map_);
+
+ argument_info_.has_value_list_ = true;
+ argument_info_.value_list_ = value_list;
+ }
+
+ void SetNames(std::vector<const char*>&& names) {
+ argument_info_.names_ = names;
+ }
+
+ void SetNames(std::initializer_list<const char*> names) {
+ argument_info_.names_ = names;
+ }
+
+ private:
+ // Copying is bad. Move only.
+ ArgumentBuilder(const ArgumentBuilder&) = delete;
+
+ // Called by any function that doesn't chain back into this builder.
+ // Completes the argument builder and save the information into the main builder.
+ void CompleteArgument() {
+ assert(save_value_specified_ &&
+ "No Into... function called, nowhere to save parsed values to");
+ assert(load_value_specified_ &&
+ "No Into... function called, nowhere to load parsed values from");
+
+ argument_info_.CompleteArgument();
+
+ // Appending the completed argument is destructive. The object is no longer
+ // usable since all the useful information got moved out of it.
+ AppendCompletedArgument(parent_,
+ new detail::CmdlineParseArgument<TArg>(
+ std::move(argument_info_),
+ std::move(save_value_),
+ std::move(load_value_)));
+ }
+
+ friend struct CmdlineParser;
+ friend struct CmdlineParser::Builder;
+ friend struct CmdlineParser::UntypedArgumentBuilder;
+
+ ArgumentBuilder(CmdlineParser::Builder& parser,
+ std::shared_ptr<SaveDestination> save_destination)
+ : parent_(parser),
+ save_value_specified_(false),
+ load_value_specified_(false),
+ save_destination_(save_destination) {
+ save_value_ = [](TArg&) {
+ assert(false && "No save value function defined");
+ };
+
+ load_value_ = []() -> TArg& {
+ assert(false && "No load value function defined");
+ return *reinterpret_cast<TArg*>(0); // Blow up.
+ };
+ }
+
+ CmdlineParser::Builder& parent_;
+ std::function<void(TArg&)> save_value_;
+ std::function<TArg&(void)> load_value_;
+ bool save_value_specified_;
+ bool load_value_specified_;
+ detail::CmdlineParserArgumentInfo<TArg> argument_info_;
+
+ std::shared_ptr<SaveDestination> save_destination_;
+ };
+
+ struct UntypedArgumentBuilder {
+ // Set a type for this argument. The specific subcommand parser is looked up by the type.
+ template <typename TArg>
+ ArgumentBuilder<TArg> WithType() {
+ return CreateTypedBuilder<TArg>();
+ }
+
+ // When used with multiple aliases, map the position of the alias to the value position.
+ template <typename TArg>
+ ArgumentBuilder<TArg> WithValues(std::initializer_list<TArg> values) {
+ auto&& a = CreateTypedBuilder<TArg>();
+ a.WithValues(values);
+ return std::move(a);
+ }
+
+ // When used with a single alias, map the alias into this value.
+ // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
+ template <typename TArg>
+ ArgumentBuilder<TArg> WithValue(const TArg& value) {
+ return WithValues({ value });
+ }
+
+ // Set the current building argument to target this key.
+ // When this command line argument is parsed, it can be fetched with this key.
+ Builder& IntoKey(const TVariantMapKey<Unit>& key) {
+ return CreateTypedBuilder<Unit>().IntoKey(key);
+ }
+
+ // Ensure we always move this when returning a new builder.
+ UntypedArgumentBuilder(UntypedArgumentBuilder&&) = default;
+
+ protected:
+ void SetNames(std::vector<const char*>&& names) {
+ names_ = std::move(names);
+ }
+
+ void SetNames(std::initializer_list<const char*> names) {
+ names_ = names;
+ }
+
+ private:
+ // No copying. Move instead.
+ UntypedArgumentBuilder(const UntypedArgumentBuilder&) = delete;
+
+ template <typename TArg>
+ ArgumentBuilder<TArg> CreateTypedBuilder() {
+ auto&& b = CreateArgumentBuilder<TArg>(parent_);
+ InitializeTypedBuilder(&b); // Type-specific initialization
+ b.SetNames(std::move(names_));
+ return std::move(b);
+ }
+
+ template <typename TArg = Unit>
+ typename std::enable_if<std::is_same<TArg, Unit>::value>::type
+ InitializeTypedBuilder(ArgumentBuilder<TArg>* arg_builder) {
+ // Every Unit argument implicitly maps to a runtime value of Unit{}
+ std::vector<Unit> values(names_.size(), Unit{}); // NOLINT [whitespace/braces] [5]
+ arg_builder->SetValuesInternal(std::move(values));
+ }
+
+ // No extra work for all other types
+ void InitializeTypedBuilder(void*) {}
+
+ template <typename TArg>
+ friend struct ArgumentBuilder;
+ friend struct Builder;
+
+ explicit UntypedArgumentBuilder(CmdlineParser::Builder& parent) : parent_(parent) {}
+ // UntypedArgumentBuilder(UntypedArgumentBuilder&& other) = default;
+
+ CmdlineParser::Builder& parent_;
+ std::vector<const char*> names_;
+ };
+
+ // Build a new parser given a chain of calls to define arguments.
+ struct Builder {
+ Builder() : save_destination_(new SaveDestination()) {}
+
+ // Define a single argument. The default type is Unit.
+ UntypedArgumentBuilder Define(const char* name) {
+ return Define({name});
+ }
+
+ // Define a single argument with multiple aliases.
+ UntypedArgumentBuilder Define(std::initializer_list<const char*> names) {
+ auto&& b = UntypedArgumentBuilder(*this);
+ b.SetNames(names);
+ return std::move(b);
+ }
+
+ // Whether the parser should give up on unrecognized arguments. Not recommended.
+ Builder& IgnoreUnrecognized(bool ignore_unrecognized) {
+ ignore_unrecognized_ = ignore_unrecognized;
+ return *this;
+ }
+
+ // Provide a list of arguments to ignore for backwards compatibility.
+ Builder& Ignore(std::initializer_list<const char*> ignore_list) {
+ for (auto&& ignore_name : ignore_list) {
+ std::string ign = ignore_name;
+
+ // Ignored arguments are just like a regular definition which have very
+ // liberal parsing requirements (no range checks, no value checks).
+ // Unlike regular argument definitions, when a value gets parsed into its
+ // stronger type, we just throw it away.
+
+ if (ign.find("_") != std::string::npos) { // Does the arg-def have a wildcard?
+ // pretend this is a string, e.g. -Xjitconfig:<anythinggoeshere>
+ auto&& builder = Define(ignore_name).template WithType<std::string>().IntoIgnore();
+ assert(&builder == this);
+ (void)builder; // Ignore pointless unused warning, it's used in the assert.
+ } else {
+ // pretend this is a unit, e.g. -Xjitblocking
+ auto&& builder = Define(ignore_name).template WithType<Unit>().IntoIgnore();
+ assert(&builder == this);
+ (void)builder; // Ignore pointless unused warning, it's used in the assert.
+ }
+ }
+ ignore_list_ = ignore_list;
+ return *this;
+ }
+
+ // Finish building the parser; performs sanity checks. Return value is moved, not copied.
+ // Do not call this more than once.
+ CmdlineParser Build() {
+ assert(!built_);
+ built_ = true;
+
+ auto&& p = CmdlineParser(ignore_unrecognized_,
+ std::move(ignore_list_),
+ save_destination_,
+ std::move(completed_arguments_));
+
+ return std::move(p);
+ }
+
+ protected:
+ void AppendCompletedArgument(detail::CmdlineParseArgumentAny* arg) {
+ auto smart_ptr = std::unique_ptr<detail::CmdlineParseArgumentAny>(arg);
+ completed_arguments_.push_back(std::move(smart_ptr));
+ }
+
+ private:
+ // No copying now!
+ Builder(const Builder& other) = delete;
+
+ template <typename TArg>
+ friend struct ArgumentBuilder;
+ friend struct UntypedArgumentBuilder;
+ friend struct CmdlineParser;
+
+ bool built_ = false;
+ bool ignore_unrecognized_ = false;
+ std::vector<const char*> ignore_list_;
+ std::shared_ptr<SaveDestination> save_destination_;
+
+ std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
+ };
+
+ CmdlineResult Parse(const std::string& argv) {
+ std::vector<std::string> tokenized;
+ Split(argv, ' ', &tokenized);
+
+ return Parse(TokenRange(std::move(tokenized)));
+ }
+
+ // Parse the arguments; storing results into the arguments map. Returns success value.
+ CmdlineResult Parse(const char* argv) {
+ return Parse(std::string(argv));
+ }
+
+ // Parse the arguments; storing the results into the arguments map. Returns success value.
+ // Assumes that argv[0] is a valid argument (i.e. not the program name).
+ CmdlineResult Parse(const std::vector<const char*>& argv) {
+ return Parse(TokenRange(argv.begin(), argv.end()));
+ }
+
+ // Parse the arguments; storing the results into the arguments map. Returns success value.
+ // Assumes that argv[0] is a valid argument (i.e. not the program name).
+ CmdlineResult Parse(const std::vector<std::string>& argv) {
+ return Parse(TokenRange(argv.begin(), argv.end()));
+ }
+
+ // Parse the arguments (directly from an int main(argv,argc)). Returns success value.
+ // Assumes that argv[0] is the program name, and ignores it.
+ CmdlineResult Parse(const char* argv[], int argc) {
+ return Parse(TokenRange(&argv[1], argc - 1)); // ignore argv[0] because it's the program name
+ }
+
+ // Look up the arguments that have been parsed; use the target keys to lookup individual args.
+ const TVariantMap& GetArgumentsMap() const {
+ return save_destination_->GetMap();
+ }
+
+ // Release the arguments map that has been parsed; useful for move semantics.
+ TVariantMap&& ReleaseArgumentsMap() {
+ return save_destination_->ReleaseMap();
+ }
+
+ // How many arguments were defined?
+ size_t CountDefinedArguments() const {
+ return completed_arguments_.size();
+ }
+
+ // Ensure we have a default move constructor.
+ CmdlineParser(CmdlineParser&& other) = default;
+ // Ensure we have a default move assignment operator.
+ CmdlineParser& operator=(CmdlineParser&& other) = default;
+
+ private:
+ friend struct Builder;
+
+ // Construct a new parser from the builder. Move all the arguments.
+ explicit CmdlineParser(bool ignore_unrecognized,
+ std::vector<const char*>&& ignore_list,
+ std::shared_ptr<SaveDestination> save_destination,
+ std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>>&&
+ completed_arguments)
+ : ignore_unrecognized_(ignore_unrecognized),
+ ignore_list_(std::move(ignore_list)),
+ save_destination_(save_destination),
+ completed_arguments_(std::move(completed_arguments)) {
+ assert(save_destination != nullptr);
+ }
+
+ // Parse the arguments; storing results into the arguments map. Returns success value.
+ // The parsing will fail on the first non-success parse result and return that error.
+ //
+ // All previously-parsed arguments are cleared out.
+ // Otherwise, all parsed arguments will be stored into SaveDestination as a side-effect.
+ // A partial parse will result only in a partial save of the arguments.
+ CmdlineResult Parse(TokenRange&& arguments_list) {
+ save_destination_->Clear();
+
+ for (size_t i = 0; i < arguments_list.Size(); ) {
+ TokenRange possible_name = arguments_list.Slice(i);
+
+ size_t best_match_size = 0; // How many tokens were matched in the best case.
+ size_t best_match_arg_idx = 0;
+ bool matched = false; // At least one argument definition has been matched?
+
+ // Find the closest argument definition for the remaining token range.
+ size_t arg_idx = 0;
+ for (auto&& arg : completed_arguments_) {
+ size_t local_match = arg->MaybeMatches(possible_name);
+
+ if (local_match > best_match_size) {
+ best_match_size = local_match;
+ best_match_arg_idx = arg_idx;
+ matched = true;
+ }
+ arg_idx++;
+ }
+
+ // Saw some kind of unknown argument
+ if (matched == false) {
+ if (UNLIKELY(ignore_unrecognized_)) { // This is usually off, we only need it for JNI.
+ // Consume 1 token and keep going, hopefully the next token is a good one.
+ ++i;
+ continue;
+ }
+ // Common case:
+ // Bail out on the first unknown argument with an error.
+ return CmdlineResult(CmdlineResult::kUnknown,
+ std::string("Unknown argument: ") + possible_name[0]);
+ }
+
+ // Look at the best-matched argument definition and try to parse against that.
+ auto&& arg = completed_arguments_[best_match_arg_idx];
+
+ assert(arg->MaybeMatches(possible_name) == best_match_size);
+
+ // Try to parse the argument now, if we have enough tokens.
+ std::pair<size_t, size_t> num_tokens = arg->GetNumTokens();
+ size_t min_tokens;
+ size_t max_tokens;
+
+ std::tie(min_tokens, max_tokens) = num_tokens;
+
+ if ((i + min_tokens) > arguments_list.Size()) {
+ // expected longer command line but it was too short
+ // e.g. if the argv was only "-Xms" without specifying a memory option
+ CMDLINE_DEBUG_LOG << "Parse failure, i = " << i << ", arg list " << arguments_list.Size() <<
+ " num tokens in arg_def: " << min_tokens << "," << max_tokens << std::endl;
+ return CmdlineResult(CmdlineResult::kFailure,
+ std::string("Argument ") +
+ possible_name[0] + ": incomplete command line arguments, expected "
+ + std::to_string(size_t(i + min_tokens) - arguments_list.Size()) +
+ " more tokens");
+ }
+
+ if (best_match_size > max_tokens || best_match_size < min_tokens) {
+ // Even our best match was out of range, so parsing would fail instantly.
+ return CmdlineResult(CmdlineResult::kFailure,
+ std::string("Argument ") + possible_name[0] + ": too few tokens "
+ "matched " + std::to_string(best_match_size)
+ + " but wanted " + std::to_string(num_tokens.first));
+ }
+
+ // We have enough tokens to begin exact parsing.
+ TokenRange exact_range = possible_name.Slice(0, max_tokens);
+
+ size_t consumed_tokens = 1; // At least 1 if we ever want to try to resume parsing on error
+ CmdlineResult parse_attempt = arg->ParseArgument(exact_range, &consumed_tokens);
+
+ if (parse_attempt.IsError()) {
+ // We may also want to continue parsing the other tokens to gather more errors.
+ return parse_attempt;
+ } // else the value has been successfully stored into the map
+
+ assert(consumed_tokens > 0); // Don't hang in an infinite loop trying to parse
+ i += consumed_tokens;
+
+ // TODO: also handle ignoring arguments for backwards compatibility
+ } // for
+
+ return CmdlineResult(CmdlineResult::kSuccess);
+ }
+
+ bool ignore_unrecognized_ = false;
+ std::vector<const char*> ignore_list_;
+ std::shared_ptr<SaveDestination> save_destination_;
+ std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
+};
+
+// This has to be defined after everything else, since we want the builders to call this.
+template <typename TVariantMap,
+ template <typename TKeyValue> class TVariantMapKey>
+template <typename TArg>
+CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>
+CmdlineParser<TVariantMap, TVariantMapKey>::CreateArgumentBuilder(
+ CmdlineParser<TVariantMap, TVariantMapKey>::Builder& parent) {
+ return CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>(
+ parent, parent.save_destination_);
+}
+
+// This has to be defined after everything else, since we want the builders to call this.
+template <typename TVariantMap,
+ template <typename TKeyValue> class TVariantMapKey>
+void CmdlineParser<TVariantMap, TVariantMapKey>::AppendCompletedArgument(
+ CmdlineParser<TVariantMap, TVariantMapKey>::Builder& builder,
+ detail::CmdlineParseArgumentAny* arg) {
+ builder.AppendCompletedArgument(arg);
+}
+
+} // namespace art
+
+#endif // ART_CMDLINE_CMDLINE_PARSER_H_
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
new file mode 100644
index 0000000..288f7ac
--- /dev/null
+++ b/cmdline/cmdline_parser_test.cc
@@ -0,0 +1,527 @@
+/*
+ * 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.
+ */
+
+#include "cmdline_parser.h"
+#include "runtime/runtime_options.h"
+#include "runtime/parsed_options.h"
+
+#include "utils.h"
+#include <numeric>
+#include "gtest/gtest.h"
+
+#define EXPECT_NULL(expected) EXPECT_EQ(reinterpret_cast<const void*>(expected), \
+ reinterpret_cast<void*>(NULL));
+
+namespace art {
+ bool UsuallyEquals(double expected, double actual);
+
+ // This has a gtest dependency, which is why it's in the gtest only.
+ bool operator==(const TestProfilerOptions& lhs, const TestProfilerOptions& rhs) {
+ return lhs.enabled_ == rhs.enabled_ &&
+ lhs.output_file_name_ == rhs.output_file_name_ &&
+ lhs.period_s_ == rhs.period_s_ &&
+ lhs.duration_s_ == rhs.duration_s_ &&
+ lhs.interval_us_ == rhs.interval_us_ &&
+ UsuallyEquals(lhs.backoff_coefficient_, rhs.backoff_coefficient_) &&
+ UsuallyEquals(lhs.start_immediately_, rhs.start_immediately_) &&
+ UsuallyEquals(lhs.top_k_threshold_, rhs.top_k_threshold_) &&
+ UsuallyEquals(lhs.top_k_change_threshold_, rhs.top_k_change_threshold_) &&
+ lhs.profile_type_ == rhs.profile_type_ &&
+ lhs.max_stack_depth_ == rhs.max_stack_depth_;
+ }
+
+ bool UsuallyEquals(double expected, double actual) {
+ using FloatingPoint = ::testing::internal::FloatingPoint<double>;
+
+ FloatingPoint exp(expected);
+ FloatingPoint act(actual);
+
+ // Compare with ULPs instead of comparing with ==
+ return exp.AlmostEquals(act);
+ }
+
+ template <typename T>
+ bool UsuallyEquals(const T& expected, const T& actual,
+ typename std::enable_if<
+ detail::SupportsEqualityOperator<T>::value>::type* = 0) {
+ return expected == actual;
+ }
+
+ // Try to use memcmp to compare simple plain-old-data structs.
+ //
+ // This should *not* generate false positives, but it can generate false negatives.
+ // This will mostly work except for fields like float which can have different bit patterns
+ // that are nevertheless equal.
+ // If a test is failing because the structs aren't "equal" when they really are
+ // then it's recommended to implement operator== for it instead.
+ template <typename T, typename ... Ignore>
+ bool UsuallyEquals(const T& expected, const T& actual,
+ const Ignore& ... more ATTRIBUTE_UNUSED,
+ typename std::enable_if<std::is_pod<T>::value>::type* = 0,
+ typename std::enable_if<!detail::SupportsEqualityOperator<T>::value>::type* = 0
+ ) {
+ return memcmp(std::addressof(expected), std::addressof(actual), sizeof(T)) == 0;
+ }
+
+ bool UsuallyEquals(const XGcOption& expected, const XGcOption& actual) {
+ return memcmp(std::addressof(expected), std::addressof(actual), sizeof(expected)) == 0;
+ }
+
+ bool UsuallyEquals(const char* expected, std::string actual) {
+ return std::string(expected) == actual;
+ }
+
+ template <typename TMap, typename TKey, typename T>
+ ::testing::AssertionResult IsExpectedKeyValue(const T& expected,
+ const TMap& map,
+ const TKey& key) {
+ auto* actual = map.Get(key);
+ if (actual != nullptr) {
+ if (!UsuallyEquals(expected, *actual)) {
+ return ::testing::AssertionFailure()
+ << "expected " << detail::ToStringAny(expected) << " but got "
+ << detail::ToStringAny(*actual);
+ }
+ return ::testing::AssertionSuccess();
+ }
+
+ return ::testing::AssertionFailure() << "key was not in the map";
+ }
+
+class CmdlineParserTest : public ::testing::Test {
+ public:
+ CmdlineParserTest() = default;
+ ~CmdlineParserTest() = default;
+
+ protected:
+ using M = RuntimeArgumentMap;
+ using RuntimeParser = ParsedOptions::RuntimeParser;
+
+ static void SetUpTestCase() {
+ art::InitLogging(nullptr); // argv = null
+ }
+
+ virtual void SetUp() {
+ parser_ = ParsedOptions::MakeParser(false); // do not ignore unrecognized options
+ }
+
+ static ::testing::AssertionResult IsResultSuccessful(CmdlineResult result) {
+ if (result.IsSuccess()) {
+ return ::testing::AssertionSuccess();
+ } else {
+ return ::testing::AssertionFailure()
+ << result.GetStatus() << " with: " << result.GetMessage();
+ }
+ }
+
+ static ::testing::AssertionResult IsResultFailure(CmdlineResult result,
+ CmdlineResult::Status failure_status) {
+ if (result.IsSuccess()) {
+ return ::testing::AssertionFailure() << " got success but expected failure: "
+ << failure_status;
+ } else if (result.GetStatus() == failure_status) {
+ return ::testing::AssertionSuccess();
+ }
+
+ return ::testing::AssertionFailure() << " expected failure " << failure_status
+ << " but got " << result.GetStatus();
+ }
+
+ std::unique_ptr<RuntimeParser> parser_;
+};
+
+#define EXPECT_KEY_EXISTS(map, key) EXPECT_TRUE((map).Exists(key))
+#define EXPECT_KEY_VALUE(map, key, expected) EXPECT_TRUE(IsExpectedKeyValue(expected, map, key))
+
+#define EXPECT_SINGLE_PARSE_EMPTY_SUCCESS(argv) \
+ do { \
+ EXPECT_TRUE(IsResultSuccessful(parser_->Parse(argv))); \
+ EXPECT_EQ(0u, parser_->GetArgumentsMap().Size()); \
+ } while (false)
+
+#define _EXPECT_SINGLE_PARSE_EXISTS(argv, key) \
+ do { \
+ EXPECT_TRUE(IsResultSuccessful(parser_->Parse(argv))); \
+ RuntimeArgumentMap args = parser_->ReleaseArgumentsMap(); \
+ EXPECT_EQ(1u, args.Size()); \
+ EXPECT_KEY_EXISTS(args, key); \
+
+#define EXPECT_SINGLE_PARSE_EXISTS(argv, key) \
+ _EXPECT_SINGLE_PARSE_EXISTS(argv, key); \
+ } while (false)
+
+#define EXPECT_SINGLE_PARSE_VALUE(expected, argv, key) \
+ _EXPECT_SINGLE_PARSE_EXISTS(argv, key); \
+ EXPECT_KEY_VALUE(args, key, expected); \
+ } while (false) // NOLINT [readability/namespace] [5]
+
+#define EXPECT_SINGLE_PARSE_VALUE_STR(expected, argv, key) \
+ EXPECT_SINGLE_PARSE_VALUE(std::string(expected), argv, key)
+
+#define EXPECT_SINGLE_PARSE_FAIL(argv, failure_status) \
+ do { \
+ EXPECT_TRUE(IsResultFailure(parser_->Parse(argv), failure_status));\
+ RuntimeArgumentMap args = parser_->ReleaseArgumentsMap();\
+ EXPECT_EQ(0u, args.Size()); \
+ } while (false)
+
+TEST_F(CmdlineParserTest, TestSimpleSuccesses) {
+ auto& parser = *parser_;
+
+ EXPECT_LT(0u, parser.CountDefinedArguments());
+
+ {
+ // Test case 1: No command line arguments
+ EXPECT_TRUE(IsResultSuccessful(parser.Parse("")));
+ RuntimeArgumentMap args = parser.ReleaseArgumentsMap();
+ EXPECT_EQ(0u, args.Size());
+ }
+
+ EXPECT_SINGLE_PARSE_EXISTS("-Xzygote", M::Zygote);
+ EXPECT_SINGLE_PARSE_VALUE_STR("/hello/world", "-Xbootclasspath:/hello/world", M::BootClassPath);
+ EXPECT_SINGLE_PARSE_VALUE("/hello/world", "-Xbootclasspath:/hello/world", M::BootClassPath);
+ EXPECT_SINGLE_PARSE_VALUE(false, "-Xverify:none", M::Verify);
+ EXPECT_SINGLE_PARSE_VALUE(true, "-Xverify:remote", M::Verify);
+ EXPECT_SINGLE_PARSE_VALUE(true, "-Xverify:all", M::Verify);
+ EXPECT_SINGLE_PARSE_VALUE(Memory<1>(234), "-Xss234", M::StackSize);
+ EXPECT_SINGLE_PARSE_VALUE(MemoryKiB(1234*MB), "-Xms1234m", M::MemoryInitialSize);
+ EXPECT_SINGLE_PARSE_VALUE(true, "-XX:EnableHSpaceCompactForOOM", M::EnableHSpaceCompactForOOM);
+ EXPECT_SINGLE_PARSE_VALUE(false, "-XX:DisableHSpaceCompactForOOM", M::EnableHSpaceCompactForOOM);
+ EXPECT_SINGLE_PARSE_VALUE(0.5, "-XX:HeapTargetUtilization=0.5", M::HeapTargetUtilization);
+ EXPECT_SINGLE_PARSE_VALUE(5u, "-XX:ParallelGCThreads=5", M::ParallelGCThreads);
+} // TEST_F
+
+TEST_F(CmdlineParserTest, TestSimpleFailures) {
+ // Test argument is unknown to the parser
+ EXPECT_SINGLE_PARSE_FAIL("abcdefg^%@#*(@#", CmdlineResult::kUnknown);
+ // Test value map substitution fails
+ EXPECT_SINGLE_PARSE_FAIL("-Xverify:whatever", CmdlineResult::kFailure);
+ // Test value type parsing failures
+ EXPECT_SINGLE_PARSE_FAIL("-Xsswhatever", CmdlineResult::kFailure); // invalid memory value
+ EXPECT_SINGLE_PARSE_FAIL("-Xms123", CmdlineResult::kFailure); // memory value too small
+ EXPECT_SINGLE_PARSE_FAIL("-XX:HeapTargetUtilization=0.0", CmdlineResult::kOutOfRange); // toosmal
+ EXPECT_SINGLE_PARSE_FAIL("-XX:HeapTargetUtilization=2.0", CmdlineResult::kOutOfRange); // toolarg
+ EXPECT_SINGLE_PARSE_FAIL("-XX:ParallelGCThreads=-5", CmdlineResult::kOutOfRange); // too small
+ EXPECT_SINGLE_PARSE_FAIL("-Xgc:blablabla", CmdlineResult::kUsage); // not a valid suboption
+} // TEST_F
+
+TEST_F(CmdlineParserTest, TestLogVerbosity) {
+ {
+ const char* log_args = "-verbose:"
+ "class,compiler,gc,heap,jdwp,jni,monitor,profiler,signals,startup,third-party-jni,"
+ "threads,verifier";
+
+ LogVerbosity log_verbosity = LogVerbosity();
+ log_verbosity.class_linker = true;
+ log_verbosity.compiler = true;
+ log_verbosity.gc = true;
+ log_verbosity.heap = true;
+ log_verbosity.jdwp = true;
+ log_verbosity.jni = true;
+ log_verbosity.monitor = true;
+ log_verbosity.profiler = true;
+ log_verbosity.signals = true;
+ log_verbosity.startup = true;
+ log_verbosity.third_party_jni = true;
+ log_verbosity.threads = true;
+ log_verbosity.verifier = true;
+
+ EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
+ }
+
+ {
+ const char* log_args = "-verbose:"
+ "class,compiler,gc,heap,jdwp,jni,monitor";
+
+ LogVerbosity log_verbosity = LogVerbosity();
+ log_verbosity.class_linker = true;
+ log_verbosity.compiler = true;
+ log_verbosity.gc = true;
+ log_verbosity.heap = true;
+ log_verbosity.jdwp = true;
+ log_verbosity.jni = true;
+ log_verbosity.monitor = true;
+
+ EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
+ }
+
+ EXPECT_SINGLE_PARSE_FAIL("-verbose:blablabla", CmdlineResult::kUsage); // invalid verbose opt
+} // TEST_F
+
+// TODO: Enable this b/19274810
+TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) {
+ /*
+ * Test success
+ */
+ {
+ XGcOption option_all_true{}; // NOLINT [readability/braces] [4]
+ option_all_true.collector_type_ = gc::CollectorType::kCollectorTypeCMS;
+ option_all_true.verify_pre_gc_heap_ = true;
+ option_all_true.verify_pre_sweeping_heap_ = true;
+ option_all_true.verify_post_gc_heap_ = true;
+ option_all_true.verify_pre_gc_rosalloc_ = true;
+ option_all_true.verify_pre_sweeping_rosalloc_ = true;
+ option_all_true.verify_post_gc_rosalloc_ = true;
+
+ const char * xgc_args_all_true = "-Xgc:concurrent,"
+ "preverify,presweepingverify,postverify,"
+ "preverify_rosalloc,presweepingverify_rosalloc,"
+ "postverify_rosalloc,precise,"
+ "verifycardtable";
+
+ EXPECT_SINGLE_PARSE_VALUE(option_all_true, xgc_args_all_true, M::GcOption);
+
+ XGcOption option_all_false{}; // NOLINT [readability/braces] [4]
+ option_all_false.collector_type_ = gc::CollectorType::kCollectorTypeMS;
+ option_all_false.verify_pre_gc_heap_ = false;
+ option_all_false.verify_pre_sweeping_heap_ = false;
+ option_all_false.verify_post_gc_heap_ = false;
+ option_all_false.verify_pre_gc_rosalloc_ = false;
+ option_all_false.verify_pre_sweeping_rosalloc_ = false;
+ option_all_false.verify_post_gc_rosalloc_ = false;
+
+ const char* xgc_args_all_false = "-Xgc:nonconcurrent,"
+ "nopreverify,nopresweepingverify,nopostverify,nopreverify_rosalloc,"
+ "nopresweepingverify_rosalloc,nopostverify_rosalloc,noprecise,noverifycardtable";
+
+ EXPECT_SINGLE_PARSE_VALUE(option_all_false, xgc_args_all_false, M::GcOption);
+
+ XGcOption option_all_default{}; // NOLINT [readability/braces] [4]
+
+ const char* xgc_args_blank = "-Xgc:";
+ EXPECT_SINGLE_PARSE_VALUE(option_all_default, xgc_args_blank, M::GcOption);
+ }
+
+ /*
+ * Test failures
+ */
+ EXPECT_SINGLE_PARSE_FAIL("-Xgc:blablabla", CmdlineResult::kUsage); // invalid Xgc opt
+} // TEST_F
+
+/*
+ * {"-Xrunjdwp:_", "-agentlib:jdwp=_"}
+ */
+TEST_F(CmdlineParserTest, TestJdwpOptions) {
+ /*
+ * Test success
+ */
+ {
+ /*
+ * "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n"
+ */
+ JDWP::JdwpOptions opt = JDWP::JdwpOptions();
+ opt.transport = JDWP::JdwpTransportType::kJdwpTransportSocket;
+ opt.port = 8000;
+ opt.server = true;
+
+ const char *opt_args = "-Xrunjdwp:transport=dt_socket,address=8000,server=y";
+
+ EXPECT_SINGLE_PARSE_VALUE(opt, opt_args, M::JdwpOptions);
+ }
+
+ {
+ /*
+ * "Example: -agentlib:jdwp=transport=dt_socket,address=localhost:6500,server=n\n");
+ */
+ JDWP::JdwpOptions opt = JDWP::JdwpOptions();
+ opt.transport = JDWP::JdwpTransportType::kJdwpTransportSocket;
+ opt.host = "localhost";
+ opt.port = 6500;
+ opt.server = false;
+
+ const char *opt_args = "-agentlib:jdwp=transport=dt_socket,address=localhost:6500,server=n";
+
+ EXPECT_SINGLE_PARSE_VALUE(opt, opt_args, M::JdwpOptions);
+ }
+
+ /*
+ * Test failures
+ */
+ EXPECT_SINGLE_PARSE_FAIL("-Xrunjdwp:help", CmdlineResult::kUsage); // usage for help only
+ EXPECT_SINGLE_PARSE_FAIL("-Xrunjdwp:blabla", CmdlineResult::kFailure); // invalid subarg
+ EXPECT_SINGLE_PARSE_FAIL("-agentlib:jdwp=help", CmdlineResult::kUsage); // usage for help only
+ EXPECT_SINGLE_PARSE_FAIL("-agentlib:jdwp=blabla", CmdlineResult::kFailure); // invalid subarg
+} // TEST_F
+
+/*
+ * -D_ -D_ -D_ ...
+ */
+TEST_F(CmdlineParserTest, TestPropertiesList) {
+ /*
+ * Test successes
+ */
+ {
+ std::vector<std::string> opt = {"hello"};
+
+ EXPECT_SINGLE_PARSE_VALUE(opt, "-Dhello", M::PropertiesList);
+ }
+
+ {
+ std::vector<std::string> opt = {"hello", "world"};
+
+ EXPECT_SINGLE_PARSE_VALUE(opt, "-Dhello -Dworld", M::PropertiesList);
+ }
+
+ {
+ std::vector<std::string> opt = {"one", "two", "three"};
+
+ EXPECT_SINGLE_PARSE_VALUE(opt, "-Done -Dtwo -Dthree", M::PropertiesList);
+ }
+} // TEST_F
+
+/*
+* -Xcompiler-option foo -Xcompiler-option bar ...
+*/
+TEST_F(CmdlineParserTest, TestCompilerOption) {
+ /*
+ * Test successes
+ */
+ {
+ std::vector<std::string> opt = {"hello"};
+ EXPECT_SINGLE_PARSE_VALUE(opt, "-Xcompiler-option hello", M::CompilerOptions);
+ }
+
+ {
+ std::vector<std::string> opt = {"hello", "world"};
+ EXPECT_SINGLE_PARSE_VALUE(opt,
+ "-Xcompiler-option hello -Xcompiler-option world",
+ M::CompilerOptions);
+ }
+
+ {
+ std::vector<std::string> opt = {"one", "two", "three"};
+ EXPECT_SINGLE_PARSE_VALUE(opt,
+ "-Xcompiler-option one -Xcompiler-option two -Xcompiler-option three",
+ M::CompilerOptions);
+ }
+} // TEST_F
+
+/*
+* -X-profile-*
+*/
+TEST_F(CmdlineParserTest, TestProfilerOptions) {
+ /*
+ * Test successes
+ */
+
+ {
+ TestProfilerOptions opt;
+ opt.enabled_ = true;
+
+ EXPECT_SINGLE_PARSE_VALUE(opt,
+ "-Xenable-profiler",
+ M::ProfilerOpts);
+ }
+
+ {
+ TestProfilerOptions opt;
+ // also need to test 'enabled'
+ opt.output_file_name_ = "hello_world.txt";
+
+ EXPECT_SINGLE_PARSE_VALUE(opt,
+ "-Xprofile-filename:hello_world.txt ",
+ M::ProfilerOpts);
+ }
+
+ {
+ TestProfilerOptions opt = TestProfilerOptions();
+ // also need to test 'enabled'
+ opt.output_file_name_ = "output.txt";
+ opt.period_s_ = 123u;
+ opt.duration_s_ = 456u;
+ opt.interval_us_ = 789u;
+ opt.backoff_coefficient_ = 2.0;
+ opt.start_immediately_ = true;
+ opt.top_k_threshold_ = 50.0;
+ opt.top_k_change_threshold_ = 60.0;
+ opt.profile_type_ = kProfilerMethod;
+ opt.max_stack_depth_ = 1337u;
+
+ EXPECT_SINGLE_PARSE_VALUE(opt,
+ "-Xprofile-filename:output.txt "
+ "-Xprofile-period:123 "
+ "-Xprofile-duration:456 "
+ "-Xprofile-interval:789 "
+ "-Xprofile-backoff:2.0 "
+ "-Xprofile-start-immediately "
+ "-Xprofile-top-k-threshold:50.0 "
+ "-Xprofile-top-k-change-threshold:60.0 "
+ "-Xprofile-type:method "
+ "-Xprofile-max-stack-depth:1337",
+ M::ProfilerOpts);
+ }
+
+ {
+ TestProfilerOptions opt = TestProfilerOptions();
+ opt.profile_type_ = kProfilerBoundedStack;
+
+ EXPECT_SINGLE_PARSE_VALUE(opt,
+ "-Xprofile-type:stack",
+ M::ProfilerOpts);
+ }
+} // TEST_F
+
+TEST_F(CmdlineParserTest, TestIgnoreUnrecognized) {
+ RuntimeParser::Builder parserBuilder;
+
+ parserBuilder
+ .Define("-help")
+ .IntoKey(M::Help)
+ .IgnoreUnrecognized(true);
+
+ parser_.reset(new RuntimeParser(parserBuilder.Build()));
+
+ EXPECT_SINGLE_PARSE_EMPTY_SUCCESS("-non-existent-option");
+ EXPECT_SINGLE_PARSE_EMPTY_SUCCESS("-non-existent-option1 --non-existent-option-2");
+} // TEST_F
+
+TEST_F(CmdlineParserTest, TestIgnoredArguments) {
+ std::initializer_list<const char*> ignored_args = {
+ "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
+ "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:abdef",
+ "-Xdexopt:foobar", "-Xnoquithandler", "-Xjnigreflimit:ixnay", "-Xgenregmap", "-Xnogenregmap",
+ "-Xverifyopt:never", "-Xcheckdexsum", "-Xincludeselectedop", "-Xjitop:noop",
+ "-Xincludeselectedmethod", "-Xjitthreshold:123", "-Xjitcodecachesize:12345",
+ "-Xjitblocking", "-Xjitmethod:_", "-Xjitclass:nosuchluck", "-Xjitoffset:none",
+ "-Xjitconfig:yes", "-Xjitcheckcg", "-Xjitverbose", "-Xjitprofile",
+ "-Xjitdisableopt", "-Xjitsuspendpoll", "-XX:mainThreadStackSize=1337"
+ };
+
+ // Check they are ignored when parsed one at a time
+ for (auto&& arg : ignored_args) {
+ SCOPED_TRACE(arg);
+ EXPECT_SINGLE_PARSE_EMPTY_SUCCESS(arg);
+ }
+
+ // Check they are ignored when we pass it all together at once
+ std::vector<const char*> argv = ignored_args;
+ EXPECT_SINGLE_PARSE_EMPTY_SUCCESS(argv);
+} // TEST_F
+
+TEST_F(CmdlineParserTest, MultipleArguments) {
+ EXPECT_TRUE(IsResultSuccessful(parser_->Parse(
+ "-help -XX:ForegroundHeapGrowthMultiplier=0.5 "
+ "-Xnodex2oat -Xmethod-trace -XX:LargeObjectSpace=map")));
+
+ auto&& map = parser_->ReleaseArgumentsMap();
+ EXPECT_EQ(5u, map.Size());
+ EXPECT_KEY_VALUE(map, M::Help, Unit{}); // NOLINT [whitespace/braces] [5]
+ EXPECT_KEY_VALUE(map, M::ForegroundHeapGrowthMultiplier, 0.5);
+ EXPECT_KEY_VALUE(map, M::Dex2Oat, false);
+ EXPECT_KEY_VALUE(map, M::MethodTrace, Unit{}); // NOLINT [whitespace/braces] [5]
+ EXPECT_KEY_VALUE(map, M::LargeObjectSpace, gc::space::LargeObjectSpaceType::kMap);
+} // TEST_F
+} // namespace art
diff --git a/cmdline/cmdline_result.h b/cmdline/cmdline_result.h
new file mode 100644
index 0000000..bf3a85d
--- /dev/null
+++ b/cmdline/cmdline_result.h
@@ -0,0 +1,103 @@
+/*
+ * 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_CMDLINE_CMDLINE_RESULT_H_
+#define ART_CMDLINE_CMDLINE_RESULT_H_
+
+#include <assert.h>
+#include <utils.h>
+
+namespace art {
+ // Result of an attempt to process the command line arguments. If fails, specifies
+ // the specific error code and an error message.
+ // Use the value-carrying CmdlineParseResult<T> to get an additional value out in a success case.
+ struct CmdlineResult {
+ enum Status {
+ kSuccess,
+ // Error codes:
+ kUsage,
+ kFailure,
+ kOutOfRange,
+ kUnknown,
+ };
+
+ // Short-hand for checking if the result was successful.
+ operator bool() const {
+ return IsSuccess();
+ }
+
+ // Check if the operation has succeeded.
+ bool IsSuccess() const { return status_ == kSuccess; }
+ // Check if the operation was not a success.
+ bool IsError() const { return status_ != kSuccess; }
+ // Get the specific status, regardless of whether it's failure or success.
+ Status GetStatus() const { return status_; }
+
+ // Get the error message, *must* only be called for error status results.
+ const std::string& GetMessage() const { assert(IsError()); return message_; }
+
+ // Constructor any status. No message.
+ explicit CmdlineResult(Status status) : status_(status) {}
+
+ // Constructor with an error status, copying the message.
+ CmdlineResult(Status status, const std::string& message)
+ : status_(status), message_(message) {
+ assert(status != kSuccess);
+ }
+
+ // Constructor with an error status, taking over the message.
+ CmdlineResult(Status status, std::string&& message)
+ : status_(status), message_(message) {
+ assert(status != kSuccess);
+ }
+
+ // Make sure copying exists
+ CmdlineResult(const CmdlineResult& other) = default;
+ // Make sure moving is cheap
+ CmdlineResult(CmdlineResult&& other) = default;
+
+ private:
+ const Status status_;
+ const std::string message_;
+ };
+
+ // TODO: code-generate this
+ static inline std::ostream& operator<<(std::ostream& stream, CmdlineResult::Status status) {
+ switch (status) {
+ case CmdlineResult::kSuccess:
+ stream << "kSuccess";
+ break;
+ case CmdlineResult::kUsage:
+ stream << "kUsage";
+ break;
+ case CmdlineResult::kFailure:
+ stream << "kFailure";
+ break;
+ case CmdlineResult::kOutOfRange:
+ stream << "kOutOfRange";
+ break;
+ case CmdlineResult::kUnknown:
+ stream << "kUnknown";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return stream;
+ }
+
+} // namespace art
+
+#endif // ART_CMDLINE_CMDLINE_RESULT_H_
diff --git a/cmdline/cmdline_type_parser.h b/cmdline/cmdline_type_parser.h
new file mode 100644
index 0000000..fa5cdaf
--- /dev/null
+++ b/cmdline/cmdline_type_parser.h
@@ -0,0 +1,76 @@
+/*
+ * 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_CMDLINE_CMDLINE_TYPE_PARSER_H_
+#define ART_CMDLINE_CMDLINE_TYPE_PARSER_H_
+
+#include "cmdline_parse_result.h"
+
+namespace art {
+
+// Base class for user-defined CmdlineType<T> specializations.
+//
+// Not strictly necessary, but if the specializations fail to Define all of these functions
+// the compilation will fail.
+template <typename T>
+struct CmdlineTypeParser {
+ // Return value of parsing attempts. Represents a Success(T value) or an Error(int code)
+ using Result = CmdlineParseResult<T>;
+
+ // Parse a single value for an argument definition out of the wildcard component.
+ //
+ // e.g. if the argument definition was "foo:_", and the user-provided input was "foo:bar",
+ // then args is "bar".
+ Result Parse(const std::string& args ATTRIBUTE_UNUSED) {
+ assert(false);
+ return Result::Failure("Missing type specialization and/or value map");
+ }
+
+ // Parse a value and append it into the existing value so far, for argument
+ // definitions which are marked with AppendValues().
+ //
+ // The value is parsed out of the wildcard component as in Parse.
+ //
+ // If the initial value does not exist yet, a default value is created by
+ // value-initializing with 'T()'.
+ Result ParseAndAppend(const std::string& args ATTRIBUTE_UNUSED,
+ T& existing_value ATTRIBUTE_UNUSED) {
+ assert(false);
+ return Result::Failure("Missing type specialization and/or value map");
+ }
+
+ // Runtime type name of T, so that we can print more useful error messages.
+ static const char* Name() { assert(false); return "UnspecializedType"; }
+
+ // Whether or not your type can parse argument definitions defined without a "_"
+ // e.g. -Xenable-profiler just mutates the existing profiler struct in-place
+ // so it doesn't need to do any parsing other than token recognition.
+ //
+ // If this is false, then either the argument definition has a _, from which the parsing
+ // happens, or the tokens get mapped to a value list/map from which a 1:1 matching occurs.
+ //
+ // This should almost *always* be false!
+ static constexpr bool kCanParseBlankless = false;
+
+ protected:
+ // Don't accidentally initialize instances of this directly; they will assert at runtime.
+ CmdlineTypeParser() = default;
+};
+
+
+} // namespace art
+
+#endif // ART_CMDLINE_CMDLINE_TYPE_PARSER_H_
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
new file mode 100644
index 0000000..45f9b56
--- /dev/null
+++ b/cmdline/cmdline_types.h
@@ -0,0 +1,837 @@
+/*
+ * 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_CMDLINE_CMDLINE_TYPES_H_
+#define ART_CMDLINE_CMDLINE_TYPES_H_
+
+#define CMDLINE_NDEBUG 1 // Do not output any debugging information for parsing.
+
+#include "cmdline/memory_representation.h"
+#include "cmdline/detail/cmdline_debug_detail.h"
+#include "cmdline_type_parser.h"
+
+// Includes for the types that are being specialized
+#include <string>
+#include "unit.h"
+#include "jdwp/jdwp.h"
+#include "runtime/base/logging.h"
+#include "gc/collector_type.h"
+#include "gc/space/large_object_space.h"
+#include "profiler_options.h"
+
+namespace art {
+
+// The default specialization will always fail parsing the type from a string.
+// Provide your own specialization that inherits from CmdlineTypeParser<T>
+// and implements either Parse or ParseAndAppend
+// (only if the argument was defined with ::AppendValues()) but not both.
+template <typename T>
+struct CmdlineType : CmdlineTypeParser<T> {
+};
+
+// Specializations for CmdlineType<T> follow:
+
+// Parse argument definitions for Unit-typed arguments.
+template <>
+struct CmdlineType<Unit> : CmdlineTypeParser<Unit> {
+ Result Parse(const std::string& args) {
+ if (args == "") {
+ return Result::Success(Unit{}); // NOLINT [whitespace/braces] [5]
+ }
+ return Result::Failure("Unexpected extra characters " + args);
+ }
+};
+
+template <>
+struct CmdlineType<JDWP::JdwpOptions> : CmdlineTypeParser<JDWP::JdwpOptions> {
+ /*
+ * Handle one of the JDWP name/value pairs.
+ *
+ * JDWP options are:
+ * help: if specified, show help message and bail
+ * transport: may be dt_socket or dt_shmem
+ * address: for dt_socket, "host:port", or just "port" when listening
+ * server: if "y", wait for debugger to attach; if "n", attach to debugger
+ * timeout: how long to wait for debugger to connect / listen
+ *
+ * Useful with server=n (these aren't supported yet):
+ * onthrow=<exception-name>: connect to debugger when exception thrown
+ * onuncaught=y|n: connect to debugger when uncaught exception thrown
+ * launch=<command-line>: launch the debugger itself
+ *
+ * The "transport" option is required, as is "address" if server=n.
+ */
+ Result Parse(const std::string& options) {
+ VLOG(jdwp) << "ParseJdwpOptions: " << options;
+
+ if (options == "help") {
+ return Result::Usage(
+ "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n"
+ "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n");
+ }
+
+ const std::string s;
+
+ std::vector<std::string> pairs;
+ Split(options, ',', &pairs);
+
+ JDWP::JdwpOptions jdwp_options;
+ std::stringstream error_stream;
+
+ for (const std::string& jdwp_option : pairs) {
+ std::string::size_type equals_pos = jdwp_option.find('=');
+ if (equals_pos == std::string::npos) {
+ return Result::Failure(s +
+ "Can't parse JDWP option '" + jdwp_option + "' in '" + options + "'");
+ }
+
+ if (!ParseJdwpOption(jdwp_option.substr(0, equals_pos),
+ jdwp_option.substr(equals_pos + 1),
+ error_stream,
+ &jdwp_options)) {
+ return Result::Failure(error_stream.str());
+ }
+ }
+
+ if (jdwp_options.transport == JDWP::kJdwpTransportUnknown) {
+ return Result::Failure(s + "Must specify JDWP transport: " + options);
+ }
+ if (!jdwp_options.server && (jdwp_options.host.empty() || jdwp_options.port == 0)) {
+ return Result::Failure(s + "Must specify JDWP host and port when server=n: " + options);
+ }
+
+ return Result::Success(std::move(jdwp_options));
+ }
+
+ bool ParseJdwpOption(const std::string& name, const std::string& value,
+ std::ostream& error_stream,
+ JDWP::JdwpOptions* jdwp_options) {
+ if (name == "transport") {
+ if (value == "dt_socket") {
+ jdwp_options->transport = JDWP::kJdwpTransportSocket;
+ } else if (value == "dt_android_adb") {
+ jdwp_options->transport = JDWP::kJdwpTransportAndroidAdb;
+ } else {
+ error_stream << "JDWP transport not supported: " << value;
+ return false;
+ }
+ } else if (name == "server") {
+ if (value == "n") {
+ jdwp_options->server = false;
+ } else if (value == "y") {
+ jdwp_options->server = true;
+ } else {
+ error_stream << "JDWP option 'server' must be 'y' or 'n'";
+ return false;
+ }
+ } else if (name == "suspend") {
+ if (value == "n") {
+ jdwp_options->suspend = false;
+ } else if (value == "y") {
+ jdwp_options->suspend = true;
+ } else {
+ error_stream << "JDWP option 'suspend' must be 'y' or 'n'";
+ return false;
+ }
+ } else if (name == "address") {
+ /* this is either <port> or <host>:<port> */
+ std::string port_string;
+ jdwp_options->host.clear();
+ std::string::size_type colon = value.find(':');
+ if (colon != std::string::npos) {
+ jdwp_options->host = value.substr(0, colon);
+ port_string = value.substr(colon + 1);
+ } else {
+ port_string = value;
+ }
+ if (port_string.empty()) {
+ error_stream << "JDWP address missing port: " << value;
+ return false;
+ }
+ char* end;
+ uint64_t port = strtoul(port_string.c_str(), &end, 10);
+ if (*end != '\0' || port > 0xffff) {
+ error_stream << "JDWP address has junk in port field: " << value;
+ return false;
+ }
+ jdwp_options->port = port;
+ } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") {
+ /* valid but unsupported */
+ LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'";
+ } else {
+ LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'";
+ }
+
+ return true;
+ }
+
+ static const char* Name() { return "JdwpOptions"; }
+};
+
+template <size_t Divisor>
+struct CmdlineType<Memory<Divisor>> : CmdlineTypeParser<Memory<Divisor>> {
+ using typename CmdlineTypeParser<Memory<Divisor>>::Result;
+
+ Result Parse(const std::string arg) {
+ CMDLINE_DEBUG_LOG << "Parsing memory: " << arg << std::endl;
+ size_t val = ParseMemoryOption(arg.c_str(), Divisor);
+ CMDLINE_DEBUG_LOG << "Memory parsed to size_t value: " << val << std::endl;
+
+ if (val == 0) {
+ return Result::Failure(std::string("not a valid memory value, or not divisible by ")
+ + std::to_string(Divisor));
+ }
+
+ return Result::Success(Memory<Divisor>(val));
+ }
+
+ // Parse a string of the form /[0-9]+[kKmMgG]?/, which is used to specify
+ // memory sizes. [kK] indicates kilobytes, [mM] megabytes, and
+ // [gG] gigabytes.
+ //
+ // "s" should point just past the "-Xm?" part of the string.
+ // "div" specifies a divisor, e.g. 1024 if the value must be a multiple
+ // of 1024.
+ //
+ // The spec says the -Xmx and -Xms options must be multiples of 1024. It
+ // doesn't say anything about -Xss.
+ //
+ // Returns 0 (a useless size) if "s" is malformed or specifies a low or
+ // non-evenly-divisible value.
+ //
+ static size_t ParseMemoryOption(const char* s, size_t div) {
+ // strtoul accepts a leading [+-], which we don't want,
+ // so make sure our string starts with a decimal digit.
+ if (isdigit(*s)) {
+ char* s2;
+ size_t val = strtoul(s, &s2, 10);
+ if (s2 != s) {
+ // s2 should be pointing just after the number.
+ // If this is the end of the string, the user
+ // has specified a number of bytes. Otherwise,
+ // there should be exactly one more character
+ // that specifies a multiplier.
+ if (*s2 != '\0') {
+ // The remainder of the string is either a single multiplier
+ // character, or nothing to indicate that the value is in
+ // bytes.
+ char c = *s2++;
+ if (*s2 == '\0') {
+ size_t mul;
+ if (c == '\0') {
+ mul = 1;
+ } else if (c == 'k' || c == 'K') {
+ mul = KB;
+ } else if (c == 'm' || c == 'M') {
+ mul = MB;
+ } else if (c == 'g' || c == 'G') {
+ mul = GB;
+ } else {
+ // Unknown multiplier character.
+ return 0;
+ }
+
+ if (val <= std::numeric_limits<size_t>::max() / mul) {
+ val *= mul;
+ } else {
+ // Clamp to a multiple of 1024.
+ val = std::numeric_limits<size_t>::max() & ~(1024-1);
+ }
+ } else {
+ // There's more than one character after the numeric part.
+ return 0;
+ }
+ }
+ // The man page says that a -Xm value must be a multiple of 1024.
+ if (val % div == 0) {
+ return val;
+ }
+ }
+ }
+ return 0;
+ }
+
+ static const char* Name() { return Memory<Divisor>::Name(); }
+};
+
+template <>
+struct CmdlineType<double> : CmdlineTypeParser<double> {
+ Result Parse(const std::string& str) {
+ char* end = nullptr;
+ errno = 0;
+ double value = strtod(str.c_str(), &end);
+
+ if (*end != '\0') {
+ return Result::Failure("Failed to parse double from " + str);
+ }
+ if (errno == ERANGE) {
+ return Result::OutOfRange(
+ "Failed to parse double from " + str + "; overflow/underflow occurred");
+ }
+
+ return Result::Success(value);
+ }
+
+ static const char* Name() { return "double"; }
+};
+
+template <>
+struct CmdlineType<unsigned int> : CmdlineTypeParser<unsigned int> {
+ Result Parse(const std::string& str) {
+ const char* begin = str.c_str();
+ char* end;
+
+ // Parse into a larger type (long long) because we can't use strtoul
+ // since it silently converts negative values into unsigned long and doesn't set errno.
+ errno = 0;
+ long long int result = strtoll(begin, &end, 10); // NOLINT [runtime/int] [4]
+ if (begin == end || *end != '\0' || errno == EINVAL) {
+ return Result::Failure("Failed to parse integer from " + str);
+ } else if ((errno == ERANGE) || // NOLINT [runtime/int] [4]
+ result < std::numeric_limits<int>::min()
+ || result > std::numeric_limits<unsigned int>::max() || result < 0) {
+ return Result::OutOfRange(
+ "Failed to parse integer from " + str + "; out of unsigned int range");
+ }
+
+ return Result::Success(static_cast<unsigned int>(result));
+ }
+
+ static const char* Name() { return "unsigned integer"; }
+};
+
+// Lightweight nanosecond value type. Allows parser to convert user-input from milliseconds
+// to nanoseconds automatically after parsing.
+//
+// All implicit conversion from uint64_t uses nanoseconds.
+struct MillisecondsToNanoseconds {
+ // Create from nanoseconds.
+ MillisecondsToNanoseconds(uint64_t nanoseconds) : nanoseconds_(nanoseconds) { // NOLINT [runtime/explicit] [5]
+ }
+
+ // Create from milliseconds.
+ static MillisecondsToNanoseconds FromMilliseconds(unsigned int milliseconds) {
+ return MillisecondsToNanoseconds(MsToNs(milliseconds));
+ }
+
+ // Get the underlying nanoseconds value.
+ uint64_t GetNanoseconds() const {
+ return nanoseconds_;
+ }
+
+ // Get the milliseconds value [via a conversion]. Loss of precision will occur.
+ uint64_t GetMilliseconds() const {
+ return NsToMs(nanoseconds_);
+ }
+
+ // Get the underlying nanoseconds value.
+ operator uint64_t() const {
+ return GetNanoseconds();
+ }
+
+ // Default constructors/copy-constructors.
+ MillisecondsToNanoseconds() : nanoseconds_(0ul) {}
+ MillisecondsToNanoseconds(const MillisecondsToNanoseconds& rhs) = default;
+ MillisecondsToNanoseconds(MillisecondsToNanoseconds&& rhs) = default;
+
+ private:
+ uint64_t nanoseconds_;
+};
+
+template <>
+struct CmdlineType<MillisecondsToNanoseconds> : CmdlineTypeParser<MillisecondsToNanoseconds> {
+ Result Parse(const std::string& str) {
+ CmdlineType<unsigned int> uint_parser;
+ CmdlineParseResult<unsigned int> res = uint_parser.Parse(str);
+
+ if (res.IsSuccess()) {
+ return Result::Success(MillisecondsToNanoseconds::FromMilliseconds(res.GetValue()));
+ } else {
+ return Result::CastError(res);
+ }
+ }
+
+ static const char* Name() { return "MillisecondsToNanoseconds"; }
+};
+
+template <>
+struct CmdlineType<std::string> : CmdlineTypeParser<std::string> {
+ Result Parse(const std::string& args) {
+ return Result::Success(args);
+ }
+
+ Result ParseAndAppend(const std::string& args,
+ std::string& existing_value) {
+ if (existing_value.empty()) {
+ existing_value = args;
+ } else {
+ existing_value += ' ';
+ existing_value += args;
+ }
+ return Result::SuccessNoValue();
+ }
+};
+
+template <>
+struct CmdlineType<std::vector<std::string>> : CmdlineTypeParser<std::vector<std::string>> {
+ Result Parse(const std::string& args) {
+ assert(false && "Use AppendValues() for a string vector type");
+ return Result::Failure("Unconditional failure: string vector must be appended: " + args);
+ }
+
+ Result ParseAndAppend(const std::string& args,
+ std::vector<std::string>& existing_value) {
+ existing_value.push_back(args);
+ return Result::SuccessNoValue();
+ }
+
+ static const char* Name() { return "std::vector<std::string>"; }
+};
+
+template <char Separator>
+struct ParseStringList {
+ explicit ParseStringList(std::vector<std::string>&& list) : list_(list) {}
+
+ operator std::vector<std::string>() const {
+ return list_;
+ }
+
+ operator std::vector<std::string>&&() && {
+ return std::move(list_);
+ }
+
+ size_t Size() const {
+ return list_.size();
+ }
+
+ std::string Join() const {
+ return art::Join(list_, Separator);
+ }
+
+ static ParseStringList<Separator> Split(const std::string& str) {
+ std::vector<std::string> list;
+ art::Split(str, Separator, &list);
+ return ParseStringList<Separator>(std::move(list));
+ }
+
+ ParseStringList() = default;
+ ParseStringList(const ParseStringList& rhs) = default;
+ ParseStringList(ParseStringList&& rhs) = default;
+
+ private:
+ std::vector<std::string> list_;
+};
+
+template <char Separator>
+struct CmdlineType<ParseStringList<Separator>> : CmdlineTypeParser<ParseStringList<Separator>> {
+ using Result = CmdlineParseResult<ParseStringList<Separator>>;
+
+ Result Parse(const std::string& args) {
+ return Result::Success(ParseStringList<Separator>::Split(args));
+ }
+
+ static const char* Name() { return "ParseStringList<Separator>"; }
+};
+
+static gc::CollectorType ParseCollectorType(const std::string& option) {
+ if (option == "MS" || option == "nonconcurrent") {
+ return gc::kCollectorTypeMS;
+ } else if (option == "CMS" || option == "concurrent") {
+ return gc::kCollectorTypeCMS;
+ } else if (option == "SS") {
+ return gc::kCollectorTypeSS;
+ } else if (option == "GSS") {
+ return gc::kCollectorTypeGSS;
+ } else if (option == "CC") {
+ return gc::kCollectorTypeCC;
+ } else if (option == "MC") {
+ return gc::kCollectorTypeMC;
+ } else {
+ return gc::kCollectorTypeNone;
+ }
+}
+
+struct XGcOption {
+ // These defaults are used when the command line arguments for -Xgc:
+ // are either omitted completely or partially.
+ gc::CollectorType collector_type_ = kUseReadBarrier ?
+ // If RB is enabled (currently a build-time decision),
+ // use CC as the default GC.
+ gc::kCollectorTypeCC :
+ gc::kCollectorTypeDefault;
+ bool verify_pre_gc_heap_ = false;
+ bool verify_pre_sweeping_heap_ = kIsDebugBuild;
+ bool verify_post_gc_heap_ = false;
+ bool verify_pre_gc_rosalloc_ = kIsDebugBuild;
+ bool verify_pre_sweeping_rosalloc_ = false;
+ bool verify_post_gc_rosalloc_ = false;
+};
+
+template <>
+struct CmdlineType<XGcOption> : CmdlineTypeParser<XGcOption> {
+ Result Parse(const std::string& option) { // -Xgc: already stripped
+ XGcOption xgc{}; // NOLINT [readability/braces] [4]
+
+ std::vector<std::string> gc_options;
+ Split(option, ',', &gc_options);
+ for (const std::string& gc_option : gc_options) {
+ gc::CollectorType collector_type = ParseCollectorType(gc_option);
+ if (collector_type != gc::kCollectorTypeNone) {
+ xgc.collector_type_ = collector_type;
+ } else if (gc_option == "preverify") {
+ xgc.verify_pre_gc_heap_ = true;
+ } else if (gc_option == "nopreverify") {
+ xgc.verify_pre_gc_heap_ = false;
+ } else if (gc_option == "presweepingverify") {
+ xgc.verify_pre_sweeping_heap_ = true;
+ } else if (gc_option == "nopresweepingverify") {
+ xgc.verify_pre_sweeping_heap_ = false;
+ } else if (gc_option == "postverify") {
+ xgc.verify_post_gc_heap_ = true;
+ } else if (gc_option == "nopostverify") {
+ xgc.verify_post_gc_heap_ = false;
+ } else if (gc_option == "preverify_rosalloc") {
+ xgc.verify_pre_gc_rosalloc_ = true;
+ } else if (gc_option == "nopreverify_rosalloc") {
+ xgc.verify_pre_gc_rosalloc_ = false;
+ } else if (gc_option == "presweepingverify_rosalloc") {
+ xgc.verify_pre_sweeping_rosalloc_ = true;
+ } else if (gc_option == "nopresweepingverify_rosalloc") {
+ xgc.verify_pre_sweeping_rosalloc_ = false;
+ } else if (gc_option == "postverify_rosalloc") {
+ xgc.verify_post_gc_rosalloc_ = true;
+ } else if (gc_option == "nopostverify_rosalloc") {
+ xgc.verify_post_gc_rosalloc_ = false;
+ } else if ((gc_option == "precise") ||
+ (gc_option == "noprecise") ||
+ (gc_option == "verifycardtable") ||
+ (gc_option == "noverifycardtable")) {
+ // Ignored for backwards compatibility.
+ } else {
+ return Result::Usage(std::string("Unknown -Xgc option ") + gc_option);
+ }
+ }
+
+ return Result::Success(std::move(xgc));
+ }
+
+ static const char* Name() { return "XgcOption"; }
+};
+
+struct BackgroundGcOption {
+ // If background_collector_type_ is kCollectorTypeNone, it defaults to the
+ // XGcOption::collector_type_ after parsing options. If you set this to
+ // kCollectorTypeHSpaceCompact then we will do an hspace compaction when
+ // we transition to background instead of a normal collector transition.
+ gc::CollectorType background_collector_type_;
+
+ BackgroundGcOption(gc::CollectorType background_collector_type) // NOLINT [runtime/explicit] [5]
+ : background_collector_type_(background_collector_type) {}
+ BackgroundGcOption()
+ : background_collector_type_(gc::kCollectorTypeNone) {
+
+ if (kUseReadBarrier) {
+ background_collector_type_ = gc::kCollectorTypeCC; // Disable background compaction for CC.
+ }
+ }
+
+ operator gc::CollectorType() const { return background_collector_type_; }
+};
+
+template<>
+struct CmdlineType<BackgroundGcOption>
+ : CmdlineTypeParser<BackgroundGcOption>, private BackgroundGcOption {
+ Result Parse(const std::string& substring) {
+ // Special handling for HSpaceCompact since this is only valid as a background GC type.
+ if (substring == "HSpaceCompact") {
+ background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact;
+ } else {
+ gc::CollectorType collector_type = ParseCollectorType(substring);
+ if (collector_type != gc::kCollectorTypeNone) {
+ background_collector_type_ = collector_type;
+ } else {
+ return Result::Failure();
+ }
+ }
+
+ BackgroundGcOption res = *this;
+ return Result::Success(res);
+ }
+
+ static const char* Name() { return "BackgroundGcOption"; }
+};
+
+template <>
+struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> {
+ Result Parse(const std::string& options) {
+ LogVerbosity log_verbosity = LogVerbosity();
+
+ std::vector<std::string> verbose_options;
+ Split(options, ',', &verbose_options);
+ for (size_t j = 0; j < verbose_options.size(); ++j) {
+ if (verbose_options[j] == "class") {
+ log_verbosity.class_linker = true;
+ } else if (verbose_options[j] == "compiler") {
+ log_verbosity.compiler = true;
+ } else if (verbose_options[j] == "gc") {
+ log_verbosity.gc = true;
+ } else if (verbose_options[j] == "heap") {
+ log_verbosity.heap = true;
+ } else if (verbose_options[j] == "jdwp") {
+ log_verbosity.jdwp = true;
+ } else if (verbose_options[j] == "jni") {
+ log_verbosity.jni = true;
+ } else if (verbose_options[j] == "monitor") {
+ log_verbosity.monitor = true;
+ } else if (verbose_options[j] == "profiler") {
+ log_verbosity.profiler = true;
+ } else if (verbose_options[j] == "signals") {
+ log_verbosity.signals = true;
+ } else if (verbose_options[j] == "startup") {
+ log_verbosity.startup = true;
+ } else if (verbose_options[j] == "third-party-jni") {
+ log_verbosity.third_party_jni = true;
+ } else if (verbose_options[j] == "threads") {
+ log_verbosity.threads = true;
+ } else if (verbose_options[j] == "verifier") {
+ log_verbosity.verifier = true;
+ } else {
+ return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]);
+ }
+ }
+
+ return Result::Success(log_verbosity);
+ }
+
+ static const char* Name() { return "LogVerbosity"; }
+};
+
+// TODO: Replace with art::ProfilerOptions for the real thing.
+struct TestProfilerOptions {
+ // Whether or not the applications should be profiled.
+ bool enabled_;
+ // Destination file name where the profiling data will be saved into.
+ std::string output_file_name_;
+ // Generate profile every n seconds.
+ uint32_t period_s_;
+ // Run profile for n seconds.
+ uint32_t duration_s_;
+ // Microseconds between samples.
+ uint32_t interval_us_;
+ // Coefficient to exponential backoff.
+ double backoff_coefficient_;
+ // Whether the profile should start upon app startup or be delayed by some random offset.
+ bool start_immediately_;
+ // Top K% of samples that are considered relevant when deciding if the app should be recompiled.
+ double top_k_threshold_;
+ // How much the top K% samples needs to change in order for the app to be recompiled.
+ double top_k_change_threshold_;
+ // The type of profile data dumped to the disk.
+ ProfileDataType profile_type_;
+ // The max depth of the stack collected by the profiler
+ uint32_t max_stack_depth_;
+
+ TestProfilerOptions() :
+ enabled_(false),
+ output_file_name_(),
+ period_s_(0),
+ duration_s_(0),
+ interval_us_(0),
+ backoff_coefficient_(0),
+ start_immediately_(0),
+ top_k_threshold_(0),
+ top_k_change_threshold_(0),
+ profile_type_(ProfileDataType::kProfilerMethod),
+ max_stack_depth_(0) {
+ }
+
+ TestProfilerOptions(const TestProfilerOptions& other) = default;
+ TestProfilerOptions(TestProfilerOptions&& other) = default;
+};
+
+static inline std::ostream& operator<<(std::ostream& stream, const TestProfilerOptions& options) {
+ stream << "TestProfilerOptions {" << std::endl;
+
+#define PRINT_TO_STREAM(field) \
+ stream << #field << ": '" << options.field << "'" << std::endl;
+
+ PRINT_TO_STREAM(enabled_);
+ PRINT_TO_STREAM(output_file_name_);
+ PRINT_TO_STREAM(period_s_);
+ PRINT_TO_STREAM(duration_s_);
+ PRINT_TO_STREAM(interval_us_);
+ PRINT_TO_STREAM(backoff_coefficient_);
+ PRINT_TO_STREAM(start_immediately_);
+ PRINT_TO_STREAM(top_k_threshold_);
+ PRINT_TO_STREAM(top_k_change_threshold_);
+ PRINT_TO_STREAM(profile_type_);
+ PRINT_TO_STREAM(max_stack_depth_);
+
+ stream << "}";
+
+ return stream;
+#undef PRINT_TO_STREAM
+}
+
+template <>
+struct CmdlineType<TestProfilerOptions> : CmdlineTypeParser<TestProfilerOptions> {
+ using Result = CmdlineParseResult<TestProfilerOptions>;
+
+ private:
+ using StringResult = CmdlineParseResult<std::string>;
+ using DoubleResult = CmdlineParseResult<double>;
+
+ template <typename T>
+ static Result ParseInto(TestProfilerOptions& options,
+ T TestProfilerOptions::*pField,
+ CmdlineParseResult<T>&& result) {
+ assert(pField != nullptr);
+
+ if (result.IsSuccess()) {
+ options.*pField = result.ReleaseValue();
+ return Result::SuccessNoValue();
+ }
+
+ return Result::CastError(result);
+ }
+
+ template <typename T>
+ static Result ParseIntoRangeCheck(TestProfilerOptions& options,
+ T TestProfilerOptions::*pField,
+ CmdlineParseResult<T>&& result,
+ T min,
+ T max) {
+ if (result.IsSuccess()) {
+ const T& value = result.GetValue();
+
+ if (value < min || value > max) {
+ CmdlineParseResult<T> out_of_range = CmdlineParseResult<T>::OutOfRange(value, min, max);
+ return Result::CastError(out_of_range);
+ }
+ }
+
+ return ParseInto(options, pField, std::forward<CmdlineParseResult<T>>(result));
+ }
+
+ static StringResult ParseStringAfterChar(const std::string& s, char c) {
+ std::string parsed_value;
+
+ std::string::size_type colon = s.find(c);
+ if (colon == std::string::npos) {
+ return StringResult::Usage(std::string() + "Missing char " + c + " in option " + s);
+ }
+ // Add one to remove the char we were trimming until.
+ parsed_value = s.substr(colon + 1);
+ return StringResult::Success(parsed_value);
+ }
+
+ static std::string RemovePrefix(const std::string& source) {
+ size_t prefix_idx = source.find(":");
+
+ if (prefix_idx == std::string::npos) {
+ return "";
+ }
+
+ return source.substr(prefix_idx + 1);
+ }
+
+ public:
+ Result ParseAndAppend(const std::string& option, TestProfilerOptions& existing) {
+ // Special case which doesn't include a wildcard argument definition.
+ // We pass-it through as-is.
+ if (option == "-Xenable-profiler") {
+ existing.enabled_ = true;
+ return Result::SuccessNoValue();
+ }
+
+ // The rest of these options are always the wildcard from '-Xprofile-*'
+ std::string suffix = RemovePrefix(option);
+
+ if (StartsWith(option, "filename:")) {
+ CmdlineType<std::string> type_parser;
+
+ return ParseInto(existing,
+ &TestProfilerOptions::output_file_name_,
+ type_parser.Parse(suffix));
+ } else if (StartsWith(option, "period:")) {
+ CmdlineType<unsigned int> type_parser;
+
+ return ParseInto(existing,
+ &TestProfilerOptions::period_s_,
+ type_parser.Parse(suffix));
+ } else if (StartsWith(option, "duration:")) {
+ CmdlineType<unsigned int> type_parser;
+
+ return ParseInto(existing,
+ &TestProfilerOptions::duration_s_,
+ type_parser.Parse(suffix));
+ } else if (StartsWith(option, "interval:")) {
+ CmdlineType<unsigned int> type_parser;
+
+ return ParseInto(existing,
+ &TestProfilerOptions::interval_us_,
+ type_parser.Parse(suffix));
+ } else if (StartsWith(option, "backoff:")) {
+ CmdlineType<double> type_parser;
+
+ return ParseIntoRangeCheck(existing,
+ &TestProfilerOptions::backoff_coefficient_,
+ type_parser.Parse(suffix),
+ 1.0,
+ 10.0);
+
+ } else if (option == "start-immediately") {
+ existing.start_immediately_ = true;
+ return Result::SuccessNoValue();
+ } else if (StartsWith(option, "top-k-threshold:")) {
+ CmdlineType<double> type_parser;
+
+ return ParseIntoRangeCheck(existing,
+ &TestProfilerOptions::top_k_threshold_,
+ type_parser.Parse(suffix),
+ 0.0,
+ 100.0);
+ } else if (StartsWith(option, "top-k-change-threshold:")) {
+ CmdlineType<double> type_parser;
+
+ return ParseIntoRangeCheck(existing,
+ &TestProfilerOptions::top_k_change_threshold_,
+ type_parser.Parse(suffix),
+ 0.0,
+ 100.0);
+ } else if (option == "type:method") {
+ existing.profile_type_ = kProfilerMethod;
+ return Result::SuccessNoValue();
+ } else if (option == "type:stack") {
+ existing.profile_type_ = kProfilerBoundedStack;
+ return Result::SuccessNoValue();
+ } else if (StartsWith(option, "max-stack-depth:")) {
+ CmdlineType<unsigned int> type_parser;
+
+ return ParseInto(existing,
+ &TestProfilerOptions::max_stack_depth_,
+ type_parser.Parse(suffix));
+ } else {
+ return Result::Failure(std::string("Invalid suboption '") + option + "'");
+ }
+ }
+
+ static const char* Name() { return "TestProfilerOptions"; }
+ static constexpr bool kCanParseBlankless = true;
+};
+
+
+} // namespace art
+#endif // ART_CMDLINE_CMDLINE_TYPES_H_
diff --git a/cmdline/detail/cmdline_debug_detail.h b/cmdline/detail/cmdline_debug_detail.h
new file mode 100644
index 0000000..79028f4
--- /dev/null
+++ b/cmdline/detail/cmdline_debug_detail.h
@@ -0,0 +1,40 @@
+/*
+ * 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_CMDLINE_DETAIL_CMDLINE_DEBUG_DETAIL_H_
+#define ART_CMDLINE_DETAIL_CMDLINE_DEBUG_DETAIL_H_
+
+#include <iostream>
+#ifndef CMDLINE_NDEBUG
+#define CMDLINE_DEBUG_LOG std::cerr
+#else
+#define CMDLINE_DEBUG_LOG ::art::detail::debug_log_ignore()
+#endif
+
+namespace art {
+ // Implementation details for some template querying. Don't look inside if you hate templates.
+ namespace detail {
+ struct debug_log_ignore {
+ // Ignore most of the normal operator<< usage.
+ template <typename T>
+ debug_log_ignore& operator<<(const T&) { return *this; }
+ // Ignore std::endl and the like.
+ debug_log_ignore& operator<<(std::ostream& (*)(std::ostream&) ) { return *this; }
+ };
+ } // namespace detail // NOLINT [readability/namespace] [5]
+} // namespace art
+
+#endif // ART_CMDLINE_DETAIL_CMDLINE_DEBUG_DETAIL_H_
diff --git a/cmdline/detail/cmdline_parse_argument_detail.h b/cmdline/detail/cmdline_parse_argument_detail.h
new file mode 100644
index 0000000..81ef36b
--- /dev/null
+++ b/cmdline/detail/cmdline_parse_argument_detail.h
@@ -0,0 +1,503 @@
+/*
+ * 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_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
+#define ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
+
+#include <type_traits>
+#include <assert.h>
+#include <functional>
+#include <vector>
+#include <algorithm>
+#include <numeric>
+#include <memory>
+
+#include "cmdline/cmdline_parse_result.h"
+#include "cmdline/token_range.h"
+#include "cmdline/unit.h"
+#include "cmdline/cmdline_types.h"
+
+namespace art {
+ // Implementation details for the parser. Do not look inside if you hate templates.
+ namespace detail {
+ // A non-templated base class for argument parsers. Used by the general parser
+ // to parse arguments, without needing to know the argument type at compile time.
+ //
+ // This is an application of the type erasure idiom.
+ struct CmdlineParseArgumentAny {
+ virtual ~CmdlineParseArgumentAny() {}
+
+ // Attempt to parse this argument starting at arguments[position].
+ // If the parsing succeeds, the parsed value will be saved as a side-effect.
+ //
+ // In most situations, the parsing will not match by returning kUnknown. In this case,
+ // no tokens were consumed and the position variable will not be updated.
+ //
+ // At other times, parsing may fail due to validation but the initial token was still matched
+ // (for example an out of range value, or passing in a string where an int was expected).
+ // In this case the tokens are still consumed, and the position variable will get incremented
+ // by all the consumed tokens.
+ //
+ // The # of tokens consumed by the parse attempt will be set as an out-parameter into
+ // consumed_tokens. The parser should skip this many tokens before parsing the next
+ // argument.
+ virtual CmdlineResult ParseArgument(const TokenRange& arguments, size_t* consumed_tokens) = 0;
+ // How many tokens should be taken off argv for parsing this argument.
+ // For example "--help" is just 1, "-compiler-option _" would be 2 (since there's a space).
+ //
+ // A [min,max] range is returned to represent argument definitions with multiple
+ // value tokens. (e.g. {"-h", "-h " } would return [1,2]).
+ virtual std::pair<size_t, size_t> GetNumTokens() const = 0;
+ // Get the run-time typename of the argument type.
+ virtual const char* GetTypeName() const = 0;
+ // Try to do a close match, returning how many tokens were matched against this argument
+ // definition. More tokens is better.
+ //
+ // Do a quick match token-by-token, and see if they match.
+ // Any tokens with a wildcard in them are only matched up until the wildcard.
+ // If this is true, then the wildcard matching later on can still fail, so this is not
+ // a guarantee that the argument is correct, it's more of a strong hint that the
+ // user-provided input *probably* was trying to match this argument.
+ //
+ // Returns how many tokens were either matched (or ignored because there was a
+ // wildcard present). 0 means no match. If the Size() tokens are returned.
+ virtual size_t MaybeMatches(const TokenRange& tokens) = 0;
+ };
+
+ template <typename T>
+ using EnableIfNumeric = std::enable_if<std::is_arithmetic<T>::value>;
+
+ template <typename T>
+ using DisableIfNumeric = std::enable_if<!std::is_arithmetic<T>::value>;
+
+ // Argument definition information, created by an ArgumentBuilder and an UntypedArgumentBuilder.
+ template <typename TArg>
+ struct CmdlineParserArgumentInfo {
+ // This version will only be used if TArg is arithmetic and thus has the <= operators.
+ template <typename T = TArg> // Necessary to get SFINAE to kick in.
+ bool CheckRange(const TArg& value, typename EnableIfNumeric<T>::type* = 0) {
+ if (has_range_) {
+ return min_ <= value && value <= max_;
+ }
+ return true;
+ }
+
+ // This version will be used at other times when TArg is not arithmetic.
+ template <typename T = TArg>
+ bool CheckRange(const TArg&, typename DisableIfNumeric<T>::type* = 0) {
+ assert(!has_range_);
+ return true;
+ }
+
+ // Do a quick match token-by-token, and see if they match.
+ // Any tokens with a wildcard in them only match the prefix up until the wildcard.
+ //
+ // If this is true, then the wildcard matching later on can still fail, so this is not
+ // a guarantee that the argument is correct, it's more of a strong hint that the
+ // user-provided input *probably* was trying to match this argument.
+ size_t MaybeMatches(TokenRange token_list) const {
+ auto best_match = FindClosestMatch(token_list);
+
+ return best_match.second;
+ }
+
+ // Attempt to find the closest match (see MaybeMatches).
+ //
+ // Returns the token range that was the closest match and the # of tokens that
+ // this range was matched up until.
+ std::pair<const TokenRange*, size_t> FindClosestMatch(TokenRange token_list) const {
+ const TokenRange* best_match_ptr = nullptr;
+
+ size_t best_match = 0;
+ for (auto&& token_range : tokenized_names_) {
+ size_t this_match = token_range.MaybeMatches(token_list, std::string("_"));
+
+ if (this_match > best_match) {
+ best_match_ptr = &token_range;
+ best_match = this_match;
+ }
+ }
+
+ return std::make_pair(best_match_ptr, best_match);
+ }
+
+ // Mark the argument definition as completed, do not mutate the object anymore after this
+ // call is done.
+ //
+ // Performs several sanity checks and token calculations.
+ void CompleteArgument() {
+ assert(names_.size() >= 1);
+ assert(!is_completed_);
+
+ is_completed_ = true;
+
+ size_t blank_count = 0;
+ size_t token_count = 0;
+
+ size_t global_blank_count = 0;
+ size_t global_token_count = 0;
+ for (auto&& name : names_) {
+ std::string s(name);
+
+ size_t local_blank_count = std::count(s.begin(), s.end(), '_');
+ size_t local_token_count = std::count(s.begin(), s.end(), ' ');
+
+ if (global_blank_count != 0) {
+ assert(local_blank_count == global_blank_count
+ && "Every argument descriptor string must have same amount of blanks (_)");
+ }
+
+ if (local_blank_count != 0) {
+ global_blank_count = local_blank_count;
+ blank_count++;
+
+ assert(local_blank_count == 1 && "More than one blank is not supported");
+ assert(s.back() == '_' && "The blank character must only be at the end of the string");
+ }
+
+ if (global_token_count != 0) {
+ assert(local_token_count == global_token_count
+ && "Every argument descriptor string must have same amount of tokens (spaces)");
+ }
+
+ if (local_token_count != 0) {
+ global_token_count = local_token_count;
+ token_count++;
+ }
+
+ // Tokenize every name, turning it from a string to a token list.
+ tokenized_names_.clear();
+ for (auto&& name1 : names_) {
+ // Split along ' ' only, removing any duplicated spaces.
+ tokenized_names_.push_back(
+ TokenRange::Split(name1, {' '}).RemoveToken(" "));
+ }
+
+ // remove the _ character from each of the token ranges
+ // we will often end up with an empty token (i.e. ["-XX", "_"] -> ["-XX", ""]
+ // and this is OK because we still need an empty token to simplify
+ // range comparisons
+ simple_names_.clear();
+
+ for (auto&& tokenized_name : tokenized_names_) {
+ simple_names_.push_back(tokenized_name.RemoveCharacter('_'));
+ }
+ }
+
+ if (token_count != 0) {
+ assert(("Every argument descriptor string must have equal amount of tokens (spaces)" &&
+ token_count == names_.size()));
+ }
+
+ if (blank_count != 0) {
+ assert(("Every argument descriptor string must have an equal amount of blanks (_)" &&
+ blank_count == names_.size()));
+ }
+
+ using_blanks_ = blank_count > 0;
+ {
+ size_t smallest_name_token_range_size =
+ std::accumulate(tokenized_names_.begin(), tokenized_names_.end(), ~(0u),
+ [](size_t min, const TokenRange& cur) {
+ return std::min(min, cur.Size());
+ });
+ size_t largest_name_token_range_size =
+ std::accumulate(tokenized_names_.begin(), tokenized_names_.end(), 0u,
+ [](size_t max, const TokenRange& cur) {
+ return std::max(max, cur.Size());
+ });
+
+ token_range_size_ = std::make_pair(smallest_name_token_range_size,
+ largest_name_token_range_size);
+ }
+
+ if (has_value_list_) {
+ assert(names_.size() == value_list_.size()
+ && "Number of arg descriptors must match number of values");
+ assert(!has_value_map_);
+ }
+ if (has_value_map_) {
+ if (!using_blanks_) {
+ assert(names_.size() == value_map_.size() &&
+ "Since no blanks were specified, each arg is mapped directly into a mapped "
+ "value without parsing; sizes must match");
+ }
+
+ assert(!has_value_list_);
+ }
+
+ if (!using_blanks_ && !CmdlineType<TArg>::kCanParseBlankless) {
+ assert((has_value_map_ || has_value_list_) &&
+ "Arguments without a blank (_) must provide either a value map or a value list");
+ }
+
+ TypedCheck();
+ }
+
+ // List of aliases for a single argument definition, e.g. {"-Xdex2oat", "-Xnodex2oat"}.
+ std::vector<const char*> names_;
+ // Is there at least 1 wildcard '_' in the argument definition?
+ bool using_blanks_ = false;
+ // [min, max] token counts in each arg def
+ std::pair<size_t, size_t> token_range_size_;
+
+ // contains all the names in a tokenized form, i.e. as a space-delimited list
+ std::vector<TokenRange> tokenized_names_;
+
+ // contains the tokenized names, but with the _ character stripped
+ std::vector<TokenRange> simple_names_;
+
+ // For argument definitions created with '.AppendValues()'
+ // Meaning that parsing should mutate the existing value in-place if possible.
+ bool appending_values_ = false;
+
+ // For argument definitions created with '.WithRange(min, max)'
+ bool has_range_ = false;
+ TArg min_;
+ TArg max_;
+
+ // For argument definitions created with '.WithValueMap'
+ bool has_value_map_ = false;
+ std::vector<std::pair<const char*, TArg>> value_map_;
+
+ // For argument definitions created with '.WithValues'
+ bool has_value_list_ = false;
+ std::vector<TArg> value_list_;
+
+ // Make sure there's a default constructor.
+ CmdlineParserArgumentInfo() = default;
+
+ // Ensure there's a default move constructor.
+ CmdlineParserArgumentInfo(CmdlineParserArgumentInfo&&) = default;
+
+ private:
+ // Perform type-specific checks at runtime.
+ template <typename T = TArg>
+ void TypedCheck(typename std::enable_if<std::is_same<Unit, T>::value>::type* = 0) {
+ assert(!using_blanks_ &&
+ "Blanks are not supported in Unit arguments; since a Unit has no parse-able value");
+ }
+
+ void TypedCheck() {}
+
+ bool is_completed_ = false;
+ };
+
+ // A virtual-implementation of the necessary argument information in order to
+ // be able to parse arguments.
+ template <typename TArg>
+ struct CmdlineParseArgument : CmdlineParseArgumentAny {
+ explicit CmdlineParseArgument(CmdlineParserArgumentInfo<TArg>&& argument_info,
+ std::function<void(TArg&)>&& save_argument,
+ std::function<TArg&(void)>&& load_argument)
+ : argument_info_(std::forward<decltype(argument_info)>(argument_info)),
+ save_argument_(std::forward<decltype(save_argument)>(save_argument)),
+ load_argument_(std::forward<decltype(load_argument)>(load_argument)) {
+ }
+
+ using UserTypeInfo = CmdlineType<TArg>;
+
+ virtual CmdlineResult ParseArgument(const TokenRange& arguments, size_t* consumed_tokens) {
+ assert(arguments.Size() > 0);
+ assert(consumed_tokens != nullptr);
+
+ auto closest_match_res = argument_info_.FindClosestMatch(arguments);
+ size_t best_match_size = closest_match_res.second;
+ const TokenRange* best_match_arg_def = closest_match_res.first;
+
+ if (best_match_size > arguments.Size()) {
+ // The best match has more tokens than were provided.
+ // Shouldn't happen in practice since the outer parser does this check.
+ return CmdlineResult(CmdlineResult::kUnknown, "Size mismatch");
+ }
+
+ assert(best_match_arg_def != nullptr);
+ *consumed_tokens = best_match_arg_def->Size();
+
+ if (!argument_info_.using_blanks_) {
+ return ParseArgumentSingle(arguments.Join(' '));
+ }
+
+ // Extract out the blank value from arguments
+ // e.g. for a def of "foo:_" and input "foo:bar", blank_value == "bar"
+ std::string blank_value = "";
+ size_t idx = 0;
+ for (auto&& def_token : *best_match_arg_def) {
+ auto&& arg_token = arguments[idx];
+
+ // Does this definition-token have a wildcard in it?
+ if (def_token.find('_') == std::string::npos) {
+ // No, regular token. Match 1:1 against the argument token.
+ bool token_match = def_token == arg_token;
+
+ if (!token_match) {
+ return CmdlineResult(CmdlineResult::kFailure,
+ std::string("Failed to parse ") + best_match_arg_def->GetToken(0)
+ + " at token " + std::to_string(idx));
+ }
+ } else {
+ // This is a wild-carded token.
+ TokenRange def_split_wildcards = TokenRange::Split(def_token, {'_'});
+
+ // Extract the wildcard contents out of the user-provided arg_token.
+ std::unique_ptr<TokenRange> arg_matches =
+ def_split_wildcards.MatchSubstrings(arg_token, "_");
+ if (arg_matches == nullptr) {
+ return CmdlineResult(CmdlineResult::kFailure,
+ std::string("Failed to parse ") + best_match_arg_def->GetToken(0)
+ + ", with a wildcard pattern " + def_token
+ + " at token " + std::to_string(idx));
+ }
+
+ // Get the corresponding wildcard tokens from arg_matches,
+ // and concatenate it to blank_value.
+ for (size_t sub_idx = 0;
+ sub_idx < def_split_wildcards.Size() && sub_idx < arg_matches->Size(); ++sub_idx) {
+ if (def_split_wildcards[sub_idx] == "_") {
+ blank_value += arg_matches->GetToken(sub_idx);
+ }
+ }
+ }
+
+ ++idx;
+ }
+
+ return ParseArgumentSingle(blank_value);
+ }
+
+ private:
+ virtual CmdlineResult ParseArgumentSingle(const std::string& argument) {
+ // TODO: refactor to use LookupValue for the value lists/maps
+
+ // Handle the 'WithValueMap(...)' argument definition
+ if (argument_info_.has_value_map_) {
+ for (auto&& value_pair : argument_info_.value_map_) {
+ const char* name = value_pair.first;
+
+ if (argument == name) {
+ return SaveArgument(value_pair.second);
+ }
+ }
+
+ // Error case: Fail, telling the user what the allowed values were.
+ std::vector<std::string> allowed_values;
+ for (auto&& value_pair : argument_info_.value_map_) {
+ const char* name = value_pair.first;
+ allowed_values.push_back(name);
+ }
+
+ std::string allowed_values_flat = Join(allowed_values, ',');
+ return CmdlineResult(CmdlineResult::kFailure,
+ "Argument value '" + argument + "' does not match any of known valid"
+ "values: {" + allowed_values_flat + "}");
+ }
+
+ // Handle the 'WithValues(...)' argument definition
+ if (argument_info_.has_value_list_) {
+ size_t arg_def_idx = 0;
+ for (auto&& value : argument_info_.value_list_) {
+ auto&& arg_def_token = argument_info_.names_[arg_def_idx];
+
+ if (arg_def_token == argument) {
+ return SaveArgument(value);
+ }
+ ++arg_def_idx;
+ }
+
+ assert(arg_def_idx + 1 == argument_info_.value_list_.size() &&
+ "Number of named argument definitions must match number of values defined");
+
+ // Error case: Fail, telling the user what the allowed values were.
+ std::vector<std::string> allowed_values;
+ for (auto&& arg_name : argument_info_.names_) {
+ allowed_values.push_back(arg_name);
+ }
+
+ std::string allowed_values_flat = Join(allowed_values, ',');
+ return CmdlineResult(CmdlineResult::kFailure,
+ "Argument value '" + argument + "' does not match any of known valid"
+ "values: {" + allowed_values_flat + "}");
+ }
+
+ // Handle the regular case where we parsed an unknown value from a blank.
+ UserTypeInfo type_parser;
+
+ if (argument_info_.appending_values_) {
+ TArg& existing = load_argument_();
+ CmdlineParseResult<TArg> result = type_parser.ParseAndAppend(argument, existing);
+
+ assert(!argument_info_.has_range_);
+
+ return result;
+ }
+
+ CmdlineParseResult<TArg> result = type_parser.Parse(argument);
+
+ if (result.IsSuccess()) {
+ TArg& value = result.GetValue();
+
+ // Do a range check for 'WithRange(min,max)' argument definition.
+ if (!argument_info_.CheckRange(value)) {
+ return CmdlineParseResult<TArg>::OutOfRange(
+ value, argument_info_.min_, argument_info_.max_);
+ }
+
+ return SaveArgument(value);
+ }
+
+ // Some kind of type-specific parse error. Pass the result as-is.
+ CmdlineResult raw_result = std::move(result);
+ return raw_result;
+ }
+
+ public:
+ virtual const char* GetTypeName() const {
+ // TODO: Obviate the need for each type specialization to hardcode the type name
+ return UserTypeInfo::Name();
+ }
+
+ // How many tokens should be taken off argv for parsing this argument.
+ // For example "--help" is just 1, "-compiler-option _" would be 2 (since there's a space).
+ //
+ // A [min,max] range is returned to represent argument definitions with multiple
+ // value tokens. (e.g. {"-h", "-h " } would return [1,2]).
+ virtual std::pair<size_t, size_t> GetNumTokens() const {
+ return argument_info_.token_range_size_;
+ }
+
+ // See if this token range might begin the same as the argument definition.
+ virtual size_t MaybeMatches(const TokenRange& tokens) {
+ return argument_info_.MaybeMatches(tokens);
+ }
+
+ private:
+ CmdlineResult SaveArgument(const TArg& value) {
+ assert(!argument_info_.appending_values_
+ && "If the values are being appended, then the updated parse value is "
+ "updated by-ref as a side effect and shouldn't be stored directly");
+ TArg val = value;
+ save_argument_(val);
+ return CmdlineResult(CmdlineResult::kSuccess);
+ }
+
+ CmdlineParserArgumentInfo<TArg> argument_info_;
+ std::function<void(TArg&)> save_argument_;
+ std::function<TArg&(void)> load_argument_;
+ };
+ } // namespace detail // NOLINT [readability/namespace] [5] [whitespace/comments] [2]
+} // namespace art
+
+#endif // ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
diff --git a/cmdline/detail/cmdline_parser_detail.h b/cmdline/detail/cmdline_parser_detail.h
new file mode 100644
index 0000000..9b43bb0
--- /dev/null
+++ b/cmdline/detail/cmdline_parser_detail.h
@@ -0,0 +1,128 @@
+/*
+ * 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_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_
+#define ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_
+
+#include <string>
+#include <sstream>
+#include <vector>
+
+namespace art {
+ // Implementation details for some template querying. Don't look inside if you hate templates.
+ namespace detail {
+ template <typename T>
+ typename std::remove_reference<T>::type& FakeReference();
+
+ // SupportsInsertionOperator<T, TStream>::value will evaluate to a boolean,
+ // whose value is true if the TStream class supports the << operator against T,
+ // and false otherwise.
+ template <typename T2, typename TStream2 = std::ostream>
+ struct SupportsInsertionOperator {
+ private:
+ template <typename TStream, typename T>
+ static std::true_type InsertionOperatorTest(TStream& os, const T& value,
+ std::remove_reference<decltype(os << value)>* = 0); // NOLINT [whitespace/operators] [3]
+
+ template <typename TStream, typename ... T>
+ static std::false_type InsertionOperatorTest(TStream& os, const T& ... args);
+
+ public:
+ static constexpr bool value =
+ decltype(InsertionOperatorTest(FakeReference<TStream2>(), std::declval<T2>()))::value;
+ };
+
+ template <typename TLeft, typename TRight = TLeft, bool IsFloatingPoint = false>
+ struct SupportsEqualityOperatorImpl;
+
+ template <typename TLeft, typename TRight>
+ struct SupportsEqualityOperatorImpl<TLeft, TRight, false> {
+ private:
+ template <typename TL, typename TR>
+ static std::true_type EqualityOperatorTest(const TL& left, const TR& right,
+ std::remove_reference<decltype(left == right)>* = 0); // NOLINT [whitespace/operators] [3]
+
+ template <typename TL, typename ... T>
+ static std::false_type EqualityOperatorTest(const TL& left, const T& ... args);
+
+ public:
+ static constexpr bool value =
+ decltype(EqualityOperatorTest(std::declval<TLeft>(), std::declval<TRight>()))::value;
+ };
+
+ // Partial specialization when TLeft/TRight are both floating points.
+ // This is a work-around because decltype(floatvar1 == floatvar2)
+ // will not compile with clang:
+ // error: comparing floating point with == or != is unsafe [-Werror,-Wfloat-equal]
+ template <typename TLeft, typename TRight>
+ struct SupportsEqualityOperatorImpl<TLeft, TRight, true> {
+ static constexpr bool value = true;
+ };
+
+ // SupportsEqualityOperatorImpl<T1, T2>::value will evaluate to a boolean,
+ // whose value is true if T1 can be compared against T2 with ==,
+ // and false otherwise.
+ template <typename TLeft, typename TRight = TLeft>
+ struct SupportsEqualityOperator :
+ SupportsEqualityOperatorImpl<TLeft, TRight,
+ std::is_floating_point<TLeft>::value
+ && std::is_floating_point<TRight>::value> {
+ };
+
+ // Convert any kind of type to an std::string, even if there's no
+ // serialization support for it. Unknown types get converted to an
+ // an arbitrary value.
+ //
+ // Meant for printing user-visible errors or unit test failures only.
+ template <typename T>
+ std::string ToStringAny(const T& value,
+ typename std::enable_if<
+ SupportsInsertionOperator<T>::value>::type* = 0) {
+ std::stringstream stream;
+ stream << value;
+ return stream.str();
+ }
+
+ template <typename T>
+ std::string ToStringAny(const std::vector<T> value,
+ typename std::enable_if<
+ SupportsInsertionOperator<T>::value>::type* = 0) {
+ std::stringstream stream;
+ stream << "vector{";
+
+ for (size_t i = 0; i < value.size(); ++i) {
+ stream << ToStringAny(value[i]);
+
+ if (i != value.size() - 1) {
+ stream << ',';
+ }
+ }
+
+ stream << "}";
+ return stream.str();
+ }
+
+ template <typename T>
+ std::string ToStringAny(const T&,
+ typename std::enable_if<
+ !SupportsInsertionOperator<T>::value>::type* = 0
+ ) {
+ return std::string("(unknown type [no operator<< implemented] for )");
+ }
+ } // namespace detail // NOLINT [readability/namespace] [5]
+} // namespace art
+
+#endif // ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_
diff --git a/cmdline/memory_representation.h b/cmdline/memory_representation.h
new file mode 100644
index 0000000..93387de
--- /dev/null
+++ b/cmdline/memory_representation.h
@@ -0,0 +1,71 @@
+/*
+ * 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_CMDLINE_MEMORY_REPRESENTATION_H_
+#define ART_CMDLINE_MEMORY_REPRESENTATION_H_
+
+#include <string>
+#include <assert.h>
+#include <ostream>
+#include "utils.h"
+
+namespace art {
+
+// An integral representation of bytes of memory.
+// The underlying runtime size_t value is guaranteed to be a multiple of Divisor.
+template <size_t Divisor = 1024>
+struct Memory {
+ static_assert(IsPowerOfTwo(Divisor), "Divisor must be a power of 2");
+
+ static Memory<Divisor> FromBytes(size_t bytes) {
+ assert(bytes % Divisor == 0);
+ return Memory<Divisor>(bytes);
+ }
+
+ Memory() : Value(0u) {}
+ Memory(size_t value) : Value(value) { // NOLINT [runtime/explicit] [5]
+ assert(value % Divisor == 0);
+ }
+ operator size_t() const { return Value; }
+
+ size_t ToBytes() const {
+ return Value;
+ }
+
+ static constexpr size_t kDivisor = Divisor;
+
+ static const char* Name() {
+ static std::string str;
+ if (str.empty()) {
+ str = "Memory<" + std::to_string(Divisor) + '>';
+ }
+
+ return str.c_str();
+ }
+
+ size_t Value;
+};
+
+template <size_t Divisor>
+std::ostream& operator<<(std::ostream& stream, Memory<Divisor> memory) {
+ return stream << memory.Value << '*' << Divisor;
+}
+
+using MemoryKiB = Memory<1024>;
+
+} // namespace art
+
+#endif // ART_CMDLINE_MEMORY_REPRESENTATION_H_
diff --git a/cmdline/token_range.h b/cmdline/token_range.h
new file mode 100644
index 0000000..50c54fe
--- /dev/null
+++ b/cmdline/token_range.h
@@ -0,0 +1,425 @@
+/*
+ * 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_CMDLINE_TOKEN_RANGE_H_
+#define ART_CMDLINE_TOKEN_RANGE_H_
+
+#include <assert.h>
+#include <vector>
+#include <string>
+#include <algorithm>
+#include <memory>
+
+namespace art {
+// A range of tokens to make token matching algorithms easier.
+//
+// We try really hard to avoid copying and store only a pointer and iterators to the
+// interiors of the vector, so a typical copy constructor never ends up doing a deep copy.
+// It is up to the user to play nice and not to mutate the strings in-place.
+//
+// Tokens are only copied if a mutating operation is performed (and even then only
+// if it *actually* mutates the token).
+struct TokenRange {
+ // Short-hand for a vector of strings. A single string and a token is synonymous.
+ using TokenList = std::vector<std::string>;
+
+ // Copying-from-vector constructor.
+ explicit TokenRange(const TokenList& token_list)
+ : token_list_(new TokenList(token_list)),
+ begin_(token_list_->begin()),
+ end_(token_list_->end())
+ {}
+
+ // Copying-from-iterator constructor
+ template <typename ForwardIterator>
+ explicit TokenRange(ForwardIterator it_begin, ForwardIterator it_end)
+ : token_list_(new TokenList(it_begin, it_end)),
+ begin_(token_list_->begin()),
+ end_(token_list_->end())
+ {}
+
+#if 0
+ // Copying-from-vector constructor.
+ TokenRange(const TokenList& token_list ATTRIBUTE_UNUSED,
+ TokenList::const_iterator it_begin,
+ TokenList::const_iterator it_end)
+ : token_list_(new TokenList(it_begin, it_end)),
+ begin_(token_list_->begin()),
+ end_(token_list_->end()) {
+ assert(it_begin >= token_list.begin());
+ assert(it_end <= token_list.end());
+ }
+#endif
+
+ // Copying from char array constructor, convertings into tokens (strings) along the way.
+ TokenRange(const char* token_list[], size_t length)
+ : token_list_(new TokenList(&token_list[0], &token_list[length])),
+ begin_(token_list_->begin()),
+ end_(token_list_->end())
+ {}
+
+ // Non-copying move-from-vector constructor. Takes over the token vector.
+ explicit TokenRange(TokenList&& token_list)
+ : token_list_(new TokenList(std::forward<TokenList>(token_list))),
+ begin_(token_list_->begin()),
+ end_(token_list_->end())
+ {}
+
+ // Non-copying constructor. Retain reference to existing list of tokens.
+ TokenRange(std::shared_ptr<TokenList> token_list,
+ TokenList::const_iterator it_begin,
+ TokenList::const_iterator it_end)
+ : token_list_(token_list),
+ begin_(it_begin),
+ end_(it_end) {
+ assert(it_begin >= token_list->begin());
+ assert(it_end <= token_list->end());
+ }
+
+ // Non-copying copy constructor.
+ TokenRange(const TokenRange& other) = default;
+
+ // Non-copying move constructor.
+ TokenRange(TokenRange&& other) = default;
+
+ // Non-copying constructor. Retains reference to an existing list of tokens, with offset.
+ explicit TokenRange(std::shared_ptr<TokenList> token_list)
+ : token_list_(token_list),
+ begin_(token_list_->begin()),
+ end_(token_list_->end())
+ {}
+
+ // Iterator type for begin() and end(). Guaranteed to be a RandomAccessIterator.
+ using iterator = TokenList::const_iterator;
+
+ // Iterator type for const begin() and const end(). Guaranteed to be a RandomAccessIterator.
+ using const_iterator = iterator;
+
+ // Create a token range by splitting a string. Each separator gets their own token.
+ // Since the separator are retained as tokens, it might be useful to call
+ // RemoveToken afterwards.
+ static TokenRange Split(const std::string& string, std::initializer_list<char> separators) {
+ TokenList new_token_list;
+
+ std::string tok;
+ for (auto&& c : string) {
+ for (char sep : separators) {
+ if (c == sep) {
+ // We spotted a separator character.
+ // Push back everything before the last separator as a new token.
+ // Push back the separator as a token.
+ if (!tok.empty()) {
+ new_token_list.push_back(tok);
+ tok = "";
+ }
+ new_token_list.push_back(std::string() + sep);
+ } else {
+ // Build up the token with another character.
+ tok += c;
+ }
+ }
+ }
+
+ if (!tok.empty()) {
+ new_token_list.push_back(tok);
+ }
+
+ return TokenRange(std::move(new_token_list));
+ }
+
+ // A RandomAccessIterator to the first element in this range.
+ iterator begin() const {
+ return begin_;
+ }
+
+ // A RandomAccessIterator to one past the last element in this range.
+ iterator end() const {
+ return end_;
+ }
+
+ // The size of the range, i.e. how many tokens are in it.
+ size_t Size() const {
+ return std::distance(begin_, end_);
+ }
+
+ // Are there 0 tokens in this range?
+ bool IsEmpty() const {
+ return Size() > 0;
+ }
+
+ // Look up a token by it's offset.
+ const std::string& GetToken(size_t offset) const {
+ assert(offset < Size());
+ return *(begin_ + offset);
+ }
+
+ // Does this token range equal the other range?
+ // Equality is defined as having both the same size, and
+ // each corresponding token being equal.
+ bool operator==(const TokenRange& other) const {
+ if (this == &other) {
+ return true;
+ }
+
+ if (Size() != other.Size()) {
+ return false;
+ }
+
+ return std::equal(begin(), end(), other.begin());
+ }
+
+ // Look up the token at the requested index.
+ const std::string& operator[](int index) const {
+ assert(index >= 0 && static_cast<size_t>(index) < Size());
+ return *(begin() + index);
+ }
+
+ // Does this current range start with the other range?
+ bool StartsWith(const TokenRange& other) const {
+ if (this == &other) {
+ return true;
+ }
+
+ if (Size() < other.Size()) {
+ return false;
+ }
+
+ auto& smaller = Size() < other.Size() ? *this : other;
+ auto& greater = Size() < other.Size() ? other : *this;
+
+ return std::equal(smaller.begin(), smaller.end(), greater.begin());
+ }
+
+ // Remove all characters 'c' from each token, potentially copying the underlying tokens.
+ TokenRange RemoveCharacter(char c) const {
+ TokenList new_token_list(begin(), end());
+
+ bool changed = false;
+ for (auto&& token : new_token_list) {
+ auto it = std::remove_if(token.begin(), token.end(), [&](char ch) {
+ if (ch == c) {
+ changed = true;
+ return true;
+ }
+ return false;
+ });
+ token.erase(it, token.end());
+ }
+
+ if (!changed) {
+ return *this;
+ }
+
+ return TokenRange(std::move(new_token_list));
+ }
+
+ // Remove all tokens matching this one, potentially copying the underlying tokens.
+ TokenRange RemoveToken(const std::string& token) {
+ return RemoveIf([&](const std::string& tok) { return tok == token; });
+ }
+
+ // Discard all empty tokens, potentially copying the underlying tokens.
+ TokenRange DiscardEmpty() const {
+ return RemoveIf([](const std::string& token) { return token.empty(); });
+ }
+
+ // Create a non-copying subset of this range.
+ // Length is trimmed so that the Slice does not go out of range.
+ TokenRange Slice(size_t offset, size_t length = std::string::npos) const {
+ assert(offset < Size());
+
+ if (length != std::string::npos && offset + length > Size()) {
+ length = Size() - offset;
+ }
+
+ iterator it_end;
+ if (length == std::string::npos) {
+ it_end = end();
+ } else {
+ it_end = begin() + offset + length;
+ }
+
+ return TokenRange(token_list_, begin() + offset, it_end);
+ }
+
+ // Try to match the string with tokens from this range.
+ // Each token is used to match exactly once (after which the next token is used, and so on).
+ // The matching happens from left-to-right in a non-greedy fashion.
+ // If the currently-matched token is the wildcard, then the new outputted token will
+ // contain as much as possible until the next token is matched.
+ //
+ // For example, if this == ["a:", "_", "b:] and "_" is the match string, then
+ // MatchSubstrings on "a:foob:" will yield: ["a:", "foo", "b:"]
+ //
+ // Since the string matching can fail (e.g. ["foo"] against "bar"), then this
+ // function can fail, in which cause it will return null.
+ std::unique_ptr<TokenRange> MatchSubstrings(const std::string& string,
+ const std::string& wildcard) const {
+ TokenList new_token_list;
+
+ size_t wildcard_idx = std::string::npos;
+ size_t string_idx = 0;
+
+ // Function to push all the characters matched as a wildcard so far
+ // as a brand new token. It resets the wildcard matching.
+ // Empty wildcards are possible and ok, but only if wildcard matching was on.
+ auto maybe_push_wildcard_token = [&]() {
+ if (wildcard_idx != std::string::npos) {
+ size_t wildcard_length = string_idx - wildcard_idx;
+ std::string wildcard_substr = string.substr(wildcard_idx, wildcard_length);
+ new_token_list.push_back(std::move(wildcard_substr));
+
+ wildcard_idx = std::string::npos;
+ }
+ };
+
+ for (iterator it = begin(); it != end(); ++it) {
+ const std::string& tok = *it;
+
+ if (tok == wildcard) {
+ maybe_push_wildcard_token();
+ wildcard_idx = string_idx;
+ continue;
+ }
+
+ size_t next_token_idx = string.find(tok);
+ if (next_token_idx == std::string::npos) {
+ // Could not find token at all
+ return nullptr;
+ } else if (next_token_idx != string_idx && wildcard_idx == std::string::npos) {
+ // Found the token at a non-starting location, and we weren't
+ // trying to parse the wildcard.
+ return nullptr;
+ }
+
+ new_token_list.push_back(string.substr(next_token_idx, tok.size()));
+ maybe_push_wildcard_token();
+ string_idx += tok.size();
+ }
+
+ size_t remaining = string.size() - string_idx;
+ if (remaining > 0) {
+ if (wildcard_idx == std::string::npos) {
+ // Some characters were still remaining in the string,
+ // but it wasn't trying to match a wildcard.
+ return nullptr;
+ }
+ }
+
+ // If some characters are remaining, the rest must be a wildcard.
+ string_idx += remaining;
+ maybe_push_wildcard_token();
+
+ return std::unique_ptr<TokenRange>(new TokenRange(std::move(new_token_list)));
+ }
+
+ // Do a quick match token-by-token, and see if they match.
+ // Any tokens with a wildcard in them are only matched up until the wildcard.
+ // If this is true, then the wildcard matching later on can still fail, so this is not
+ // a guarantee that the argument is correct, it's more of a strong hint that the
+ // user-provided input *probably* was trying to match this argument.
+ //
+ // Returns how many tokens were either matched (or ignored because there was a
+ // wildcard present). 0 means no match. If the size() tokens are returned.
+ size_t MaybeMatches(const TokenRange& token_list, const std::string& wildcard) const {
+ auto token_it = token_list.begin();
+ auto token_end = token_list.end();
+ auto name_it = begin();
+ auto name_end = end();
+
+ size_t matched_tokens = 0;
+
+ while (token_it != token_end && name_it != name_end) {
+ // Skip token matching when the corresponding name has a wildcard in it.
+ const std::string& name = *name_it;
+
+ size_t wildcard_idx = name.find(wildcard);
+ if (wildcard_idx == std::string::npos) { // No wildcard present
+ // Did the definition token match the user token?
+ if (name != *token_it) {
+ return matched_tokens;
+ }
+ } else {
+ std::string name_prefix = name.substr(0, wildcard_idx);
+
+ // Did the user token start with the up-to-the-wildcard prefix?
+ if (!StartsWith(*token_it, name_prefix)) {
+ return matched_tokens;
+ }
+ }
+
+ ++token_it;
+ ++name_it;
+ ++matched_tokens;
+ }
+
+ // If we got this far, it's either a full match or the token list was too short.
+ return matched_tokens;
+ }
+
+ // Flatten the token range by joining every adjacent token with the separator character.
+ // e.g. ["hello", "world"].join('$') == "hello$world"
+ std::string Join(char separator) const {
+ TokenList tmp(begin(), end());
+ return art::Join(tmp, separator);
+ // TODO: Join should probably take an offset or iterators
+ }
+
+ private:
+ static bool StartsWith(const std::string& larger, const std::string& smaller) {
+ if (larger.size() >= smaller.size()) {
+ return std::equal(smaller.begin(), smaller.end(), larger.begin());
+ }
+
+ return false;
+ }
+
+ template <typename TPredicate>
+ TokenRange RemoveIf(const TPredicate& predicate) const {
+ // If any of the tokens in the token lists are empty, then
+ // we need to remove them and compress the token list into a smaller one.
+ bool remove = false;
+ for (auto it = begin_; it != end_; ++it) {
+ auto&& token = *it;
+
+ if (predicate(token)) {
+ remove = true;
+ break;
+ }
+ }
+
+ // Actually copy the token list and remove the tokens that don't match our predicate.
+ if (remove) {
+ auto token_list = std::make_shared<TokenList>(begin(), end());
+ TokenList::iterator new_end =
+ std::remove_if(token_list->begin(), token_list->end(), predicate);
+ token_list->erase(new_end, token_list->end());
+
+ assert(token_list_->size() > token_list->size() && "Nothing was actually removed!");
+
+ return TokenRange(token_list);
+ }
+
+ return *this;
+ }
+
+ const std::shared_ptr<std::vector<std::string>> token_list_;
+ const iterator begin_;
+ const iterator end_;
+};
+} // namespace art
+
+#endif // ART_CMDLINE_TOKEN_RANGE_H_
diff --git a/cmdline/unit.h b/cmdline/unit.h
new file mode 100644
index 0000000..6b53b18
--- /dev/null
+++ b/cmdline/unit.h
@@ -0,0 +1,35 @@
+/*
+ * 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_CMDLINE_UNIT_H_
+#define ART_CMDLINE_UNIT_H_
+
+namespace art {
+
+// Used for arguments that simply indicate presence (e.g. "-help") without any values.
+struct Unit {
+ // Avoid 'Conditional jump or move depends on uninitialised value(s)' errors
+ // when running valgrind by specifying a user-defined constructor.
+ Unit() {}
+ ~Unit() {}
+ bool operator==(Unit) const {
+ return true;
+ }
+};
+
+} // namespace art
+
+#endif // ART_CMDLINE_UNIT_H_
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 70c7e52..61379fb 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -60,7 +60,6 @@
dex/dex_to_dex_compiler.cc \
dex/bb_optimizations.cc \
dex/compiler_ir.cc \
- dex/frontend.cc \
dex/mir_analysis.cc \
dex/mir_dataflow.cc \
dex/mir_field_info.cc \
@@ -70,12 +69,14 @@
dex/post_opt_passes.cc \
dex/pass_driver_me_opts.cc \
dex/pass_driver_me_post_opt.cc \
+ dex/pass_manager.cc \
dex/ssa_transformation.cc \
dex/verified_method.cc \
dex/verification_results.cc \
dex/vreg_analysis.cc \
dex/quick_compiler_callbacks.cc \
driver/compiler_driver.cc \
+ driver/compiler_options.cc \
driver/dex_compilation_unit.cc \
jni/quick/arm/calling_convention_arm.cc \
jni/quick/arm64/calling_convention_arm64.cc \
@@ -84,8 +85,8 @@
jni/quick/x86_64/calling_convention_x86_64.cc \
jni/quick/calling_convention.cc \
jni/quick/jni_compiler.cc \
- llvm/llvm_compiler.cc \
optimizing/builder.cc \
+ optimizing/bounds_check_elimination.cc \
optimizing/code_generator.cc \
optimizing/code_generator_arm.cc \
optimizing/code_generator_arm64.cc \
@@ -96,7 +97,13 @@
optimizing/graph_checker.cc \
optimizing/graph_visualizer.cc \
optimizing/gvn.cc \
+ optimizing/inliner.cc \
optimizing/instruction_simplifier.cc \
+ optimizing/intrinsics.cc \
+ optimizing/intrinsics_arm.cc \
+ optimizing/intrinsics_arm64.cc \
+ optimizing/intrinsics_x86_64.cc \
+ optimizing/licm.cc \
optimizing/locations.cc \
optimizing/nodes.cc \
optimizing/optimization.cc \
@@ -104,10 +111,12 @@
optimizing/parallel_move_resolver.cc \
optimizing/prepare_for_register_allocation.cc \
optimizing/register_allocator.cc \
+ optimizing/side_effects_analysis.cc \
optimizing/ssa_builder.cc \
optimizing/ssa_liveness_analysis.cc \
optimizing/ssa_phi_elimination.cc \
- optimizing/ssa_type_propagation.cc \
+ optimizing/primitive_type_propagation.cc \
+ optimizing/reference_type_propagation.cc \
trampolines/trampoline_compiler.cc \
utils/arena_allocator.cc \
utils/arena_bit_vector.cc \
@@ -121,11 +130,14 @@
utils/dwarf_cfi.cc \
utils/mips/assembler_mips.cc \
utils/mips/managed_register_mips.cc \
+ utils/mips64/assembler_mips64.cc \
+ utils/mips64/managed_register_mips64.cc \
utils/x86/assembler_x86.cc \
utils/x86/managed_register_x86.cc \
utils/x86_64/assembler_x86_64.cc \
utils/x86_64/managed_register_x86_64.cc \
utils/scoped_arena_allocator.cc \
+ utils/swap_space.cc \
buffered_output_stream.cc \
compiler.cc \
elf_writer.cc \
@@ -136,38 +148,8 @@
output_stream.cc \
vector_output_stream.cc
-ifeq ($(ART_SEA_IR_MODE),true)
-LIBART_COMPILER_SRC_FILES += \
- sea_ir/frontend.cc \
- sea_ir/ir/instruction_tools.cc \
- sea_ir/ir/sea.cc \
- sea_ir/code_gen/code_gen.cc \
- sea_ir/code_gen/code_gen_data.cc \
- sea_ir/types/type_inference.cc \
- sea_ir/types/type_inference_visitor.cc \
- sea_ir/debug/dot_gen.cc
-endif
-
LIBART_COMPILER_CFLAGS :=
-ifeq ($(ART_USE_PORTABLE_COMPILER),true)
-LIBART_COMPILER_SRC_FILES += \
- dex/portable/mir_to_gbc.cc \
- elf_writer_mclinker.cc \
- jni/portable/jni_compiler.cc \
- llvm/compiler_llvm.cc \
- llvm/gbc_expander.cc \
- llvm/generated/art_module.cc \
- llvm/intrinsic_helper.cc \
- llvm/ir_builder.cc \
- llvm/llvm_compilation_unit.cc \
- llvm/md_builder.cc \
- llvm/runtime_support_builder.cc \
- llvm/runtime_support_builder_arm.cc \
- llvm/runtime_support_builder_x86.cc
-LIBART_COMPILER_CFLAGS += -DART_USE_PORTABLE_COMPILER=1
-endif
-
LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES := \
dex/quick/arm/arm_lir.h \
dex/quick/arm64/arm64_lir.h \
@@ -233,7 +215,6 @@
LOCAL_GENERATED_SOURCES += $$(ENUM_OPERATOR_OUT_GEN)
LOCAL_CFLAGS := $$(LIBART_COMPILER_CFLAGS)
- include external/libcxx/libcxx.mk
ifeq ($$(art_target_or_host),target)
$(call set-target-local-clang-vars)
$(call set-target-local-cflags-vars,$(2))
@@ -248,28 +229,6 @@
endif
endif
- ifeq ($(ART_USE_PORTABLE_COMPILER),true)
- LOCAL_SHARED_LIBRARIES += libLLVM
- LOCAL_CFLAGS += -DART_USE_PORTABLE_COMPILER=1
- ifeq ($$(art_target_or_host),target)
- LOCAL_STATIC_LIBRARIES_arm += libmcldARMInfo libmcldARMTarget
- LOCAL_STATIC_LIBRARIES_x86 += libmcldX86Info libmcldX86Target
- LOCAL_STATIC_LIBRARIES_x86_64 += libmcldX86Info libmcldX86Target
- LOCAL_STATIC_LIBRARIES_mips += libmcldMipsInfo libmcldMipsTarget
- ifeq ($(TARGET_ARCH),arm64)
- $$(info TODOAArch64: $$(LOCAL_PATH)/Android.mk Add Arm64 specific MCLinker libraries)
- endif # TARGET_ARCH != arm64
- include $(LLVM_DEVICE_BUILD_MK)
- else # host
- LOCAL_STATIC_LIBRARIES += libmcldARMInfo libmcldARMTarget
- LOCAL_STATIC_LIBRARIES += libmcldX86Info libmcldX86Target
- LOCAL_STATIC_LIBRARIES += libmcldMipsInfo libmcldMipsTarget
- include $(LLVM_HOST_BUILD_MK)
- endif
- LOCAL_STATIC_LIBRARIES += libmcldCore libmcldObject libmcldADT libmcldFragment libmcldTarget libmcldCodeGen libmcldLDVariant libmcldMC libmcldSupport libmcldLD libmcldScript
- include $(LLVM_GEN_INTRINSICS_MK)
- endif
-
LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
ifeq ($$(art_target_or_host),host)
@@ -284,6 +243,9 @@
else
LOCAL_SHARED_LIBRARIES += libvixl
endif
+
+ LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
+
ifeq ($$(art_target_or_host),target)
# For atrace.
LOCAL_SHARED_LIBRARIES += libcutils
@@ -322,18 +284,3 @@
ifeq ($(ART_BUILD_TARGET_DEBUG),true)
$(eval $(call build-libart-compiler,target,debug))
endif
-
-# Rule to build /system/lib/libcompiler_rt.a
-# Usually static libraries are not installed on the device.
-ifeq ($(ART_USE_PORTABLE_COMPILER),true)
-ifeq ($(ART_BUILD_TARGET),true)
-# TODO: Move to external/compiler_rt
-$(eval $(call copy-one-file, $(call intermediates-dir-for,STATIC_LIBRARIES,libcompiler_rt,,)/libcompiler_rt.a, $(TARGET_OUT_SHARED_LIBRARIES)/libcompiler_rt.a))
-ifdef TARGET_2ND_ARCH
-$(eval $(call copy-one-file, $(call intermediates-dir-for,STATIC_LIBRARIES,libcompiler_rt,,,t)/libcompiler_rt.a, $(2ND_TARGET_OUT_SHARED_LIBRARIES)/libcompiler_rt.a))
-endif
-
-$(DEX2OAT): $(TARGET_OUT_SHARED_LIBRARIES)/libcompiler_rt.a
-
-endif
-endif
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index a3d9a0b..1cd78f8 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -19,9 +19,10 @@
#include "arch/instruction_set_features.h"
#include "class_linker.h"
#include "compiled_method.h"
+#include "dex/pass_manager.h"
#include "dex/quick_compiler_callbacks.h"
-#include "dex/verification_results.h"
#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "dex/verification_results.h"
#include "driver/compiler_driver.h"
#include "interpreter/interpreter.h"
#include "mirror/art_method.h"
@@ -48,51 +49,52 @@
method->GetDexMethodIndex()));
}
if (compiled_method != nullptr) {
- const std::vector<uint8_t>* code = compiled_method->GetQuickCode();
- const void* code_ptr;
- bool is_portable = (code == nullptr);
- if (!is_portable) {
- uint32_t code_size = code->size();
- CHECK_NE(0u, code_size);
- const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable();
- uint32_t vmap_table_offset = vmap_table.empty() ? 0u
- : sizeof(OatQuickMethodHeader) + vmap_table.size();
- const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable();
- uint32_t mapping_table_offset = mapping_table.empty() ? 0u
- : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table.size();
- const std::vector<uint8_t>& gc_map = *compiled_method->GetGcMap();
- uint32_t gc_map_offset = gc_map.empty() ? 0u
- : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table.size() + gc_map.size();
- OatQuickMethodHeader method_header(mapping_table_offset, vmap_table_offset, gc_map_offset,
- compiled_method->GetFrameSizeInBytes(),
- compiled_method->GetCoreSpillMask(),
- compiled_method->GetFpSpillMask(), code_size);
+ const SwapVector<uint8_t>* code = compiled_method->GetQuickCode();
+ uint32_t code_size = code->size();
+ CHECK_NE(0u, code_size);
+ const SwapVector<uint8_t>& vmap_table = compiled_method->GetVmapTable();
+ uint32_t vmap_table_offset = vmap_table.empty() ? 0u
+ : sizeof(OatQuickMethodHeader) + vmap_table.size();
+ const SwapVector<uint8_t>* mapping_table = compiled_method->GetMappingTable();
+ bool mapping_table_used = mapping_table != nullptr && !mapping_table->empty();
+ size_t mapping_table_size = mapping_table_used ? mapping_table->size() : 0U;
+ uint32_t mapping_table_offset = !mapping_table_used ? 0u
+ : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table_size;
+ const SwapVector<uint8_t>* gc_map = compiled_method->GetGcMap();
+ bool gc_map_used = gc_map != nullptr && !gc_map->empty();
+ size_t gc_map_size = gc_map_used ? gc_map->size() : 0U;
+ uint32_t gc_map_offset = !gc_map_used ? 0u
+ : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table_size + gc_map_size;
+ OatQuickMethodHeader method_header(mapping_table_offset, vmap_table_offset, gc_map_offset,
+ compiled_method->GetFrameSizeInBytes(),
+ compiled_method->GetCoreSpillMask(),
+ compiled_method->GetFpSpillMask(), code_size);
- header_code_and_maps_chunks_.push_back(std::vector<uint8_t>());
- std::vector<uint8_t>* chunk = &header_code_and_maps_chunks_.back();
- size_t size = sizeof(method_header) + code_size + vmap_table.size() + mapping_table.size() +
- gc_map.size();
- size_t code_offset = compiled_method->AlignCode(size - code_size);
- size_t padding = code_offset - (size - code_size);
- chunk->reserve(padding + size);
- chunk->resize(sizeof(method_header));
- memcpy(&(*chunk)[0], &method_header, sizeof(method_header));
- chunk->insert(chunk->begin(), vmap_table.begin(), vmap_table.end());
- chunk->insert(chunk->begin(), mapping_table.begin(), mapping_table.end());
- chunk->insert(chunk->begin(), gc_map.begin(), gc_map.end());
- chunk->insert(chunk->begin(), padding, 0);
- chunk->insert(chunk->end(), code->begin(), code->end());
- CHECK_EQ(padding + size, chunk->size());
- code_ptr = &(*chunk)[code_offset];
- } else {
- code = compiled_method->GetPortableCode();
- code_ptr = &(*code)[0];
+ header_code_and_maps_chunks_.push_back(std::vector<uint8_t>());
+ std::vector<uint8_t>* chunk = &header_code_and_maps_chunks_.back();
+ size_t size = sizeof(method_header) + code_size + vmap_table.size() + mapping_table_size +
+ gc_map_size;
+ size_t code_offset = compiled_method->AlignCode(size - code_size);
+ size_t padding = code_offset - (size - code_size);
+ chunk->reserve(padding + size);
+ chunk->resize(sizeof(method_header));
+ memcpy(&(*chunk)[0], &method_header, sizeof(method_header));
+ chunk->insert(chunk->begin(), vmap_table.begin(), vmap_table.end());
+ if (mapping_table_used) {
+ chunk->insert(chunk->begin(), mapping_table->begin(), mapping_table->end());
}
+ if (gc_map_used) {
+ chunk->insert(chunk->begin(), gc_map->begin(), gc_map->end());
+ }
+ chunk->insert(chunk->begin(), padding, 0);
+ chunk->insert(chunk->end(), code->begin(), code->end());
+ CHECK_EQ(padding + size, chunk->size());
+ const void* code_ptr = &(*chunk)[code_offset];
MakeExecutable(code_ptr, code->size());
const void* method_code = CompiledMethod::CodePointer(code_ptr,
compiled_method->GetInstructionSet());
LOG(INFO) << "MakeExecutable " << PrettyMethod(method) << " code=" << method_code;
- class_linker_->SetEntryPointsToCompiledCode(method, method_code, is_portable);
+ class_linker_->SetEntryPointsToCompiledCode(method, method_code);
} else {
// No code? You must mean to go into the interpreter.
// Or the generic JNI...
@@ -155,7 +157,7 @@
}
// TODO: make selectable
- Compiler::Kind compiler_kind = kUsePortableCompiler ? Compiler::kPortable : Compiler::kQuick;
+ Compiler::Kind compiler_kind = Compiler::kQuick;
timer_.reset(new CumulativeLogger("Compilation times"));
compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
verification_results_.get(),
@@ -163,7 +165,7 @@
compiler_kind, instruction_set,
instruction_set_features_.get(),
true, new std::set<std::string>, nullptr,
- 2, true, true, timer_.get(), ""));
+ 2, true, true, "", timer_.get(), -1, ""));
}
// We typically don't generate an image in unit tests, disable this optimization by default.
compiler_driver_->SetSupportBootImageFixup(false);
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc
index 698bf3b..22be28c 100644
--- a/compiler/compiled_method.cc
+++ b/compiler/compiled_method.cc
@@ -20,35 +20,13 @@
namespace art {
CompiledCode::CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
- const std::vector<uint8_t>& quick_code)
+ const ArrayRef<const uint8_t>& quick_code)
: compiler_driver_(compiler_driver), instruction_set_(instruction_set),
- portable_code_(nullptr), quick_code_(nullptr) {
- SetCode(&quick_code, nullptr);
+ quick_code_(nullptr) {
+ SetCode(&quick_code);
}
-CompiledCode::CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
- const std::string& elf_object, const std::string& symbol)
- : compiler_driver_(compiler_driver), instruction_set_(instruction_set),
- portable_code_(nullptr), quick_code_(nullptr), symbol_(symbol) {
- CHECK_NE(elf_object.size(), 0U);
- CHECK_NE(symbol.size(), 0U);
- std::vector<uint8_t> temp_code(elf_object.size());
- for (size_t i = 0; i < elf_object.size(); ++i) {
- temp_code[i] = elf_object[i];
- }
- // TODO: we shouldn't just shove ELF objects in as "code" but
- // change to have different kinds of compiled methods. This is
- // being deferred until we work on hybrid execution or at least
- // until we work on batch compilation.
- SetCode(nullptr, &temp_code);
-}
-
-void CompiledCode::SetCode(const std::vector<uint8_t>* quick_code,
- const std::vector<uint8_t>* portable_code) {
- if (portable_code != nullptr) {
- CHECK(!portable_code->empty());
- portable_code_ = compiler_driver_->DeduplicateCode(*portable_code);
- }
+void CompiledCode::SetCode(const ArrayRef<const uint8_t>* quick_code) {
if (quick_code != nullptr) {
CHECK(!quick_code->empty());
quick_code_ = compiler_driver_->DeduplicateCode(*quick_code);
@@ -64,17 +42,8 @@
} else {
return std::equal(quick_code_->begin(), quick_code_->end(), rhs.quick_code_->begin());
}
- } else if (portable_code_ != nullptr) {
- if (rhs.portable_code_ == nullptr) {
- return false;
- } else if (portable_code_->size() != rhs.portable_code_->size()) {
- return false;
- } else {
- return std::equal(portable_code_->begin(), portable_code_->end(),
- rhs.portable_code_->begin());
- }
}
- return (rhs.quick_code_ == nullptr) && (rhs.portable_code_ == nullptr);
+ return (rhs.quick_code_ == nullptr);
}
uint32_t CompiledCode::AlignCode(uint32_t offset) const {
@@ -94,6 +63,7 @@
case kArm:
case kArm64:
case kMips:
+ case kMips64:
case kX86:
case kX86_64:
return 0;
@@ -113,6 +83,7 @@
case kArm:
case kArm64:
case kMips:
+ case kMips64:
case kX86:
case kX86_64:
return code_pointer;
@@ -128,13 +99,8 @@
}
}
-const std::string& CompiledCode::GetSymbol() const {
- CHECK_NE(0U, symbol_.size());
- return symbol_;
-}
-
const std::vector<uint32_t>& CompiledCode::GetOatdataOffsetsToCompliledCodeOffset() const {
- CHECK_NE(0U, oatdata_offsets_to_compiled_code_offset_.size()) << symbol_;
+ CHECK_NE(0U, oatdata_offsets_to_compiled_code_offset_.size());
return oatdata_offsets_to_compiled_code_offset_;
}
@@ -144,90 +110,88 @@
CompiledMethod::CompiledMethod(CompilerDriver* driver,
InstructionSet instruction_set,
- const std::vector<uint8_t>& quick_code,
+ const ArrayRef<const uint8_t>& quick_code,
const size_t frame_size_in_bytes,
const uint32_t core_spill_mask,
const uint32_t fp_spill_mask,
- SrcMap* src_mapping_table,
- const std::vector<uint8_t>& mapping_table,
- const std::vector<uint8_t>& vmap_table,
- const std::vector<uint8_t>& native_gc_map,
- const std::vector<uint8_t>* cfi_info,
+ DefaultSrcMap* src_mapping_table,
+ const ArrayRef<const uint8_t>& mapping_table,
+ const ArrayRef<const uint8_t>& vmap_table,
+ const ArrayRef<const uint8_t>& native_gc_map,
+ const ArrayRef<const uint8_t>& cfi_info,
const ArrayRef<LinkerPatch>& patches)
: CompiledCode(driver, instruction_set, quick_code), frame_size_in_bytes_(frame_size_in_bytes),
core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask),
- src_mapping_table_(driver->DeduplicateSrcMappingTable(src_mapping_table->Arrange())),
- mapping_table_(driver->DeduplicateMappingTable(mapping_table)),
+ src_mapping_table_(src_mapping_table == nullptr ?
+ driver->DeduplicateSrcMappingTable(ArrayRef<SrcMapElem>()) :
+ driver->DeduplicateSrcMappingTable(ArrayRef<SrcMapElem>(src_mapping_table->Arrange()))),
+ mapping_table_(mapping_table.data() == nullptr ?
+ nullptr : driver->DeduplicateMappingTable(mapping_table)),
vmap_table_(driver->DeduplicateVMapTable(vmap_table)),
- gc_map_(driver->DeduplicateGCMap(native_gc_map)),
- cfi_info_(driver->DeduplicateCFIInfo(cfi_info)),
- patches_(patches.begin(), patches.end()) {
+ gc_map_(native_gc_map.data() == nullptr ? nullptr : driver->DeduplicateGCMap(native_gc_map)),
+ cfi_info_(cfi_info.data() == nullptr ? nullptr : driver->DeduplicateCFIInfo(cfi_info)),
+ patches_(patches.begin(), patches.end(), driver->GetSwapSpaceAllocator()) {
}
-CompiledMethod::CompiledMethod(CompilerDriver* driver,
- InstructionSet instruction_set,
- const std::vector<uint8_t>& quick_code,
- const size_t frame_size_in_bytes,
- const uint32_t core_spill_mask,
- const uint32_t fp_spill_mask,
- const std::vector<uint8_t>& mapping_table,
- const std::vector<uint8_t>& stack_map)
- : CompiledCode(driver, instruction_set, quick_code),
- frame_size_in_bytes_(frame_size_in_bytes),
- core_spill_mask_(core_spill_mask),
- fp_spill_mask_(fp_spill_mask),
- src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
- mapping_table_(driver->DeduplicateMappingTable(mapping_table)),
- vmap_table_(driver->DeduplicateVMapTable(stack_map)),
- gc_map_(nullptr),
- cfi_info_(nullptr),
- patches_() {
+CompiledMethod* CompiledMethod::SwapAllocCompiledMethod(
+ CompilerDriver* driver,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code,
+ const size_t frame_size_in_bytes,
+ const uint32_t core_spill_mask,
+ const uint32_t fp_spill_mask,
+ DefaultSrcMap* src_mapping_table,
+ const ArrayRef<const uint8_t>& mapping_table,
+ const ArrayRef<const uint8_t>& vmap_table,
+ const ArrayRef<const uint8_t>& native_gc_map,
+ const ArrayRef<const uint8_t>& cfi_info,
+ const ArrayRef<LinkerPatch>& patches) {
+ SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator());
+ CompiledMethod* ret = alloc.allocate(1);
+ alloc.construct(ret, driver, instruction_set, quick_code, frame_size_in_bytes, core_spill_mask,
+ fp_spill_mask, src_mapping_table, mapping_table, vmap_table, native_gc_map,
+ cfi_info, patches);
+ return ret;
}
-CompiledMethod::CompiledMethod(CompilerDriver* driver,
- InstructionSet instruction_set,
- const std::vector<uint8_t>& code,
- const size_t frame_size_in_bytes,
- const uint32_t core_spill_mask,
- const uint32_t fp_spill_mask,
- const std::vector<uint8_t>* cfi_info)
- : CompiledCode(driver, instruction_set, code),
- frame_size_in_bytes_(frame_size_in_bytes),
- core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask),
- src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
- mapping_table_(driver->DeduplicateMappingTable(std::vector<uint8_t>())),
- vmap_table_(driver->DeduplicateVMapTable(std::vector<uint8_t>())),
- gc_map_(driver->DeduplicateGCMap(std::vector<uint8_t>())),
- cfi_info_(driver->DeduplicateCFIInfo(cfi_info)),
- patches_() {
+CompiledMethod* CompiledMethod::SwapAllocCompiledMethodStackMap(
+ CompilerDriver* driver,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code,
+ const size_t frame_size_in_bytes,
+ const uint32_t core_spill_mask,
+ const uint32_t fp_spill_mask,
+ const ArrayRef<const uint8_t>& stack_map) {
+ SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator());
+ CompiledMethod* ret = alloc.allocate(1);
+ alloc.construct(ret, driver, instruction_set, quick_code, frame_size_in_bytes, core_spill_mask,
+ fp_spill_mask, nullptr, ArrayRef<const uint8_t>(), stack_map,
+ ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(), ArrayRef<LinkerPatch>());
+ return ret;
}
-// Constructs a CompiledMethod for the Portable compiler.
-CompiledMethod::CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set,
- const std::string& code, const std::vector<uint8_t>& gc_map,
- const std::string& symbol)
- : CompiledCode(driver, instruction_set, code, symbol),
- frame_size_in_bytes_(kStackAlignment), core_spill_mask_(0),
- fp_spill_mask_(0),
- src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
- mapping_table_(driver->DeduplicateMappingTable(std::vector<uint8_t>())),
- vmap_table_(driver->DeduplicateVMapTable(std::vector<uint8_t>())),
- gc_map_(driver->DeduplicateGCMap(gc_map)),
- cfi_info_(nullptr),
- patches_() {
+CompiledMethod* CompiledMethod::SwapAllocCompiledMethodCFI(
+ CompilerDriver* driver,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code,
+ const size_t frame_size_in_bytes,
+ const uint32_t core_spill_mask,
+ const uint32_t fp_spill_mask,
+ const ArrayRef<const uint8_t>& cfi_info) {
+ SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator());
+ CompiledMethod* ret = alloc.allocate(1);
+ alloc.construct(ret, driver, instruction_set, quick_code, frame_size_in_bytes, core_spill_mask,
+ fp_spill_mask, nullptr, ArrayRef<const uint8_t>(),
+ ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(),
+ cfi_info, ArrayRef<LinkerPatch>());
+ return ret;
}
-CompiledMethod::CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set,
- const std::string& code, const std::string& symbol)
- : CompiledCode(driver, instruction_set, code, symbol),
- frame_size_in_bytes_(kStackAlignment), core_spill_mask_(0),
- fp_spill_mask_(0),
- src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
- mapping_table_(driver->DeduplicateMappingTable(std::vector<uint8_t>())),
- vmap_table_(driver->DeduplicateVMapTable(std::vector<uint8_t>())),
- gc_map_(driver->DeduplicateGCMap(std::vector<uint8_t>())),
- cfi_info_(nullptr),
- patches_() {
+
+void CompiledMethod::ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m) {
+ SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator());
+ alloc.destroy(m);
+ alloc.deallocate(m, 1);
}
} // namespace art
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index 7f76eef..6013507 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -25,6 +25,7 @@
#include "method_reference.h"
#include "utils.h"
#include "utils/array_ref.h"
+#include "utils/swap_space.h"
namespace llvm {
class Function;
@@ -38,25 +39,17 @@
public:
// For Quick to supply an code blob
CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
- const std::vector<uint8_t>& quick_code);
-
- // For Portable to supply an ELF object
- CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
- const std::string& elf_object, const std::string &symbol);
+ const ArrayRef<const uint8_t>& quick_code);
InstructionSet GetInstructionSet() const {
return instruction_set_;
}
- const std::vector<uint8_t>* GetPortableCode() const {
- return portable_code_;
- }
-
- const std::vector<uint8_t>* GetQuickCode() const {
+ const SwapVector<uint8_t>* GetQuickCode() const {
return quick_code_;
}
- void SetCode(const std::vector<uint8_t>* quick_code, const std::vector<uint8_t>* portable_code);
+ void SetCode(const ArrayRef<const uint8_t>* quick_code);
bool operator==(const CompiledCode& rhs) const;
@@ -77,7 +70,6 @@
static const void* CodePointer(const void* code_pointer,
InstructionSet instruction_set);
- const std::string& GetSymbol() const;
const std::vector<uint32_t>& GetOatdataOffsetsToCompliledCodeOffset() const;
void AddOatdataOffsetToCompliledCodeOffset(uint32_t offset);
@@ -86,14 +78,8 @@
const InstructionSet instruction_set_;
- // The ELF image for portable.
- std::vector<uint8_t>* portable_code_;
-
// Used to store the PIC code for Quick.
- std::vector<uint8_t>* quick_code_;
-
- // Used for the Portable ELF symbol name.
- const std::string symbol_;
+ SwapVector<uint8_t>* quick_code_;
// There are offsets from the oatdata symbol to where the offset to
// the compiled method will be found. These are computed by the
@@ -124,8 +110,23 @@
}
};
-class SrcMap FINAL : public std::vector<SrcMapElem> {
+template <class Allocator>
+class SrcMap FINAL : public std::vector<SrcMapElem, Allocator> {
public:
+ using std::vector<SrcMapElem, Allocator>::begin;
+ using typename std::vector<SrcMapElem, Allocator>::const_iterator;
+ using std::vector<SrcMapElem, Allocator>::empty;
+ using std::vector<SrcMapElem, Allocator>::end;
+ using std::vector<SrcMapElem, Allocator>::resize;
+ using std::vector<SrcMapElem, Allocator>::shrink_to_fit;
+ using std::vector<SrcMapElem, Allocator>::size;
+
+ explicit SrcMap() {}
+
+ template <class InputIt>
+ SrcMap(InputIt first, InputIt last, const Allocator& alloc)
+ : std::vector<SrcMapElem, Allocator>(first, last, alloc) {}
+
void SortByFrom() {
std::sort(begin(), end(), [] (const SrcMapElem& lhs, const SrcMapElem& rhs) -> bool {
return lhs.from_ < rhs.from_;
@@ -173,6 +174,10 @@
}
};
+using DefaultSrcMap = SrcMap<std::allocator<SrcMapElem>>;
+using SwapSrcMap = SrcMap<SwapAllocator<SrcMapElem>>;
+
+
enum LinkerPatchType {
kLinkerPatchMethod,
kLinkerPatchCall,
@@ -270,49 +275,57 @@
class CompiledMethod FINAL : public CompiledCode {
public:
- // Constructs a CompiledMethod for Quick.
+ // Constructs a CompiledMethod.
+ // Note: Consider using the static allocation methods below that will allocate the CompiledMethod
+ // in the swap space.
CompiledMethod(CompilerDriver* driver,
InstructionSet instruction_set,
- const std::vector<uint8_t>& quick_code,
+ const ArrayRef<const uint8_t>& quick_code,
const size_t frame_size_in_bytes,
const uint32_t core_spill_mask,
const uint32_t fp_spill_mask,
- SrcMap* src_mapping_table,
- const std::vector<uint8_t>& mapping_table,
- const std::vector<uint8_t>& vmap_table,
- const std::vector<uint8_t>& native_gc_map,
- const std::vector<uint8_t>* cfi_info,
+ DefaultSrcMap* src_mapping_table,
+ const ArrayRef<const uint8_t>& mapping_table,
+ const ArrayRef<const uint8_t>& vmap_table,
+ const ArrayRef<const uint8_t>& native_gc_map,
+ const ArrayRef<const uint8_t>& cfi_info,
const ArrayRef<LinkerPatch>& patches = ArrayRef<LinkerPatch>());
- // Constructs a CompiledMethod for Optimizing.
- CompiledMethod(CompilerDriver* driver,
- InstructionSet instruction_set,
- const std::vector<uint8_t>& quick_code,
- const size_t frame_size_in_bytes,
- const uint32_t core_spill_mask,
- const uint32_t fp_spill_mask,
- const std::vector<uint8_t>& mapping_table,
- const std::vector<uint8_t>& vmap_table);
-
- // Constructs a CompiledMethod for the QuickJniCompiler.
- CompiledMethod(CompilerDriver* driver,
- InstructionSet instruction_set,
- const std::vector<uint8_t>& quick_code,
- const size_t frame_size_in_bytes,
- const uint32_t core_spill_mask,
- const uint32_t fp_spill_mask,
- const std::vector<uint8_t>* cfi_info);
-
- // Constructs a CompiledMethod for the Portable compiler.
- CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set, const std::string& code,
- const std::vector<uint8_t>& gc_map, const std::string& symbol);
-
- // Constructs a CompiledMethod for the Portable JniCompiler.
- CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set, const std::string& code,
- const std::string& symbol);
-
~CompiledMethod() {}
+ static CompiledMethod* SwapAllocCompiledMethod(
+ CompilerDriver* driver,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code,
+ const size_t frame_size_in_bytes,
+ const uint32_t core_spill_mask,
+ const uint32_t fp_spill_mask,
+ DefaultSrcMap* src_mapping_table,
+ const ArrayRef<const uint8_t>& mapping_table,
+ const ArrayRef<const uint8_t>& vmap_table,
+ const ArrayRef<const uint8_t>& native_gc_map,
+ const ArrayRef<const uint8_t>& cfi_info,
+ const ArrayRef<LinkerPatch>& patches = ArrayRef<LinkerPatch>());
+
+ static CompiledMethod* SwapAllocCompiledMethodStackMap(
+ CompilerDriver* driver,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code,
+ const size_t frame_size_in_bytes,
+ const uint32_t core_spill_mask,
+ const uint32_t fp_spill_mask,
+ const ArrayRef<const uint8_t>& stack_map);
+
+ static CompiledMethod* SwapAllocCompiledMethodCFI(CompilerDriver* driver,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code,
+ const size_t frame_size_in_bytes,
+ const uint32_t core_spill_mask,
+ const uint32_t fp_spill_mask,
+ const ArrayRef<const uint8_t>& cfi_info);
+
+ static void ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m);
+
size_t GetFrameSizeInBytes() const {
return frame_size_in_bytes_;
}
@@ -325,30 +338,29 @@
return fp_spill_mask_;
}
- const SrcMap& GetSrcMappingTable() const {
+ const SwapSrcMap& GetSrcMappingTable() const {
DCHECK(src_mapping_table_ != nullptr);
return *src_mapping_table_;
}
- const std::vector<uint8_t>& GetMappingTable() const {
- DCHECK(mapping_table_ != nullptr);
- return *mapping_table_;
+ SwapVector<uint8_t> const* GetMappingTable() const {
+ return mapping_table_;
}
- const std::vector<uint8_t>& GetVmapTable() const {
+ const SwapVector<uint8_t>& GetVmapTable() const {
DCHECK(vmap_table_ != nullptr);
return *vmap_table_;
}
- std::vector<uint8_t> const* GetGcMap() const {
+ SwapVector<uint8_t> const* GetGcMap() const {
return gc_map_;
}
- const std::vector<uint8_t>* GetCFIInfo() const {
+ const SwapVector<uint8_t>* GetCFIInfo() const {
return cfi_info_;
}
- const std::vector<LinkerPatch>& GetPatches() const {
+ const SwapVector<LinkerPatch>& GetPatches() const {
return patches_;
}
@@ -360,19 +372,19 @@
// For quick code, a bit mask describing spilled FPR callee-save registers.
const uint32_t fp_spill_mask_;
// For quick code, a set of pairs (PC, Line) mapping from native PC offset to Java line
- SrcMap* src_mapping_table_;
+ SwapSrcMap* src_mapping_table_;
// For quick code, a uleb128 encoded map from native PC offset to dex PC aswell as dex PC to
// native PC offset. Size prefixed.
- std::vector<uint8_t>* mapping_table_;
+ SwapVector<uint8_t>* mapping_table_;
// For quick code, a uleb128 encoded map from GPR/FPR register to dex register. Size prefixed.
- std::vector<uint8_t>* vmap_table_;
+ SwapVector<uint8_t>* vmap_table_;
// For quick code, a map keyed by native PC indices to bitmaps describing what dalvik registers
- // are live. For portable code, the key is a dalvik PC.
- std::vector<uint8_t>* gc_map_;
+ // are live.
+ SwapVector<uint8_t>* gc_map_;
// For quick code, a FDE entry for the debug_frame section.
- std::vector<uint8_t>* cfi_info_;
+ SwapVector<uint8_t>* cfi_info_;
// For quick code, linker patches needed by the method.
- std::vector<LinkerPatch> patches_;
+ SwapVector<LinkerPatch> patches_;
};
} // namespace art
diff --git a/compiler/compiler.cc b/compiler/compiler.cc
index b9fcf5b..5e8ec1e 100644
--- a/compiler/compiler.cc
+++ b/compiler/compiler.cc
@@ -17,57 +17,12 @@
#include "compiler.h"
#include "base/logging.h"
-#include "dex/quick/quick_compiler.h"
+#include "dex/quick/quick_compiler_factory.h"
#include "driver/compiler_driver.h"
-#include "llvm/llvm_compiler.h"
#include "optimizing/optimizing_compiler.h"
namespace art {
-#ifdef ART_SEA_IR_MODE
-constexpr bool kCanUseSeaIR = true;
-#else
-constexpr bool kCanUseSeaIR = false;
-#endif
-
-extern "C" art::CompiledMethod* SeaIrCompileMethod(const art::DexFile::CodeItem* code_item ATTRIBUTE_UNUSED,
- uint32_t access_flags ATTRIBUTE_UNUSED,
- art::InvokeType invoke_type ATTRIBUTE_UNUSED,
- uint16_t class_def_idx ATTRIBUTE_UNUSED,
- uint32_t method_idx ATTRIBUTE_UNUSED,
- jobject class_loader ATTRIBUTE_UNUSED,
- const art::DexFile& dex_file ATTRIBUTE_UNUSED)
-#ifdef ART_SEA_IR_MODE
-; // NOLINT(whitespace/semicolon)
-#else
-{
- UNREACHABLE();
-}
-#endif
-
-
-CompiledMethod* Compiler::TryCompileWithSeaIR(const art::DexFile::CodeItem* code_item,
- uint32_t access_flags,
- art::InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const art::DexFile& dex_file) {
- bool use_sea = kCanUseSeaIR &&
- (std::string::npos != PrettyMethod(method_idx, dex_file).find("fibonacci"));
- if (use_sea) {
- LOG(INFO) << "Using SEA IR to compile..." << std::endl;
- return SeaIrCompileMethod(code_item,
- access_flags,
- invoke_type,
- class_def_idx,
- method_idx,
- class_loader,
- dex_file);
- }
- return nullptr;
-}
-
Compiler* Compiler::Create(CompilerDriver* driver, Compiler::Kind kind) {
switch (kind) {
case kQuick:
@@ -76,13 +31,6 @@
case kOptimizing:
return CreateOptimizingCompiler(driver);
- case kPortable:
- {
- Compiler* compiler = CreateLLVMCompiler(driver);
- CHECK(compiler != nullptr) << "Portable compiler not compiled";
- return compiler;
- }
-
default:
LOG(FATAL) << "UNREACHABLE";
UNREACHABLE();
diff --git a/compiler/compiler.h b/compiler/compiler.h
index c2c15ff..6ec39f9 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -32,24 +32,16 @@
class ArtMethod;
}
-// Base class for compiler-specific thread-local storage for compiler worker threads
-class CompilerTls {
- public:
- CompilerTls() {}
- ~CompilerTls() {}
-};
-
class Compiler {
public:
enum Kind {
kQuick,
- kOptimizing,
- kPortable
+ kOptimizing
};
static Compiler* Create(CompilerDriver* driver, Kind kind);
- virtual void Init() const = 0;
+ virtual void Init() = 0;
virtual void UnInit() const = 0;
@@ -64,14 +56,6 @@
jobject class_loader,
const DexFile& dex_file) const = 0;
- static CompiledMethod* TryCompileWithSeaIR(const art::DexFile::CodeItem* code_item,
- uint32_t access_flags,
- art::InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const art::DexFile& dex_file);
-
virtual CompiledMethod* JniCompile(uint32_t access_flags,
uint32_t method_idx,
const DexFile& dex_file) const = 0;
@@ -86,21 +70,10 @@
bool is_host) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
- virtual Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const = 0;
-
uint64_t GetMaximumCompilationTimeBeforeWarning() const {
return maximum_compilation_time_before_warning_;
}
- virtual bool IsPortable() const {
- return false;
- }
-
- void SetBitcodeFileName(const CompilerDriver& driver, const std::string& filename) {
- UNUSED(driver);
- UNUSED(filename);
- }
-
virtual void InitCompilationUnit(CompilationUnit& cu) const = 0;
virtual ~Compiler() {}
@@ -119,10 +92,6 @@
return nullptr;
}
- virtual CompilerTls* CreateNewCompilerTls() {
- return nullptr;
- }
-
// Returns whether the method to compile is such a pathological case that
// it's not worth compiling.
static bool IsPathologicalCase(const DexFile::CodeItem& code_item,
diff --git a/compiler/dex/backend.h b/compiler/dex/backend.h
deleted file mode 100644
index 9cad933..0000000
--- a/compiler/dex/backend.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_DEX_BACKEND_H_
-#define ART_COMPILER_DEX_BACKEND_H_
-
-namespace art {
-
-class ArenaAllocator;
-class CompiledMethod;
-
-class Backend {
- public:
- virtual ~Backend() {}
- virtual void Materialize() = 0;
- virtual CompiledMethod* GetCompiledMethod() = 0;
-
- // Queries for backend support for vectors
- /*
- * Return the number of bits in a vector register.
- * @return 0 if vector registers are not supported, or the
- * number of bits in the vector register if supported.
- */
- virtual int VectorRegisterSize() { return 0; }
-
- /*
- * Return the number of reservable vector registers supported
- * @param long_or_fp, true if floating point computations will be
- * executed or the operations will be long type while vector
- * registers are reserved.
- * @return the number of vector registers that are available
- * @note The backend should ensure that sufficient vector registers
- * are held back to generate scalar code without exhausting vector
- * registers, if scalar code also uses the vector registers.
- */
- virtual int NumReservableVectorRegisters(bool long_or_fp) {
- UNUSED(long_or_fp);
- return 0;
- }
-
- protected:
- explicit Backend(ArenaAllocator* arena) : arena_(arena) {}
- ArenaAllocator* const arena_;
-}; // Class Backend
-
-} // namespace art
-
-#endif // ART_COMPILER_DEX_BACKEND_H_
diff --git a/compiler/dex/bb_optimizations.cc b/compiler/dex/bb_optimizations.cc
index 6a610ab..11a7e44 100644
--- a/compiler/dex/bb_optimizations.cc
+++ b/compiler/dex/bb_optimizations.cc
@@ -52,19 +52,31 @@
}
/*
- * BasicBlock Optimization pass implementation start.
+ * MethodUseCount pass implementation start.
*/
-void BBOptimizations::Start(PassDataHolder* data) const {
+bool MethodUseCount::Gate(const PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+ CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
DCHECK(c_unit != nullptr);
- /*
- * This pass has a different ordering depEnding on the suppress exception,
- * so do the pass here for now:
- * - Later, the Start should just change the ordering and we can move the extended
- * creation into the pass driver's main job with a new iterator
- */
- c_unit->mir_graph->BasicBlockOptimization();
+ // First initialize the data.
+ c_unit->mir_graph->InitializeMethodUses();
+
+ // Now check if the pass is to be ignored.
+ bool res = ((c_unit->disable_opt & (1 << kPromoteRegs)) == 0);
+
+ return res;
+}
+
+bool MethodUseCount::Worker(PassDataHolder* data) const {
+ DCHECK(data != nullptr);
+ PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+ CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+ DCHECK(c_unit != nullptr);
+ BasicBlock* bb = pass_me_data_holder->bb;
+ DCHECK(bb != nullptr);
+ c_unit->mir_graph->CountUses(bb);
+ // No need of repeating, so just return false.
+ return false;
}
} // namespace art
diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h
index 764a4cf..7685200 100644
--- a/compiler/dex/bb_optimizations.h
+++ b/compiler/dex/bb_optimizations.h
@@ -18,8 +18,10 @@
#define ART_COMPILER_DEX_BB_OPTIMIZATIONS_H_
#include "base/casts.h"
-#include "compiler_internals.h"
+#include "compiler_ir.h"
+#include "dex_flags.h"
#include "pass_me.h"
+#include "mir_graph.h"
namespace art {
@@ -171,27 +173,6 @@
}
};
-/**
- * @class TypeInference
- * @brief Type inference pass.
- */
-class TypeInference : public PassME {
- public:
- TypeInference()
- : PassME("TypeInference", kRepeatingPreOrderDFSTraversal, "4_post_type_cfg") {
- }
-
- bool Worker(PassDataHolder* data) const {
- DCHECK(data != nullptr);
- PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
- CompilationUnit* c_unit = pass_me_data_holder->c_unit;
- DCHECK(c_unit != nullptr);
- BasicBlock* bb = pass_me_data_holder->bb;
- DCHECK(bb != nullptr);
- return c_unit->mir_graph->InferTypes(bb);
- }
-};
-
class ClassInitCheckElimination : public PassME {
public:
ClassInitCheckElimination()
@@ -279,12 +260,55 @@
};
/**
+ * @class ConstantPropagation
+ * @brief Perform a constant propagation pass.
+ */
+class ConstantPropagation : public PassME {
+ public:
+ ConstantPropagation() : PassME("ConstantPropagation") {
+ }
+
+ void Start(PassDataHolder* data) const {
+ DCHECK(data != nullptr);
+ CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ c_unit->mir_graph->InitializeConstantPropagation();
+ }
+
+ bool Worker(PassDataHolder* data) const {
+ DCHECK(data != nullptr);
+ CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ BasicBlock* bb = down_cast<PassMEDataHolder*>(data)->bb;
+ DCHECK(bb != nullptr);
+ c_unit->mir_graph->DoConstantPropagation(bb);
+ // No need of repeating, so just return false.
+ return false;
+ }
+};
+
+/**
+ * @class MethodUseCount
+ * @brief Count the register uses of the method
+ */
+class MethodUseCount : public PassME {
+ public:
+ MethodUseCount() : PassME("UseCount") {
+ }
+
+ bool Worker(PassDataHolder* data) const;
+
+ bool Gate(const PassDataHolder* data) const;
+};
+
+/**
* @class BasicBlock Optimizations
* @brief Any simple BasicBlock optimization can be put here.
*/
class BBOptimizations : public PassME {
public:
- BBOptimizations() : PassME("BBOptimizations", kNoNodes, "5_post_bbo_cfg") {
+ BBOptimizations()
+ : PassME("BBOptimizations", kNoNodes, kOptimizationBasicBlockChange, "5_post_bbo_cfg") {
}
bool Gate(const PassDataHolder* data) const {
@@ -294,7 +318,63 @@
return ((c_unit->disable_opt & (1 << kBBOpt)) == 0);
}
- void Start(PassDataHolder* data) const;
+ void Start(PassDataHolder* data) const {
+ DCHECK(data != nullptr);
+ CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ c_unit->mir_graph->BasicBlockOptimizationStart();
+
+ /*
+ * This pass has a different ordering depending on the suppress exception,
+ * so do the pass here for now:
+ * - Later, the Start should just change the ordering and we can move the extended
+ * creation into the pass driver's main job with a new iterator
+ */
+ c_unit->mir_graph->BasicBlockOptimization();
+ }
+
+ void End(PassDataHolder* data) const {
+ DCHECK(data != nullptr);
+ CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ c_unit->mir_graph->BasicBlockOptimizationEnd();
+ down_cast<PassMEDataHolder*>(data)->dirty = !c_unit->mir_graph->DfsOrdersUpToDate();
+ }
+};
+
+/**
+ * @class SuspendCheckElimination
+ * @brief Any simple BasicBlock optimization can be put here.
+ */
+class SuspendCheckElimination : public PassME {
+ public:
+ SuspendCheckElimination()
+ : PassME("SuspendCheckElimination", kTopologicalSortTraversal, "6_post_sce_cfg") {
+ }
+
+ bool Gate(const PassDataHolder* data) const {
+ DCHECK(data != nullptr);
+ CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ return c_unit->mir_graph->EliminateSuspendChecksGate();
+ }
+
+ bool Worker(PassDataHolder* data) const {
+ DCHECK(data != nullptr);
+ PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+ CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+ DCHECK(c_unit != nullptr);
+ BasicBlock* bb = pass_me_data_holder->bb;
+ DCHECK(bb != nullptr);
+ return c_unit->mir_graph->EliminateSuspendChecks(bb);
+ }
+
+ void End(PassDataHolder* data) const {
+ DCHECK(data != nullptr);
+ CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ c_unit->mir_graph->EliminateSuspendChecksEnd();
+ }
};
} // namespace art
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index 3b3170e..7edb490 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -311,6 +311,34 @@
// arg[0]: TypeSize (most other vector opcodes have this in vC)
kMirOpPackedArrayPut,
+ // @brief Multiply-add integer.
+ // vA: destination
+ // vB: multiplicand
+ // vC: multiplier
+ // arg[0]: addend
+ kMirOpMaddInt,
+
+ // @brief Multiply-subtract integer.
+ // vA: destination
+ // vB: multiplicand
+ // vC: multiplier
+ // arg[0]: minuend
+ kMirOpMsubInt,
+
+ // @brief Multiply-add long.
+ // vA: destination
+ // vB: multiplicand
+ // vC: multiplier
+ // arg[0]: addend
+ kMirOpMaddLong,
+
+ // @brief Multiply-subtract long.
+ // vA: destination
+ // vB: multiplicand
+ // vC: multiplier
+ // arg[0]: minuend
+ kMirOpMsubLong,
+
kMirOpLast,
};
@@ -527,7 +555,7 @@
* The current recipe is as follows:
* -# Use AnyStore ~= (LoadStore | StoreStore) ~= release barrier before volatile store.
* -# Use AnyAny barrier after volatile store. (StoreLoad is as expensive.)
- * -# Use LoadAny barrier ~= (LoadLoad | LoadStore) ~= acquire barrierafter each volatile load.
+ * -# Use LoadAny barrier ~= (LoadLoad | LoadStore) ~= acquire barrier after each volatile load.
* -# Use StoreStore barrier after all stores but before return from any constructor whose
* class has final fields.
* -# Use NTStoreStore to order non-temporal stores with respect to all later
@@ -606,7 +634,7 @@
};
std::ostream& operator<<(std::ostream& os, const SelectInstructionKind& kind);
-// LIR fixup kinds for Arm
+// LIR fixup kinds for Arm and X86.
enum FixupKind {
kFixupNone,
kFixupLabel, // For labels we just adjust the offset.
@@ -624,6 +652,7 @@
kFixupMovImmHST, // kThumb2MovImm16HST.
kFixupAlign4, // Align to 4-byte boundary.
kFixupA53Erratum835769, // Cortex A53 Erratum 835769.
+ kFixupSwitchTable, // X86_64 packed switch table.
};
std::ostream& operator<<(std::ostream& os, const FixupKind& kind);
diff --git a/compiler/dex/compiler_internals.h b/compiler/dex/compiler_internals.h
deleted file mode 100644
index 2019f0b..0000000
--- a/compiler/dex/compiler_internals.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_DEX_COMPILER_INTERNALS_H_
-#define ART_COMPILER_DEX_COMPILER_INTERNALS_H_
-
-#include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-
-#include "base/logging.h"
-#include "mir_graph.h"
-#include "compiler_ir.h"
-#include "frontend.h" // Debug flags.
-#include "utils.h"
-
-#endif // ART_COMPILER_DEX_COMPILER_INTERNALS_H_
diff --git a/compiler/dex/compiler_ir.cc b/compiler/dex/compiler_ir.cc
index a2b3fe4..7fc1b03 100644
--- a/compiler/dex/compiler_ir.cc
+++ b/compiler/dex/compiler_ir.cc
@@ -16,16 +16,19 @@
#include "compiler_ir.h"
+#include "arch/instruction_set_features.h"
#include "base/dumpable.h"
-#include "backend.h"
-#include "frontend.h"
+#include "dex_flags.h"
+#include "dex/quick/mir_to_lir.h"
+#include "driver/compiler_driver.h"
#include "mir_graph.h"
namespace art {
-CompilationUnit::CompilationUnit(ArenaPool* pool)
- : compiler_driver(nullptr),
- class_linker(nullptr),
+CompilationUnit::CompilationUnit(ArenaPool* pool, InstructionSet isa, CompilerDriver* driver,
+ ClassLinker* linker)
+ : compiler_driver(driver),
+ class_linker(linker),
dex_file(nullptr),
class_loader(nullptr),
class_def_idx(0),
@@ -36,10 +39,8 @@
disable_opt(0),
enable_debug(0),
verbose(false),
- compiler(nullptr),
- instruction_set(kNone),
- target64(false),
- compiler_flip_match(false),
+ instruction_set(isa),
+ target64(Is64BitInstructionSet(isa)),
arena(pool),
arena_stack(pool),
mir_graph(nullptr),
diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h
index 34585c1..0c46d43 100644
--- a/compiler/dex/compiler_ir.h
+++ b/compiler/dex/compiler_ir.h
@@ -17,31 +17,148 @@
#ifndef ART_COMPILER_DEX_COMPILER_IR_H_
#define ART_COMPILER_DEX_COMPILER_IR_H_
+#include "jni.h"
#include <string>
#include <vector>
-#include "compiler_enums.h"
-#include "driver/compiler_driver.h"
-#include "utils/scoped_arena_allocator.h"
#include "base/timing_logger.h"
+#include "invoke_type.h"
+#include "safe_map.h"
#include "utils/arena_allocator.h"
+#include "utils/scoped_arena_allocator.h"
namespace art {
-class Backend;
class ClassLinker;
+class CompilerDriver;
+class Mir2Lir;
class MIRGraph;
-/*
- * TODO: refactoring pass to move these (and other) typedefs towards usage style of runtime to
- * add type safety (see runtime/offsets.h).
+constexpr size_t kOptionStringMaxLength = 2048;
+
+/**
+ * Structure abstracting pass option values, which can be of type string or integer.
*/
-typedef uint32_t DexOffset; // Dex offset in code units.
-typedef uint16_t NarrowDexOffset; // For use in structs, Dex offsets range from 0 .. 0xffff.
-typedef uint32_t CodeOffset; // Native code offset in bytes.
+struct OptionContent {
+ OptionContent(const OptionContent& option) :
+ type(option.type), container(option.container, option.type) {}
+
+ explicit OptionContent(const char* value) :
+ type(kString), container(value) {}
+
+ explicit OptionContent(int value) :
+ type(kInteger), container(value) {}
+
+ explicit OptionContent(int64_t value) :
+ type(kInteger), container(value) {}
+
+ ~OptionContent() {
+ if (type == kString) {
+ container.StringDelete();
+ }
+ }
+
+ /**
+ * Allows for a transparent display of the option content.
+ */
+ friend std::ostream& operator<<(std::ostream& out, const OptionContent& option) {
+ if (option.type == kString) {
+ out << option.container.s;
+ } else {
+ out << option.container.i;
+ }
+
+ return out;
+ }
+
+ inline const char* GetString() const {
+ return container.s;
+ }
+
+ inline int64_t GetInteger() const {
+ return container.i;
+ }
+
+ /**
+ * @brief Used to compare a string option value to a given @p value.
+ * @details Will return whether the internal string option is equal to
+ * the parameter @p value. It will return false if the type of the
+ * object is not a string.
+ * @param value The string to compare to.
+ * @return Returns whether the internal string option is equal to the
+ * parameter @p value.
+ */
+ inline bool Equals(const char* value) const {
+ DCHECK(value != nullptr);
+ if (type != kString) {
+ return false;
+ }
+ return !strncmp(container.s, value, kOptionStringMaxLength);
+ }
+
+ /**
+ * @brief Used to compare an integer option value to a given @p value.
+ * @details Will return whether the internal integer option is equal to
+ * the parameter @p value. It will return false if the type of the
+ * object is not an integer.
+ * @param value The integer to compare to.
+ * @return Returns whether the internal integer option is equal to the
+ * parameter @p value.
+ */
+ inline bool Equals(int64_t value) const {
+ if (type != kInteger) {
+ return false;
+ }
+ return container.i == value;
+ }
+
+ /**
+ * Describes the type of parameters allowed as option values.
+ */
+ enum OptionType {
+ kString = 0,
+ kInteger
+ };
+
+ OptionType type;
+
+ private:
+ /**
+ * Union containing the option value of either type.
+ */
+ union OptionContainer {
+ explicit OptionContainer(const OptionContainer& c, OptionType t) {
+ if (t == kString) {
+ DCHECK(c.s != nullptr);
+ s = strndup(c.s, kOptionStringMaxLength);
+ } else {
+ i = c.i;
+ }
+ }
+
+ explicit OptionContainer(const char* value) {
+ DCHECK(value != nullptr);
+ s = strndup(value, kOptionStringMaxLength);
+ }
+
+ explicit OptionContainer(int64_t value) : i(value) {}
+ ~OptionContainer() {}
+
+ void StringDelete() {
+ if (s != nullptr) {
+ free(s);
+ }
+ }
+
+ char* s;
+ int64_t i;
+ };
+
+ OptionContainer container;
+};
struct CompilationUnit {
- explicit CompilationUnit(ArenaPool* pool);
+ CompilationUnit(ArenaPool* pool, InstructionSet isa, CompilerDriver* driver, ClassLinker* linker);
~CompilationUnit();
void StartTimingSplit(const char* label);
@@ -52,37 +169,27 @@
* Fields needed/generated by common frontend and generally used throughout
* the compiler.
*/
- CompilerDriver* compiler_driver;
- ClassLinker* class_linker; // Linker to resolve fields and methods.
- const DexFile* dex_file; // DexFile containing the method being compiled.
- jobject class_loader; // compiling method's class loader.
- uint16_t class_def_idx; // compiling method's defining class definition index.
- uint32_t method_idx; // compiling method's index into method_ids of DexFile.
- uint32_t access_flags; // compiling method's access flags.
- InvokeType invoke_type; // compiling method's invocation type.
- const char* shorty; // compiling method's shorty.
- uint32_t disable_opt; // opt_control_vector flags.
- uint32_t enable_debug; // debugControlVector flags.
+ CompilerDriver* const compiler_driver;
+ ClassLinker* const class_linker; // Linker to resolve fields and methods.
+ const DexFile* dex_file; // DexFile containing the method being compiled.
+ jobject class_loader; // compiling method's class loader.
+ uint16_t class_def_idx; // compiling method's defining class definition index.
+ uint32_t method_idx; // compiling method's index into method_ids of DexFile.
+ uint32_t access_flags; // compiling method's access flags.
+ InvokeType invoke_type; // compiling method's invocation type.
+ const char* shorty; // compiling method's shorty.
+ uint32_t disable_opt; // opt_control_vector flags.
+ uint32_t enable_debug; // debugControlVector flags.
bool verbose;
- const Compiler* compiler;
- InstructionSet instruction_set;
- bool target64;
-
- const InstructionSetFeatures* GetInstructionSetFeatures() {
- return compiler_driver->GetInstructionSetFeatures();
- }
-
- // If non-empty, apply optimizer/debug flags only to matching methods.
- std::string compiler_method_match;
- // Flips sense of compiler_method_match - apply flags if doesn't match.
- bool compiler_flip_match;
+ const InstructionSet instruction_set;
+ const bool target64;
// TODO: move memory management to mir_graph, or just switch to using standard containers.
ArenaAllocator arena;
ArenaStack arena_stack; // Arenas for ScopedArenaAllocator.
std::unique_ptr<MIRGraph> mir_graph; // MIR container.
- std::unique_ptr<Backend> cg; // Target-specific codegen.
+ std::unique_ptr<Mir2Lir> cg; // Target-specific codegen.
TimingLogger timings;
bool print_pass; // Do we want to print a pass or not?
@@ -93,7 +200,7 @@
* default settings have been changed. The key is simply the option string without
* the pass name.
*/
- SafeMap<const std::string, int> overridden_pass_options;
+ SafeMap<const std::string, const OptionContent> overridden_pass_options;
};
} // namespace art
diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h
index 9f17a3e..2a06cec 100644
--- a/compiler/dex/dataflow_iterator.h
+++ b/compiler/dex/dataflow_iterator.h
@@ -17,7 +17,7 @@
#ifndef ART_COMPILER_DEX_DATAFLOW_ITERATOR_H_
#define ART_COMPILER_DEX_DATAFLOW_ITERATOR_H_
-#include "compiler_ir.h"
+#include "base/logging.h"
#include "mir_graph.h"
namespace art {
diff --git a/compiler/dex/dex_flags.h b/compiler/dex/dex_flags.h
new file mode 100644
index 0000000..eaf272b
--- /dev/null
+++ b/compiler/dex/dex_flags.h
@@ -0,0 +1,67 @@
+/*
+ * 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_DEX_DEX_FLAGS_H_
+#define ART_COMPILER_DEX_DEX_FLAGS_H_
+
+namespace art {
+
+// Suppress optimization if corresponding bit set.
+enum OptControlVector {
+ kLoadStoreElimination = 0,
+ kLoadHoisting,
+ kSuppressLoads,
+ kNullCheckElimination,
+ kClassInitCheckElimination,
+ kGlobalValueNumbering,
+ kLocalValueNumbering,
+ kPromoteRegs,
+ kTrackLiveTemps,
+ kSafeOptimizations,
+ kBBOpt,
+ kSuspendCheckElimination,
+ kMatch,
+ kPromoteCompilerTemps,
+ kBranchFusing,
+ kSuppressExceptionEdges,
+ kSuppressMethodInlining,
+};
+
+// Force code generation paths for testing.
+enum DebugControlVector {
+ kDebugVerbose,
+ kDebugDumpCFG,
+ kDebugSlowFieldPath,
+ kDebugSlowInvokePath,
+ kDebugSlowStringPath,
+ kDebugSlowTypePath,
+ kDebugSlowestFieldPath,
+ kDebugSlowestStringPath,
+ kDebugExerciseResolveMethod,
+ kDebugVerifyDataflow,
+ kDebugShowMemoryUsage,
+ kDebugShowNops,
+ kDebugCountOpcodes,
+ kDebugDumpCheckStats,
+ kDebugShowSummaryMemoryUsage,
+ kDebugShowFilterStats,
+ kDebugTimings,
+ kDebugCodegenDump
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_DEX_DEX_FLAGS_H_
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 205a521..f7968c2 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -120,6 +120,22 @@
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT_QUICK, false);
break;
+ case Instruction::IGET_BOOLEAN:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BOOLEAN_QUICK, false);
+ break;
+
+ case Instruction::IGET_BYTE:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BYTE_QUICK, false);
+ break;
+
+ case Instruction::IGET_CHAR:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_CHAR_QUICK, false);
+ break;
+
+ case Instruction::IGET_SHORT:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_SHORT_QUICK, false);
+ break;
+
case Instruction::IPUT:
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_QUICK, true);
break;
diff --git a/compiler/llvm/utils_llvm.h b/compiler/dex/dex_types.h
similarity index 67%
rename from compiler/llvm/utils_llvm.h
rename to compiler/dex/dex_types.h
index a606b91..f485c1c 100644
--- a/compiler/llvm/utils_llvm.h
+++ b/compiler/dex/dex_types.h
@@ -14,19 +14,14 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_LLVM_UTILS_LLVM_H_
-#define ART_COMPILER_LLVM_UTILS_LLVM_H_
-
-#include <llvm/Analysis/Verifier.h>
+#ifndef ART_COMPILER_DEX_DEX_TYPES_H_
+#define ART_COMPILER_DEX_DEX_TYPES_H_
namespace art {
-#ifndef NDEBUG
-#define VERIFY_LLVM_FUNCTION(func) ::llvm::verifyFunction(func, ::llvm::AbortProcessAction)
-#else
-#define VERIFY_LLVM_FUNCTION(func)
-#endif
+typedef uint32_t DexOffset; // Dex offset in code units.
+typedef uint16_t NarrowDexOffset; // For use in structs, Dex offsets range from 0 .. 0xffff.
} // namespace art
-#endif // ART_COMPILER_LLVM_UTILS_LLVM_H_
+#endif // ART_COMPILER_DEX_DEX_TYPES_H_
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
deleted file mode 100644
index 3f6231c..0000000
--- a/compiler/dex/frontend.cc
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "frontend.h"
-
-#include <cstdint>
-
-#include "backend.h"
-#include "base/dumpable.h"
-#include "compiler.h"
-#include "compiler_internals.h"
-#include "driver/compiler_driver.h"
-#include "driver/compiler_options.h"
-#include "mirror/object.h"
-#include "pass_driver_me_opts.h"
-#include "runtime.h"
-#include "base/logging.h"
-#include "base/timing_logger.h"
-#include "driver/compiler_options.h"
-#include "dex/quick/dex_file_to_method_inliner_map.h"
-
-namespace art {
-
-/* Default optimizer/debug setting for the compiler. */
-static uint32_t kCompilerOptimizerDisableFlags = 0 | // Disable specific optimizations
- // (1 << kLoadStoreElimination) |
- // (1 << kLoadHoisting) |
- // (1 << kSuppressLoads) |
- // (1 << kNullCheckElimination) |
- // (1 << kClassInitCheckElimination) |
- // (1 << kGlobalValueNumbering) |
- // (1 << kLocalValueNumbering) |
- // (1 << kPromoteRegs) |
- // (1 << kTrackLiveTemps) |
- // (1 << kSafeOptimizations) |
- // (1 << kBBOpt) |
- // (1 << kMatch) |
- // (1 << kPromoteCompilerTemps) |
- // (1 << kSuppressExceptionEdges) |
- // (1 << kSuppressMethodInlining) |
- 0;
-
-static uint32_t kCompilerDebugFlags = 0 | // Enable debug/testing modes
- // (1 << kDebugDisplayMissingTargets) |
- // (1 << kDebugVerbose) |
- // (1 << kDebugDumpCFG) |
- // (1 << kDebugSlowFieldPath) |
- // (1 << kDebugSlowInvokePath) |
- // (1 << kDebugSlowStringPath) |
- // (1 << kDebugSlowestFieldPath) |
- // (1 << kDebugSlowestStringPath) |
- // (1 << kDebugExerciseResolveMethod) |
- // (1 << kDebugVerifyDataflow) |
- // (1 << kDebugShowMemoryUsage) |
- // (1 << kDebugShowNops) |
- // (1 << kDebugCountOpcodes) |
- // (1 << kDebugDumpCheckStats) |
- // (1 << kDebugDumpBitcodeFile) |
- // (1 << kDebugVerifyBitcode) |
- // (1 << kDebugShowSummaryMemoryUsage) |
- // (1 << kDebugShowFilterStats) |
- // (1 << kDebugTimings) |
- // (1 << kDebugCodegenDump) |
- 0;
-
-static CompiledMethod* CompileMethod(CompilerDriver& driver,
- const Compiler* compiler,
- const DexFile::CodeItem* code_item,
- uint32_t access_flags, InvokeType invoke_type,
- uint16_t class_def_idx, uint32_t method_idx,
- jobject class_loader, const DexFile& dex_file,
- void* llvm_compilation_unit) {
- VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "...";
- if (Compiler::IsPathologicalCase(*code_item, method_idx, dex_file)) {
- return nullptr;
- }
-
- DCHECK(driver.GetCompilerOptions().IsCompilationEnabled());
-
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- CompilationUnit cu(driver.GetArenaPool());
-
- cu.compiler_driver = &driver;
- cu.class_linker = class_linker;
- cu.instruction_set = driver.GetInstructionSet();
- if (cu.instruction_set == kArm) {
- cu.instruction_set = kThumb2;
- }
- cu.target64 = Is64BitInstructionSet(cu.instruction_set);
- cu.compiler = compiler;
- // TODO: Mips64 is not yet implemented.
- CHECK((cu.instruction_set == kThumb2) ||
- (cu.instruction_set == kArm64) ||
- (cu.instruction_set == kX86) ||
- (cu.instruction_set == kX86_64) ||
- (cu.instruction_set == kMips));
-
- // TODO: set this from command line
- cu.compiler_flip_match = false;
- bool use_match = !cu.compiler_method_match.empty();
- bool match = use_match && (cu.compiler_flip_match ^
- (PrettyMethod(method_idx, dex_file).find(cu.compiler_method_match) != std::string::npos));
- if (!use_match || match) {
- cu.disable_opt = kCompilerOptimizerDisableFlags;
- cu.enable_debug = kCompilerDebugFlags;
- cu.verbose = VLOG_IS_ON(compiler) ||
- (cu.enable_debug & (1 << kDebugVerbose));
- }
-
- if (driver.GetCompilerOptions().HasVerboseMethods()) {
- cu.verbose = driver.GetCompilerOptions().IsVerboseMethod(PrettyMethod(method_idx, dex_file));
- }
-
- if (cu.verbose) {
- cu.enable_debug |= (1 << kDebugCodegenDump);
- }
-
- /*
- * TODO: rework handling of optimization and debug flags. Should we split out
- * MIR and backend flags? Need command-line setting as well.
- */
-
- compiler->InitCompilationUnit(cu);
-
- cu.StartTimingSplit("BuildMIRGraph");
- cu.mir_graph.reset(new MIRGraph(&cu, &cu.arena));
-
- /*
- * After creation of the MIR graph, also create the code generator.
- * The reason we do this is that optimizations on the MIR graph may need to get information
- * that is only available if a CG exists.
- */
- cu.cg.reset(compiler->GetCodeGenerator(&cu, llvm_compilation_unit));
-
- /* Gathering opcode stats? */
- if (kCompilerDebugFlags & (1 << kDebugCountOpcodes)) {
- cu.mir_graph->EnableOpcodeCounting();
- }
-
- /* Build the raw MIR graph */
- cu.mir_graph->InlineMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx,
- class_loader, dex_file);
-
- if (!compiler->CanCompileMethod(method_idx, dex_file, &cu)) {
- VLOG(compiler) << cu.instruction_set << ": Cannot compile method : "
- << PrettyMethod(method_idx, dex_file);
- return nullptr;
- }
-
- cu.NewTimingSplit("MIROpt:CheckFilters");
- std::string skip_message;
- if (cu.mir_graph->SkipCompilation(&skip_message)) {
- VLOG(compiler) << cu.instruction_set << ": Skipping method : "
- << PrettyMethod(method_idx, dex_file) << " Reason = " << skip_message;
- return nullptr;
- }
-
- /* Create the pass driver and launch it */
- PassDriverMEOpts pass_driver(&cu);
- pass_driver.Launch();
-
- /* For non-leaf methods check if we should skip compilation when the profiler is enabled. */
- if (cu.compiler_driver->ProfilePresent()
- && !cu.mir_graph->MethodIsLeaf()
- && cu.mir_graph->SkipCompilationByName(PrettyMethod(method_idx, dex_file))) {
- return nullptr;
- }
-
- if (cu.enable_debug & (1 << kDebugDumpCheckStats)) {
- cu.mir_graph->DumpCheckStats();
- }
-
- if (kCompilerDebugFlags & (1 << kDebugCountOpcodes)) {
- cu.mir_graph->ShowOpcodeStats();
- }
-
- /* Reassociate sreg names with original Dalvik vreg names. */
- cu.mir_graph->RemapRegLocations();
-
- /* Free Arenas from the cu.arena_stack for reuse by the cu.arena in the codegen. */
- if (cu.enable_debug & (1 << kDebugShowMemoryUsage)) {
- if (cu.arena_stack.PeakBytesAllocated() > 1 * 1024 * 1024) {
- MemStats stack_stats(cu.arena_stack.GetPeakStats());
- LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(stack_stats);
- }
- }
- cu.arena_stack.Reset();
-
- CompiledMethod* result = NULL;
-
- if (cu.mir_graph->PuntToInterpreter()) {
- VLOG(compiler) << cu.instruction_set << ": Punted method to interpreter: "
- << PrettyMethod(method_idx, dex_file);
- return nullptr;
- }
-
- cu.cg->Materialize();
-
- cu.NewTimingSplit("Dedupe"); /* deduping takes up the vast majority of time in GetCompiledMethod(). */
- result = cu.cg->GetCompiledMethod();
- cu.NewTimingSplit("Cleanup");
-
- if (result) {
- VLOG(compiler) << cu.instruction_set << ": Compiled " << PrettyMethod(method_idx, dex_file);
- } else {
- VLOG(compiler) << cu.instruction_set << ": Deferred " << PrettyMethod(method_idx, dex_file);
- }
-
- if (cu.enable_debug & (1 << kDebugShowMemoryUsage)) {
- if (cu.arena.BytesAllocated() > (1 * 1024 *1024)) {
- MemStats mem_stats(cu.arena.GetMemStats());
- LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(mem_stats);
- }
- }
-
- if (cu.enable_debug & (1 << kDebugShowSummaryMemoryUsage)) {
- LOG(INFO) << "MEMINFO " << cu.arena.BytesAllocated() << " " << cu.mir_graph->GetNumBlocks()
- << " " << PrettyMethod(method_idx, dex_file);
- }
-
- cu.EndTiming();
- driver.GetTimingsLogger()->AddLogger(cu.timings);
- return result;
-}
-
-CompiledMethod* CompileOneMethod(CompilerDriver* driver,
- const Compiler* compiler,
- const DexFile::CodeItem* code_item,
- uint32_t access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const DexFile& dex_file,
- void* compilation_unit) {
- return CompileMethod(*driver, compiler, code_item, access_flags, invoke_type, class_def_idx,
- method_idx, class_loader, dex_file, compilation_unit);
-}
-
-} // namespace art
diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h
deleted file mode 100644
index bed3b97..0000000
--- a/compiler/dex/frontend.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_DEX_FRONTEND_H_
-#define ART_COMPILER_DEX_FRONTEND_H_
-
-#include "dex_file.h"
-#include "invoke_type.h"
-
-namespace art {
-
-class CompiledMethod;
-class Compiler;
-class CompilerDriver;
-
-/*
- * Assembly is an iterative process, and usually terminates within
- * two or three passes. This should be high enough to handle bizarre
- * cases, but detect an infinite loop bug.
- */
-#define MAX_ASSEMBLER_RETRIES 50
-
-// Suppress optimization if corresponding bit set.
-enum opt_control_vector {
- kLoadStoreElimination = 0,
- kLoadHoisting,
- kSuppressLoads,
- kNullCheckElimination,
- kClassInitCheckElimination,
- kGlobalValueNumbering,
- kLocalValueNumbering,
- kPromoteRegs,
- kTrackLiveTemps,
- kSafeOptimizations,
- kBBOpt,
- kMatch,
- kPromoteCompilerTemps,
- kBranchFusing,
- kSuppressExceptionEdges,
- kSuppressMethodInlining,
-};
-
-// Force code generation paths for testing.
-enum debugControlVector {
- kDebugVerbose,
- kDebugDumpCFG,
- kDebugSlowFieldPath,
- kDebugSlowInvokePath,
- kDebugSlowStringPath,
- kDebugSlowTypePath,
- kDebugSlowestFieldPath,
- kDebugSlowestStringPath,
- kDebugExerciseResolveMethod,
- kDebugVerifyDataflow,
- kDebugShowMemoryUsage,
- kDebugShowNops,
- kDebugCountOpcodes,
- kDebugDumpCheckStats,
- kDebugDumpBitcodeFile,
- kDebugVerifyBitcode,
- kDebugShowSummaryMemoryUsage,
- kDebugShowFilterStats,
- kDebugTimings,
- kDebugCodegenDump
-};
-
-CompiledMethod* CompileOneMethod(CompilerDriver* driver,
- const Compiler* compiler,
- const DexFile::CodeItem* code_item,
- uint32_t access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const DexFile& dex_file,
- void* compilation_unit);
-
-} // namespace art
-
-#endif // ART_COMPILER_DEX_FRONTEND_H_
diff --git a/compiler/dex/global_value_numbering.cc b/compiler/dex/global_value_numbering.cc
index 578952b..a8fd812 100644
--- a/compiler/dex/global_value_numbering.cc
+++ b/compiler/dex/global_value_numbering.cc
@@ -15,6 +15,8 @@
*/
#include "global_value_numbering.h"
+
+#include "base/stl_util.h"
#include "local_value_numbering.h"
namespace art {
diff --git a/compiler/dex/global_value_numbering.h b/compiler/dex/global_value_numbering.h
index d72144a..cdafc68 100644
--- a/compiler/dex/global_value_numbering.h
+++ b/compiler/dex/global_value_numbering.h
@@ -17,8 +17,11 @@
#ifndef ART_COMPILER_DEX_GLOBAL_VALUE_NUMBERING_H_
#define ART_COMPILER_DEX_GLOBAL_VALUE_NUMBERING_H_
+#include "base/logging.h"
#include "base/macros.h"
-#include "compiler_internals.h"
+#include "mir_graph.h"
+#include "compiler_ir.h"
+#include "dex_flags.h"
#include "utils/arena_object.h"
namespace art {
@@ -252,7 +255,7 @@
};
std::ostream& operator<<(std::ostream& os, const GlobalValueNumbering::Mode& rhs);
-inline void GlobalValueNumbering::StartPostProcessing() {
+inline void GlobalValueNumbering::StartPostProcessing() {
DCHECK(Good());
DCHECK_EQ(mode_, kModeGvn);
mode_ = kModeGvnPostProcessing;
diff --git a/compiler/dex/global_value_numbering_test.cc b/compiler/dex/global_value_numbering_test.cc
index 7e3b4d8..f71b7ae 100644
--- a/compiler/dex/global_value_numbering_test.cc
+++ b/compiler/dex/global_value_numbering_test.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "compiler_internals.h"
+#include "base/logging.h"
#include "dataflow_iterator.h"
#include "dataflow_iterator-inl.h"
#include "dex/mir_field_info.h"
@@ -215,7 +215,6 @@
bb->data_flow_info->live_in_v = live_in_v_;
}
}
- cu_.mir_graph->num_blocks_ = count;
ASSERT_EQ(count, cu_.mir_graph->block_list_.size());
cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1];
ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type);
@@ -338,7 +337,7 @@
GlobalValueNumberingTest()
: pool_(),
- cu_(&pool_),
+ cu_(&pool_, kRuntimeISA, nullptr, nullptr),
mir_count_(0u),
mirs_(nullptr),
ssa_reps_(),
diff --git a/compiler/dex/local_value_numbering.h b/compiler/dex/local_value_numbering.h
index 9b89c95..aef8c6d 100644
--- a/compiler/dex/local_value_numbering.h
+++ b/compiler/dex/local_value_numbering.h
@@ -19,7 +19,7 @@
#include <memory>
-#include "compiler_internals.h"
+#include "base/logging.h"
#include "global_value_numbering.h"
#include "utils/arena_object.h"
#include "utils/dex_instruction_utils.h"
diff --git a/compiler/dex/local_value_numbering_test.cc b/compiler/dex/local_value_numbering_test.cc
index 0fcb584..c894892 100644
--- a/compiler/dex/local_value_numbering_test.cc
+++ b/compiler/dex/local_value_numbering_test.cc
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include "compiler_internals.h"
#include "dex/mir_field_info.h"
#include "global_value_numbering.h"
#include "local_value_numbering.h"
@@ -202,7 +201,7 @@
LocalValueNumberingTest()
: pool_(),
- cu_(&pool_),
+ cu_(&pool_, kRuntimeISA, nullptr, nullptr),
mir_count_(0u),
mirs_(nullptr),
ssa_reps_(),
diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc
index 7b53b14..473196b 100644
--- a/compiler/dex/mir_analysis.cc
+++ b/compiler/dex/mir_analysis.cc
@@ -17,15 +17,18 @@
#include <algorithm>
#include <memory>
-#include "compiler_internals.h"
+#include "base/logging.h"
#include "dataflow_iterator-inl.h"
-#include "dex_instruction.h"
+#include "compiler_ir.h"
+#include "dex_flags.h"
#include "dex_instruction-inl.h"
#include "dex/mir_field_info.h"
#include "dex/verified_method.h"
#include "dex/quick/dex_file_method_inliner.h"
#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
+#include "driver/dex_compilation_unit.h"
#include "utils/scoped_arena_containers.h"
namespace art {
@@ -1151,7 +1154,7 @@
skip_compilation = true;
*skip_message = "Huge method: " + std::to_string(GetNumDalvikInsns());
// If we're got a huge number of basic blocks, don't bother with further analysis.
- if (static_cast<size_t>(num_blocks_) > (compiler_options.GetHugeMethodThreshold() / 2)) {
+ if (static_cast<size_t>(GetNumBlocks()) > (compiler_options.GetHugeMethodThreshold() / 2)) {
return true;
}
} else if (compiler_options.IsLargeMethod(GetNumDalvikInsns()) &&
diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc
index 5b7ac3c..a1f4294 100644
--- a/compiler/dex/mir_dataflow.cc
+++ b/compiler/dex/mir_dataflow.cc
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include "compiler_internals.h"
#include "local_value_numbering.h"
#include "dataflow_iterator-inl.h"
@@ -897,6 +896,18 @@
// 120 MirOpPackedArrayPut
DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
+
+ // 121 MirOpMaddInt
+ DF_FORMAT_EXTENDED,
+
+ // 122 MirOpMsubInt
+ DF_FORMAT_EXTENDED,
+
+ // 123 MirOpMaddLong
+ DF_FORMAT_EXTENDED,
+
+ // 124 MirOpMsubLong
+ DF_FORMAT_EXTENDED,
};
/* Return the base virtual register for a SSA name */
@@ -906,7 +917,7 @@
/* Any register that is used before being defined is considered live-in */
void MIRGraph::HandleLiveInUse(ArenaBitVector* use_v, ArenaBitVector* def_v,
- ArenaBitVector* live_in_v, int dalvik_reg_id) {
+ ArenaBitVector* live_in_v, int dalvik_reg_id) {
use_v->SetBit(dalvik_reg_id);
if (!def_v->IsBitSet(dalvik_reg_id)) {
live_in_v->SetBit(dalvik_reg_id);
@@ -919,8 +930,8 @@
}
void MIRGraph::HandleExtended(ArenaBitVector* use_v, ArenaBitVector* def_v,
- ArenaBitVector* live_in_v,
- const MIR::DecodedInstruction& d_insn) {
+ ArenaBitVector* live_in_v,
+ const MIR::DecodedInstruction& d_insn) {
// For vector MIRs, vC contains type information
bool is_vector_type_wide = false;
int type_size = d_insn.vC >> 16;
@@ -951,6 +962,24 @@
HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB + 1);
}
break;
+ case kMirOpMaddInt:
+ case kMirOpMsubInt:
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB);
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vC);
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn.arg[0]);
+ HandleDef(def_v, d_insn.vA);
+ break;
+ case kMirOpMaddLong:
+ case kMirOpMsubLong:
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB);
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB + 1);
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vC);
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vC + 1);
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn.arg[0]);
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn.arg[0] + 1);
+ HandleDef(def_v, d_insn.vA);
+ HandleDef(def_v, d_insn.vA + 1);
+ break;
default:
LOG(ERROR) << "Unexpected Extended Opcode " << d_insn.opcode;
break;
@@ -1139,6 +1168,28 @@
HandleSSAUse(mir->ssa_rep->uses, d_insn.vB + 1, 1);
}
break;
+ case kMirOpMaddInt:
+ case kMirOpMsubInt:
+ AllocateSSAUseData(mir, 3);
+ HandleSSAUse(mir->ssa_rep->uses, d_insn.vB, 0);
+ HandleSSAUse(mir->ssa_rep->uses, d_insn.vC, 1);
+ HandleSSAUse(mir->ssa_rep->uses, d_insn.arg[0], 2);
+ AllocateSSADefData(mir, 1);
+ HandleSSADef(mir->ssa_rep->defs, d_insn.vA, 0);
+ break;
+ case kMirOpMaddLong:
+ case kMirOpMsubLong:
+ AllocateSSAUseData(mir, 6);
+ HandleSSAUse(mir->ssa_rep->uses, d_insn.vB, 0);
+ HandleSSAUse(mir->ssa_rep->uses, d_insn.vB + 1, 1);
+ HandleSSAUse(mir->ssa_rep->uses, d_insn.vC, 2);
+ HandleSSAUse(mir->ssa_rep->uses, d_insn.vC + 1, 3);
+ HandleSSAUse(mir->ssa_rep->uses, d_insn.arg[0], 4);
+ HandleSSAUse(mir->ssa_rep->uses, d_insn.arg[0] + 1, 5);
+ AllocateSSADefData(mir, 2);
+ HandleSSADef(mir->ssa_rep->defs, d_insn.vA, 0);
+ HandleSSADef(mir->ssa_rep->defs, d_insn.vA + 1, 1);
+ break;
default:
LOG(ERROR) << "Missing case for extended MIR: " << mir->dalvikInsn.opcode;
break;
@@ -1147,11 +1198,30 @@
/* Entry function to convert a block into SSA representation */
bool MIRGraph::DoSSAConversion(BasicBlock* bb) {
- MIR* mir;
-
if (bb->data_flow_info == NULL) return false;
- for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+ /*
+ * Pruned SSA form: Insert phi nodes for each dalvik register marked in phi_node_blocks
+ * only if the dalvik register is in the live-in set.
+ */
+ BasicBlockId bb_id = bb->id;
+ for (int dalvik_reg = GetNumOfCodeAndTempVRs() - 1; dalvik_reg >= 0; dalvik_reg--) {
+ if (temp_.ssa.phi_node_blocks[dalvik_reg]->IsBitSet(bb_id)) {
+ if (!bb->data_flow_info->live_in_v->IsBitSet(dalvik_reg)) {
+ /* Variable will be clobbered before being used - no need for phi */
+ vreg_to_ssa_map_[dalvik_reg] = INVALID_SREG;
+ continue;
+ }
+ MIR *phi = NewMIR();
+ phi->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpPhi);
+ phi->dalvikInsn.vA = dalvik_reg;
+ phi->offset = bb->start_offset;
+ phi->m_unit_index = 0; // Arbitrarily assign all Phi nodes to outermost method.
+ bb->PrependMIR(phi);
+ }
+ }
+
+ for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
mir->ssa_rep =
static_cast<struct SSARepresentation *>(arena_->Alloc(sizeof(SSARepresentation),
kArenaAllocDFInfo));
@@ -1343,7 +1413,7 @@
* counts explicitly used s_regs. A later phase will add implicit
* counts for things such as Method*, null-checked references, etc.
*/
-void MIRGraph::CountUses(class BasicBlock* bb) {
+void MIRGraph::CountUses(BasicBlock* bb) {
if (bb->block_type != kDalvikByteCode) {
return;
}
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index 023abca..92f960e 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -18,15 +18,19 @@
#include <inttypes.h>
#include <queue>
+#include <unistd.h>
#include "base/bit_vector-inl.h"
+#include "base/logging.h"
#include "base/stl_util.h"
-#include "compiler_internals.h"
+#include "base/stringprintf.h"
+#include "compiler_ir.h"
#include "dex_file-inl.h"
+#include "dex_flags.h"
#include "dex_instruction-inl.h"
-#include "dex/global_value_numbering.h"
-#include "dex/quick/dex_file_to_method_inliner_map.h"
-#include "dex/quick/dex_file_method_inliner.h"
+#include "driver/compiler_driver.h"
+#include "driver/dex_compilation_unit.h"
+#include "dex/quick/quick_compiler.h"
#include "leb128.h"
#include "pass_driver_me_post_opt.h"
#include "stack.h"
@@ -70,6 +74,10 @@
"MemBarrier",
"PackedArrayGet",
"PackedArrayPut",
+ "MaddInt",
+ "MsubInt",
+ "MaddLong",
+ "MsubLong",
};
MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena)
@@ -87,6 +95,9 @@
num_reachable_blocks_(0),
max_num_reachable_blocks_(0),
dfs_orders_up_to_date_(false),
+ domination_up_to_date_(false),
+ mir_ssa_rep_up_to_date_(false),
+ topological_order_up_to_date_(false),
dfs_order_(arena->Adapter(kArenaAllocDfsPreOrder)),
dfs_post_order_(arena->Adapter(kArenaAllocDfsPostOrder)),
dom_post_order_traversal_(arena->Adapter(kArenaAllocDomPostOrder)),
@@ -101,9 +112,7 @@
try_block_addr_(NULL),
entry_block_(NULL),
exit_block_(NULL),
- num_blocks_(0),
current_code_item_(NULL),
- dex_pc_to_block_map_(arena->Adapter()),
m_units_(arena->Adapter()),
method_stack_(arena->Adapter()),
current_method_(kInvalidEntry),
@@ -127,7 +136,7 @@
ifield_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)),
sfield_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)),
method_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)),
- gen_suspend_test_list_(arena->Adapter()) {
+ suspend_checks_in_loops_(nullptr) {
memset(&temp_, 0, sizeof(temp_));
use_counts_.reserve(256);
raw_use_counts_.reserve(256);
@@ -258,31 +267,14 @@
DCHECK(insn != orig_block->first_mir_insn);
DCHECK(insn == bottom_block->first_mir_insn);
DCHECK_EQ(insn->offset, bottom_block->start_offset);
- DCHECK_EQ(dex_pc_to_block_map_[insn->offset], orig_block->id);
// Scan the "bottom" instructions, remapping them to the
// newly created "bottom" block.
MIR* p = insn;
p->bb = bottom_block->id;
- dex_pc_to_block_map_[p->offset] = bottom_block->id;
while (p != bottom_block->last_mir_insn) {
p = p->next;
DCHECK(p != nullptr);
p->bb = bottom_block->id;
- int opcode = p->dalvikInsn.opcode;
- /*
- * Some messiness here to ensure that we only enter real opcodes and only the
- * first half of a potentially throwing instruction that has been split into
- * CHECK and work portions. Since the 2nd half of a split operation is always
- * the first in a BasicBlock, we can't hit it here.
- */
- if ((opcode == kMirOpCheck) || !MIR::DecodedInstruction::IsPseudoMirOp(opcode)) {
- BasicBlockId mapped_id = dex_pc_to_block_map_[p->offset];
- // At first glance the instructions should all be mapped to orig_block.
- // However, multiple instructions may correspond to the same dex, hence an earlier
- // instruction may have already moved the mapping for dex to bottom_block.
- DCHECK((mapped_id == orig_block->id) || (mapped_id == bottom_block->id));
- dex_pc_to_block_map_[p->offset] = bottom_block->id;
- }
}
return bottom_block;
@@ -297,12 +289,13 @@
* Utilizes a map for fast lookup of the typical cases.
*/
BasicBlock* MIRGraph::FindBlock(DexOffset code_offset, bool create,
- BasicBlock** immed_pred_block_p) {
+ BasicBlock** immed_pred_block_p,
+ ScopedArenaVector<uint16_t>* dex_pc_to_block_map) {
if (code_offset >= current_code_item_->insns_size_in_code_units_) {
return nullptr;
}
- int block_id = dex_pc_to_block_map_[code_offset];
+ int block_id = (*dex_pc_to_block_map)[code_offset];
BasicBlock* bb = GetBasicBlock(block_id);
if ((bb != nullptr) && (bb->start_offset == code_offset)) {
@@ -317,19 +310,46 @@
if (bb != nullptr) {
// The target exists somewhere in an existing block.
- return SplitBlock(code_offset, bb, bb == *immed_pred_block_p ? immed_pred_block_p : nullptr);
+ BasicBlock* bottom_block = SplitBlock(code_offset, bb, bb == *immed_pred_block_p ? immed_pred_block_p : nullptr);
+ DCHECK(bottom_block != nullptr);
+ MIR* p = bottom_block->first_mir_insn;
+ BasicBlock* orig_block = bb;
+ DCHECK_EQ((*dex_pc_to_block_map)[p->offset], orig_block->id);
+ // Scan the "bottom" instructions, remapping them to the
+ // newly created "bottom" block.
+ (*dex_pc_to_block_map)[p->offset] = bottom_block->id;
+ while (p != bottom_block->last_mir_insn) {
+ p = p->next;
+ DCHECK(p != nullptr);
+ int opcode = p->dalvikInsn.opcode;
+ /*
+ * Some messiness here to ensure that we only enter real opcodes and only the
+ * first half of a potentially throwing instruction that has been split into
+ * CHECK and work portions. Since the 2nd half of a split operation is always
+ * the first in a BasicBlock, we can't hit it here.
+ */
+ if ((opcode == kMirOpCheck) || !MIR::DecodedInstruction::IsPseudoMirOp(opcode)) {
+ BasicBlockId mapped_id = (*dex_pc_to_block_map)[p->offset];
+ // At first glance the instructions should all be mapped to orig_block.
+ // However, multiple instructions may correspond to the same dex, hence an earlier
+ // instruction may have already moved the mapping for dex to bottom_block.
+ DCHECK((mapped_id == orig_block->id) || (mapped_id == bottom_block->id));
+ (*dex_pc_to_block_map)[p->offset] = bottom_block->id;
+ }
+ }
+ return bottom_block;
}
// Create a new block.
bb = CreateNewBB(kDalvikByteCode);
bb->start_offset = code_offset;
- dex_pc_to_block_map_[bb->start_offset] = bb->id;
+ (*dex_pc_to_block_map)[bb->start_offset] = bb->id;
return bb;
}
/* Identify code range in try blocks and set up the empty catch blocks */
-void MIRGraph::ProcessTryCatchBlocks() {
+void MIRGraph::ProcessTryCatchBlocks(ScopedArenaVector<uint16_t>* dex_pc_to_block_map) {
int tries_size = current_code_item_->tries_size_;
DexOffset offset;
@@ -354,7 +374,7 @@
CatchHandlerIterator iterator(handlers_ptr);
for (; iterator.HasNext(); iterator.Next()) {
uint32_t address = iterator.GetHandlerAddress();
- FindBlock(address, true /*create*/, /* immed_pred_block_p */ nullptr);
+ FindBlock(address, true /*create*/, /* immed_pred_block_p */ nullptr, dex_pc_to_block_map);
}
handlers_ptr = iterator.EndDataPointer();
}
@@ -429,7 +449,8 @@
/* Process instructions with the kBranch flag */
BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset,
int width, int flags, const uint16_t* code_ptr,
- const uint16_t* code_end) {
+ const uint16_t* code_end,
+ ScopedArenaVector<uint16_t>* dex_pc_to_block_map) {
DexOffset target = cur_offset;
switch (insn->dalvikInsn.opcode) {
case Instruction::GOTO:
@@ -460,7 +481,8 @@
}
CountBranch(target);
BasicBlock* taken_block = FindBlock(target, /* create */ true,
- /* immed_pred_block_p */ &cur_block);
+ /* immed_pred_block_p */ &cur_block,
+ dex_pc_to_block_map);
cur_block->taken = taken_block->id;
taken_block->predecessors.push_back(cur_block->id);
@@ -470,18 +492,20 @@
/* create */
true,
/* immed_pred_block_p */
- &cur_block);
+ &cur_block,
+ dex_pc_to_block_map);
cur_block->fall_through = fallthrough_block->id;
fallthrough_block->predecessors.push_back(cur_block->id);
} else if (code_ptr < code_end) {
- FindBlock(cur_offset + width, /* create */ true, /* immed_pred_block_p */ nullptr);
+ FindBlock(cur_offset + width, /* create */ true, /* immed_pred_block_p */ nullptr, dex_pc_to_block_map);
}
return cur_block;
}
/* Process instructions with the kSwitch flag */
BasicBlock* MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset,
- int width, int flags) {
+ int width, int flags,
+ ScopedArenaVector<uint16_t>* dex_pc_to_block_map) {
UNUSED(flags);
const uint16_t* switch_data =
reinterpret_cast<const uint16_t*>(GetCurrentInsns() + cur_offset + insn->dalvikInsn.vB);
@@ -535,7 +559,8 @@
for (i = 0; i < size; i++) {
BasicBlock* case_block = FindBlock(cur_offset + target_table[i], /* create */ true,
- /* immed_pred_block_p */ &cur_block);
+ /* immed_pred_block_p */ &cur_block,
+ dex_pc_to_block_map);
SuccessorBlockInfo* successor_block_info =
static_cast<SuccessorBlockInfo*>(arena_->Alloc(sizeof(SuccessorBlockInfo),
kArenaAllocSuccessor));
@@ -549,7 +574,8 @@
/* Fall-through case */
BasicBlock* fallthrough_block = FindBlock(cur_offset + width, /* create */ true,
- /* immed_pred_block_p */ nullptr);
+ /* immed_pred_block_p */ nullptr,
+ dex_pc_to_block_map);
cur_block->fall_through = fallthrough_block->id;
fallthrough_block->predecessors.push_back(cur_block->id);
return cur_block;
@@ -558,7 +584,8 @@
/* Process instructions with the kThrow flag */
BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset,
int width, int flags, ArenaBitVector* try_block_addr,
- const uint16_t* code_ptr, const uint16_t* code_end) {
+ const uint16_t* code_ptr, const uint16_t* code_end,
+ ScopedArenaVector<uint16_t>* dex_pc_to_block_map) {
UNUSED(flags);
bool in_try_block = try_block_addr->IsBitSet(cur_offset);
bool is_throw = (insn->dalvikInsn.opcode == Instruction::THROW);
@@ -575,7 +602,8 @@
for (; iterator.HasNext(); iterator.Next()) {
BasicBlock* catch_block = FindBlock(iterator.GetHandlerAddress(), false /* create */,
- nullptr /* immed_pred_block_p */);
+ nullptr /* immed_pred_block_p */,
+ dex_pc_to_block_map);
if (insn->dalvikInsn.opcode == Instruction::MONITOR_EXIT &&
IsBadMonitorExitCatch(insn->offset, catch_block->start_offset)) {
// Don't allow monitor-exit to catch its own exception, http://b/15745363 .
@@ -610,7 +638,7 @@
cur_block->explicit_throw = true;
if (code_ptr < code_end) {
// Force creation of new block following THROW via side-effect.
- FindBlock(cur_offset + width, /* create */ true, /* immed_pred_block_p */ nullptr);
+ FindBlock(cur_offset + width, /* create */ true, /* immed_pred_block_p */ nullptr, dex_pc_to_block_map);
}
if (!in_try_block) {
// Don't split a THROW that can't rethrow - we're done.
@@ -642,7 +670,7 @@
* not automatically terminated after the work portion, and may
* contain following instructions.
*
- * Note also that the dex_pc_to_block_map_ entry for the potentially
+ * Note also that the dex_pc_to_block_map entry for the potentially
* throwing instruction will refer to the original basic block.
*/
BasicBlock* new_block = CreateNewBB(kDalvikByteCode);
@@ -677,7 +705,11 @@
// TODO: need to rework expansion of block list & try_block_addr when inlining activated.
// TUNING: use better estimate of basic blocks for following resize.
block_list_.reserve(block_list_.size() + current_code_item_->insns_size_in_code_units_);
- dex_pc_to_block_map_.resize(dex_pc_to_block_map_.size() + current_code_item_->insns_size_in_code_units_);
+ // FindBlock lookup cache.
+ ScopedArenaAllocator allocator(&cu_->arena_stack);
+ ScopedArenaVector<uint16_t> dex_pc_to_block_map(allocator.Adapter());
+ dex_pc_to_block_map.resize(dex_pc_to_block_map.size() +
+ current_code_item_->insns_size_in_code_units_);
// TODO: replace with explicit resize routine. Using automatic extension side effect for now.
try_block_addr_->SetBit(current_code_item_->insns_size_in_code_units_);
@@ -687,7 +719,7 @@
if (current_method_ == 0) {
DCHECK(entry_block_ == NULL);
DCHECK(exit_block_ == NULL);
- DCHECK_EQ(num_blocks_, 0U);
+ DCHECK_EQ(GetNumBlocks(), 0U);
// Use id 0 to represent a null block.
BasicBlock* null_block = CreateNewBB(kNullBlock);
DCHECK_EQ(null_block->id, NullBasicBlockId);
@@ -718,7 +750,7 @@
cur_block->predecessors.push_back(entry_block_->id);
/* Identify code range in try blocks and set up the empty catch blocks */
- ProcessTryCatchBlocks();
+ ProcessTryCatchBlocks(&dex_pc_to_block_map);
uint64_t merged_df_flags = 0u;
@@ -767,20 +799,21 @@
DCHECK(cur_block->taken == NullBasicBlockId);
// Unreachable instruction, mark for no continuation and end basic block.
flags &= ~Instruction::kContinue;
- FindBlock(current_offset_ + width, /* create */ true, /* immed_pred_block_p */ nullptr);
+ FindBlock(current_offset_ + width, /* create */ true,
+ /* immed_pred_block_p */ nullptr, &dex_pc_to_block_map);
}
} else {
cur_block->AppendMIR(insn);
}
// Associate the starting dex_pc for this opcode with its containing basic block.
- dex_pc_to_block_map_[insn->offset] = cur_block->id;
+ dex_pc_to_block_map[insn->offset] = cur_block->id;
code_ptr += width;
if (flags & Instruction::kBranch) {
cur_block = ProcessCanBranch(cur_block, insn, current_offset_,
- width, flags, code_ptr, code_end);
+ width, flags, code_ptr, code_end, &dex_pc_to_block_map);
} else if (flags & Instruction::kReturn) {
cur_block->terminated_by_return = true;
cur_block->fall_through = exit_block_->id;
@@ -794,13 +827,15 @@
* Create a fallthrough block for real instructions
* (incl. NOP).
*/
- FindBlock(current_offset_ + width, /* create */ true, /* immed_pred_block_p */ nullptr);
+ FindBlock(current_offset_ + width, /* create */ true,
+ /* immed_pred_block_p */ nullptr, &dex_pc_to_block_map);
}
} else if (flags & Instruction::kThrow) {
cur_block = ProcessCanThrow(cur_block, insn, current_offset_, width, flags, try_block_addr_,
- code_ptr, code_end);
+ code_ptr, code_end, &dex_pc_to_block_map);
} else if (flags & Instruction::kSwitch) {
- cur_block = ProcessCanSwitch(cur_block, insn, current_offset_, width, flags);
+ cur_block = ProcessCanSwitch(cur_block, insn, current_offset_, width,
+ flags, &dex_pc_to_block_map);
}
if (verify_flags & Instruction::kVerifyVarArgRange ||
verify_flags & Instruction::kVerifyVarArgRangeNonZero) {
@@ -818,7 +853,8 @@
}
current_offset_ += width;
BasicBlock* next_block = FindBlock(current_offset_, /* create */ false,
- /* immed_pred_block_p */ nullptr);
+ /* immed_pred_block_p */ nullptr,
+ &dex_pc_to_block_map);
if (next_block) {
/*
* The next instruction could be the target of a previously parsed
@@ -870,6 +906,34 @@
return GetDataFlowAttributes(opcode);
}
+// The path can easily surpass FS limits because of parameters etc. Use pathconf to get FS
+// restrictions here. Note that a successful invocation will return an actual value. If the path
+// is too long for some reason, the return will be ENAMETOOLONG. Then cut off part of the name.
+//
+// It's possible the path is not valid, or some other errors appear. In that case return false.
+static bool CreateDumpFile(std::string& fname, const char* dir_prefix, NarrowDexOffset start_offset,
+ const char *suffix, int nr, std::string* output) {
+ std::string dir = StringPrintf("./%s", dir_prefix);
+ int64_t max_name_length = pathconf(dir.c_str(), _PC_NAME_MAX);
+ if (max_name_length <= 0) {
+ PLOG(ERROR) << "Could not get file name restrictions for " << dir;
+ return false;
+ }
+
+ std::string name = StringPrintf("%s%x%s_%d.dot", fname.c_str(), start_offset,
+ suffix == nullptr ? "" : suffix, nr);
+ std::string fpath;
+ if (static_cast<int64_t>(name.size()) > max_name_length) {
+ std::string suffix_str = StringPrintf("_%d.dot", nr);
+ name = name.substr(0, static_cast<size_t>(max_name_length) - suffix_str.size()) + suffix_str;
+ }
+ // Sanity check.
+ DCHECK_LE(name.size(), static_cast<size_t>(max_name_length));
+
+ *output = StringPrintf("%s%s", dir_prefix, name.c_str());
+ return true;
+}
+
// TODO: use a configurable base prefix, and adjust callers to supply pass name.
/* Dump the CFG into a DOT graph */
void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks, const char *suffix) {
@@ -878,15 +942,19 @@
// Increment counter to get a unique file number.
cnt++;
+ int nr = cnt.LoadRelaxed();
std::string fname(PrettyMethod(cu_->method_idx, *cu_->dex_file));
ReplaceSpecialChars(fname);
- fname = StringPrintf("%s%s%x%s_%d.dot", dir_prefix, fname.c_str(),
- GetBasicBlock(GetEntryBlock()->fall_through)->start_offset,
- suffix == nullptr ? "" : suffix,
- cnt.LoadRelaxed());
- file = fopen(fname.c_str(), "w");
+ std::string fpath;
+ if (!CreateDumpFile(fname, dir_prefix, GetBasicBlock(GetEntryBlock()->fall_through)->start_offset,
+ suffix, nr, &fpath)) {
+ LOG(ERROR) << "Could not create dump file name for " << fname;
+ return;
+ }
+ file = fopen(fpath.c_str(), "w");
if (file == NULL) {
+ PLOG(ERROR) << "Could not open " << fpath << " for DumpCFG.";
return;
}
fprintf(file, "digraph G {\n");
@@ -1386,6 +1454,27 @@
}
FillTypeSizeString(mir->dalvikInsn.arg[0], decoded_mir);
break;
+ case kMirOpMaddInt:
+ case kMirOpMsubInt:
+ case kMirOpMaddLong:
+ case kMirOpMsubLong:
+ if (ssa_rep != nullptr) {
+ decoded_mir->append(" ");
+ decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[0], false));
+ if (defs > 1) {
+ decoded_mir->append(", ");
+ decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false));
+ }
+ for (int i = 0; i < uses; i++) {
+ decoded_mir->append(", ");
+ decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[i], false));
+ }
+ } else {
+ decoded_mir->append(StringPrintf(" v%d, v%d, v%d, v%d",
+ mir->dalvikInsn.vA, mir->dalvikInsn.vB,
+ mir->dalvikInsn.vC, mir->dalvikInsn.arg[0]));
+ }
+ break;
default:
break;
}
@@ -1590,6 +1679,12 @@
return cu_->dex_file->GetShorty(method_id.proto_idx_);
}
+const char* MIRGraph::GetShortyFromMethodReference(const MethodReference& target_method) {
+ const DexFile::MethodId& method_id =
+ target_method.dex_file->GetMethodId(target_method.dex_method_index);
+ return target_method.dex_file->GetShorty(method_id.proto_idx_);
+}
+
/* Debug Utility - dump a compilation unit */
void MIRGraph::DumpMIRGraph() {
const char* block_type_names[] = {
@@ -1703,12 +1798,16 @@
temp_.ssa.num_vregs = 0u;
temp_.ssa.work_live_vregs = nullptr;
- temp_.ssa.def_block_matrix = nullptr;
+ DCHECK(temp_.ssa.def_block_matrix == nullptr);
+ temp_.ssa.phi_node_blocks = nullptr;
DCHECK(temp_scoped_alloc_.get() != nullptr);
temp_scoped_alloc_.reset();
// Update the maximum number of reachable blocks.
max_num_reachable_blocks_ = num_reachable_blocks_;
+
+ // Mark MIR SSA representations as up to date.
+ mir_ssa_rep_up_to_date_ = true;
}
size_t MIRGraph::GetNumDalvikInsns() const {
@@ -1941,6 +2040,7 @@
DCHECK_EQ(bb->hidden, false);
DCHECK_EQ(bb->visited, false);
bb->visited = true;
+ bb->nesting_depth = loop_head_stack.size();
// Now add the basic block.
uint16_t idx = static_cast<uint16_t>(topological_order_.size());
@@ -1973,6 +2073,7 @@
topological_order_loop_head_stack_.clear();
topological_order_loop_head_stack_.reserve(max_nested_loops);
max_nested_loops_ = max_nested_loops;
+ topological_order_up_to_date_ = true;
}
bool BasicBlock::IsExceptionBlock() const {
@@ -1982,24 +2083,6 @@
return false;
}
-bool MIRGraph::HasSuspendTestBetween(BasicBlock* source, BasicBlockId target_id) {
- BasicBlock* target = GetBasicBlock(target_id);
-
- if (source == nullptr || target == nullptr)
- return false;
-
- int idx;
- for (idx = gen_suspend_test_list_.size() - 1; idx >= 0; idx--) {
- BasicBlock* bb = gen_suspend_test_list_[idx];
- if (bb == source)
- return true; // The block has been inserted by a suspend check before.
- if (source->dominators->IsBitSet(bb->id) && bb->dominators->IsBitSet(target_id))
- return true;
- }
-
- return false;
-}
-
ChildBlockIterator::ChildBlockIterator(BasicBlock* bb, MIRGraph* mir_graph)
: basic_block_(bb), mir_graph_(mir_graph), visited_fallthrough_(false),
visited_taken_(false), have_successors_(false) {
@@ -2222,21 +2305,7 @@
}
}
-void BasicBlock::Hide(MIRGraph* mir_graph) {
- // First lets make it a dalvik bytecode block so it doesn't have any special meaning.
- block_type = kDalvikByteCode;
-
- // Mark it as hidden.
- hidden = true;
-
- // Detach it from its MIRs so we don't generate code for them. Also detached MIRs
- // are updated to know that they no longer have a parent.
- for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) {
- mir->bb = NullBasicBlockId;
- }
- first_mir_insn = nullptr;
- last_mir_insn = nullptr;
-
+void BasicBlock::Kill(MIRGraph* mir_graph) {
for (BasicBlockId pred_id : predecessors) {
BasicBlock* pred_bb = mir_graph->GetBasicBlock(pred_id);
DCHECK(pred_bb != nullptr);
@@ -2244,26 +2313,7 @@
// Sadly we have to go through the children by hand here.
pred_bb->ReplaceChild(id, NullBasicBlockId);
}
-
- // Iterate through children of bb we are hiding.
- ChildBlockIterator successorChildIter(this, mir_graph);
-
- for (BasicBlock* childPtr = successorChildIter.Next(); childPtr != 0; childPtr = successorChildIter.Next()) {
- // Erase this predecessor from child.
- childPtr->ErasePredecessor(id);
- }
-
- // Remove link to children.
- taken = NullBasicBlockId;
- fall_through = NullBasicBlockId;
- successor_block_list_type = kNotUsed;
-}
-
-/*
- * Kill an unreachable block and all blocks that become unreachable by killing this one.
- */
-void BasicBlock::KillUnreachable(MIRGraph* mir_graph) {
- DCHECK(predecessors.empty()); // Unreachable.
+ predecessors.clear();
// Mark as dead and hidden.
block_type = kDead;
@@ -2283,9 +2333,6 @@
ChildBlockIterator iter(this, mir_graph);
for (BasicBlock* succ_bb = iter.Next(); succ_bb != nullptr; succ_bb = iter.Next()) {
succ_bb->ErasePredecessor(id);
- if (succ_bb->predecessors.empty()) {
- succ_bb->KillUnreachable(mir_graph);
- }
}
// Remove links to children.
@@ -2406,20 +2453,20 @@
// Create a new basic block with block_id as num_blocks_ that is
// post-incremented.
BasicBlock* MIRGraph::CreateNewBB(BBType block_type) {
- BasicBlock* res = NewMemBB(block_type, num_blocks_++);
+ BasicBlockId id = static_cast<BasicBlockId>(block_list_.size());
+ BasicBlock* res = NewMemBB(block_type, id);
block_list_.push_back(res);
return res;
}
void MIRGraph::CalculateBasicBlockInformation() {
- PassDriverMEPostOpt driver(cu_);
+ auto* quick_compiler = down_cast<QuickCompiler*>(cu_->compiler_driver->GetCompiler());
+ DCHECK(quick_compiler != nullptr);
+ /* Create the pass driver and launch it */
+ PassDriverMEPostOpt driver(quick_compiler->GetPostOptPassManager(), cu_);
driver.Launch();
}
-void MIRGraph::InitializeBasicBlockData() {
- num_blocks_ = block_list_.size();
-}
-
int MIR::DecodedInstruction::FlagsOf() const {
// Calculate new index.
int idx = static_cast<int>(opcode) - kNumPackedOpcodes;
@@ -2497,9 +2544,19 @@
return Instruction::kContinue | Instruction::kThrow;
case kMirOpPackedArrayPut:
return Instruction::kContinue | Instruction::kThrow;
+ case kMirOpMaddInt:
+ case kMirOpMsubInt:
+ case kMirOpMaddLong:
+ case kMirOpMsubLong:
+ return Instruction::kContinue;
default:
LOG(WARNING) << "ExtendedFlagsOf: Unhandled case: " << static_cast<int> (opcode);
return 0;
}
}
+
+const uint16_t* MIRGraph::GetInsns(int m_unit_index) const {
+ return m_units_[m_unit_index]->GetCodeItem()->insns_;
+}
+
} // namespace art
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 1a18841..27dca65 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -19,10 +19,9 @@
#include <stdint.h>
-#include "compiler_ir.h"
#include "dex_file.h"
#include "dex_instruction.h"
-#include "driver/dex_compilation_unit.h"
+#include "dex_types.h"
#include "invoke_type.h"
#include "mir_field_info.h"
#include "mir_method_info.h"
@@ -34,8 +33,14 @@
namespace art {
+struct CompilationUnit;
+class DexCompilationUnit;
+class DexFileMethodInliner;
class GlobalValueNumbering;
+// Forward declaration.
+class MIRGraph;
+
enum DataFlowAttributePos {
kUA = 0,
kUB,
@@ -131,16 +136,13 @@
enum OatMethodAttributes {
kIsLeaf, // Method is leaf.
- kHasLoop, // Method contains simple loop.
};
#define METHOD_IS_LEAF (1 << kIsLeaf)
-#define METHOD_HAS_LOOP (1 << kHasLoop)
// Minimum field size to contain Dalvik v_reg number.
#define VREG_NUM_WIDTH 16
-#define INVALID_SREG (-1)
#define INVALID_VREG (0xFFFFU)
#define INVALID_OFFSET (0xDEADF00FU)
@@ -410,17 +412,11 @@
void ResetOptimizationFlags(uint16_t reset_flags);
/**
- * @brief Hide the BasicBlock.
- * @details Set it to kDalvikByteCode, set hidden to true, remove all MIRs,
- * remove itself from any predecessor edges, remove itself from any
- * child's predecessor array.
+ * @brief Kill the BasicBlock.
+ * @details Unlink predecessors and successors, remove all MIRs, set the block type to kDead
+ * and set hidden to true.
*/
- void Hide(MIRGraph* mir_graph);
-
- /**
- * @brief Kill the unreachable block and all blocks that become unreachable by killing this one.
- */
- void KillUnreachable(MIRGraph* mir_graph);
+ void Kill(MIRGraph* mir_graph);
/**
* @brief Is ssa_reg the last SSA definition of that VR in the block?
@@ -546,8 +542,9 @@
uint32_t method_idx, jobject class_loader, const DexFile& dex_file);
/* Find existing block */
- BasicBlock* FindBlock(DexOffset code_offset) {
- return FindBlock(code_offset, false, NULL);
+ BasicBlock* FindBlock(DexOffset code_offset,
+ ScopedArenaVector<uint16_t>* dex_pc_to_block_map) {
+ return FindBlock(code_offset, false, nullptr, dex_pc_to_block_map);
}
const uint16_t* GetCurrentInsns() const {
@@ -560,9 +557,7 @@
* This is guaranteed to contain index 0 which is the base method being compiled.
* @return Returns the raw instruction pointer.
*/
- const uint16_t* GetInsns(int m_unit_index) const {
- return m_units_[m_unit_index]->GetCodeItem()->insns_;
- }
+ const uint16_t* GetInsns(int m_unit_index) const;
/**
* @brief Used to obtain the raw data table.
@@ -575,7 +570,7 @@
}
unsigned int GetNumBlocks() const {
- return num_blocks_;
+ return block_list_.size();
}
/**
@@ -705,7 +700,9 @@
void DumpRegLocTable(RegLocation* table, int count);
+ void BasicBlockOptimizationStart();
void BasicBlockOptimization();
+ void BasicBlockOptimizationEnd();
const ArenaVector<BasicBlockId>& GetTopologicalSortOrder() {
DCHECK(!topological_order_.empty());
@@ -731,6 +728,10 @@
return max_nested_loops_;
}
+ bool IsLoopHead(BasicBlockId bb_id) {
+ return topological_order_loop_ends_[topological_order_indexes_[bb_id]] != 0u;
+ }
+
bool IsConst(int32_t s_reg) const {
return is_constant_v_->IsBitSet(s_reg);
}
@@ -969,13 +970,23 @@
return reg_location_[method_sreg_];
}
- bool IsBackedge(BasicBlock* branch_bb, BasicBlockId target_bb_id) {
- return ((target_bb_id != NullBasicBlockId) &&
- (GetBasicBlock(target_bb_id)->start_offset <= branch_bb->start_offset));
+ bool IsBackEdge(BasicBlock* branch_bb, BasicBlockId target_bb_id) {
+ DCHECK_NE(target_bb_id, NullBasicBlockId);
+ DCHECK_LT(target_bb_id, topological_order_indexes_.size());
+ DCHECK_LT(branch_bb->id, topological_order_indexes_.size());
+ return topological_order_indexes_[target_bb_id] <= topological_order_indexes_[branch_bb->id];
}
- bool IsBackwardsBranch(BasicBlock* branch_bb) {
- return IsBackedge(branch_bb, branch_bb->taken) || IsBackedge(branch_bb, branch_bb->fall_through);
+ bool IsSuspendCheckEdge(BasicBlock* branch_bb, BasicBlockId target_bb_id) {
+ if (!IsBackEdge(branch_bb, target_bb_id)) {
+ return false;
+ }
+ if (suspend_checks_in_loops_ == nullptr) {
+ // We didn't run suspend check elimination.
+ return true;
+ }
+ uint16_t target_depth = GetBasicBlock(target_bb_id)->nesting_depth;
+ return (suspend_checks_in_loops_[branch_bb->id] & (1u << (target_depth - 1u))) == 0;
}
void CountBranch(DexOffset target_offset) {
@@ -1055,6 +1066,9 @@
bool ApplyGlobalValueNumberingGate();
bool ApplyGlobalValueNumbering(BasicBlock* bb);
void ApplyGlobalValueNumberingEnd();
+ bool EliminateSuspendChecksGate();
+ bool EliminateSuspendChecks(BasicBlock* bb);
+ void EliminateSuspendChecksEnd();
uint16_t GetGvnIFieldId(MIR* mir) const {
DCHECK(IsInstructionIGetOrIPut(mir->dalvikInsn.opcode));
@@ -1113,6 +1127,7 @@
std::string GetSSANameWithConst(int ssa_reg, bool singles_only);
void GetBlockName(BasicBlock* bb, char* name);
const char* GetShortyFromTargetIdx(int);
+ const char* GetShortyFromMethodReference(const MethodReference& target_method);
void DumpMIRGraph();
CallInfo* NewMemCallInfo(BasicBlock* bb, MIR* mir, InvokeType type, bool is_range);
BasicBlock* NewMemBB(BBType block_type, int block_id);
@@ -1165,7 +1180,7 @@
* @brief Count the uses in the BasicBlock
* @param bb the BasicBlock
*/
- void CountUses(class BasicBlock* bb);
+ void CountUses(BasicBlock* bb);
static uint64_t GetDataFlowAttributes(Instruction::Code opcode);
static uint64_t GetDataFlowAttributes(MIR* mir);
@@ -1181,19 +1196,30 @@
void AllocateSSAUseData(MIR *mir, int num_uses);
void AllocateSSADefData(MIR *mir, int num_defs);
void CalculateBasicBlockInformation();
- void InitializeBasicBlockData();
void ComputeDFSOrders();
void ComputeDefBlockMatrix();
void ComputeDominators();
void CompilerInitializeSSAConversion();
virtual void InitializeBasicBlockDataFlow();
- void InsertPhiNodes();
+ void FindPhiNodeBlocks();
void DoDFSPreOrderSSARename(BasicBlock* block);
bool DfsOrdersUpToDate() const {
return dfs_orders_up_to_date_;
}
+ bool DominationUpToDate() const {
+ return domination_up_to_date_;
+ }
+
+ bool MirSsaRepUpToDate() const {
+ return mir_ssa_rep_up_to_date_;
+ }
+
+ bool TopologicalOrderUpToDate() const {
+ return topological_order_up_to_date_;
+ }
+
/*
* IsDebugBuild sanity check: keep track of the Dex PCs for catch entries so that later on
* we can verify that all catch entries have native PC entries.
@@ -1209,20 +1235,6 @@
void HandleSSADef(int* defs, int dalvik_reg, int reg_index);
bool InferTypeAndSize(BasicBlock* bb, MIR* mir, bool changed);
- // Used for removing redudant suspend tests
- void AppendGenSuspendTestList(BasicBlock* bb) {
- if (gen_suspend_test_list_.size() == 0 ||
- gen_suspend_test_list_.back() != bb) {
- gen_suspend_test_list_.push_back(bb);
- }
- }
-
- /* This is used to check if there is already a method call dominating the
- * source basic block of a backedge and being dominated by the target basic
- * block of the backedge.
- */
- bool HasSuspendTestBetween(BasicBlock* source, BasicBlockId target_id);
-
protected:
int FindCommonParent(int block1, int block2);
void ComputeSuccLineIn(ArenaBitVector* dest, const ArenaBitVector* src1,
@@ -1238,16 +1250,20 @@
bool ContentIsInsn(const uint16_t* code_ptr);
BasicBlock* SplitBlock(DexOffset code_offset, BasicBlock* orig_block,
BasicBlock** immed_pred_block_p);
- BasicBlock* FindBlock(DexOffset code_offset, bool create, BasicBlock** immed_pred_block_p);
- void ProcessTryCatchBlocks();
+ BasicBlock* FindBlock(DexOffset code_offset, bool create, BasicBlock** immed_pred_block_p,
+ ScopedArenaVector<uint16_t>* dex_pc_to_block_map);
+ void ProcessTryCatchBlocks(ScopedArenaVector<uint16_t>* dex_pc_to_block_map);
bool IsBadMonitorExitCatch(NarrowDexOffset monitor_exit_offset, NarrowDexOffset catch_offset);
BasicBlock* ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width,
- int flags, const uint16_t* code_ptr, const uint16_t* code_end);
+ int flags, const uint16_t* code_ptr, const uint16_t* code_end,
+ ScopedArenaVector<uint16_t>* dex_pc_to_block_map);
BasicBlock* ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width,
- int flags);
+ int flags,
+ ScopedArenaVector<uint16_t>* dex_pc_to_block_map);
BasicBlock* ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width,
int flags, ArenaBitVector* try_block_addr, const uint16_t* code_ptr,
- const uint16_t* code_end);
+ const uint16_t* code_end,
+ ScopedArenaVector<uint16_t>* dex_pc_to_block_map);
int AddNewSReg(int v_reg);
void HandleSSAUse(int* uses, int dalvik_reg, int reg_index);
void DataFlowSSAFormat35C(MIR* mir);
@@ -1262,6 +1278,34 @@
void ComputeDomPostOrderTraversal(BasicBlock* bb);
int GetSSAUseCount(int s_reg);
bool BasicBlockOpt(BasicBlock* bb);
+ void MultiplyAddOpt(BasicBlock* bb);
+
+ /**
+ * @brief Check whether the given MIR is possible to throw an exception.
+ * @param mir The mir to check.
+ * @return Returns 'true' if the given MIR might throw an exception.
+ */
+ bool CanThrow(MIR* mir);
+ /**
+ * @brief Combine multiply and add/sub MIRs into corresponding extended MAC MIR.
+ * @param mul_mir The multiply MIR to be combined.
+ * @param add_mir The add/sub MIR to be combined.
+ * @param mul_is_first_addend 'true' if multiply product is the first addend of add operation.
+ * @param is_wide 'true' if the operations are long type.
+ * @param is_sub 'true' if it is a multiply-subtract operation.
+ */
+ void CombineMultiplyAdd(MIR* mul_mir, MIR* add_mir, bool mul_is_first_addend,
+ bool is_wide, bool is_sub);
+ /*
+ * @brief Check whether the first MIR anti-depends on the second MIR.
+ * @details To check whether one of first MIR's uses of vregs is redefined by the second MIR,
+ * i.e. there is a write-after-read dependency.
+ * @param first The first MIR.
+ * @param second The second MIR.
+ * @param Returns true if there is a write-after-read dependency.
+ */
+ bool HasAntiDependency(MIR* first, MIR* second);
+
bool BuildExtendedBBList(class BasicBlock* bb);
bool FillDefBlockMatrix(BasicBlock* bb);
void InitializeDominationInfo(BasicBlock* bb);
@@ -1290,6 +1334,9 @@
unsigned int num_reachable_blocks_;
unsigned int max_num_reachable_blocks_;
bool dfs_orders_up_to_date_;
+ bool domination_up_to_date_;
+ bool mir_ssa_rep_up_to_date_;
+ bool topological_order_up_to_date_;
ArenaVector<BasicBlockId> dfs_order_;
ArenaVector<BasicBlockId> dfs_post_order_;
ArenaVector<BasicBlockId> dom_post_order_traversal_;
@@ -1331,6 +1378,7 @@
size_t num_vregs;
ArenaBitVector* work_live_vregs;
ArenaBitVector** def_block_matrix; // num_vregs x num_blocks_.
+ ArenaBitVector** phi_node_blocks; // num_vregs x num_blocks_.
} ssa;
// Global value numbering.
struct {
@@ -1338,15 +1386,17 @@
uint16_t* ifield_ids_; // Part of GVN/LVN but cached here for LVN to avoid recalculation.
uint16_t* sfield_ids_; // Ditto.
} gvn;
+ // Suspend check elimination.
+ struct {
+ DexFileMethodInliner* inliner;
+ } sce;
} temp_;
static const int kInvalidEntry = -1;
ArenaVector<BasicBlock*> block_list_;
ArenaBitVector* try_block_addr_;
BasicBlock* entry_block_;
BasicBlock* exit_block_;
- unsigned int num_blocks_;
const DexFile::CodeItem* current_code_item_;
- ArenaVector<uint16_t> dex_pc_to_block_map_; // FindBlock lookup cache.
ArenaVector<DexCompilationUnit*> m_units_; // List of methods included in this graph
typedef std::pair<int, int> MIRLocation; // Insert point, (m_unit_ index, offset)
ArenaVector<MIRLocation> method_stack_; // Include stack
@@ -1373,11 +1423,19 @@
ArenaVector<MirIFieldLoweringInfo> ifield_lowering_infos_;
ArenaVector<MirSFieldLoweringInfo> sfield_lowering_infos_;
ArenaVector<MirMethodLoweringInfo> method_lowering_infos_;
+
+ // In the suspend check elimination pass we determine for each basic block and enclosing
+ // loop whether there's guaranteed to be a suspend check on the path from the loop head
+ // to this block. If so, we can eliminate the back-edge suspend check.
+ // The bb->id is index into suspend_checks_in_loops_ and the loop head's depth is bit index
+ // in a suspend_checks_in_loops_[bb->id].
+ uint32_t* suspend_checks_in_loops_;
+
static const uint64_t oat_data_flow_attributes_[kMirOpLast];
- ArenaVector<BasicBlock*> gen_suspend_test_list_; // List of blocks containing suspend tests
friend class MirOptimizationTest;
friend class ClassInitCheckEliminationTest;
+ friend class SuspendCheckEliminationTest;
friend class NullCheckEliminationTest;
friend class GlobalValueNumberingTest;
friend class LocalValueNumberingTest;
diff --git a/compiler/dex/mir_graph_test.cc b/compiler/dex/mir_graph_test.cc
index a96cd84..b3ad040 100644
--- a/compiler/dex/mir_graph_test.cc
+++ b/compiler/dex/mir_graph_test.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "compiler_ir.h"
#include "mir_graph.h"
#include "gtest/gtest.h"
@@ -89,7 +90,6 @@
cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo));
}
}
- cu_.mir_graph->num_blocks_ = count;
ASSERT_EQ(count, cu_.mir_graph->block_list_.size());
cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1];
ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type);
@@ -149,7 +149,7 @@
TopologicalSortOrderTest()
: pool_(),
- cu_(&pool_) {
+ cu_(&pool_, kRuntimeISA, nullptr, nullptr) {
cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena));
}
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 55f2abc..8718191 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -15,8 +15,11 @@
*/
#include "base/bit_vector-inl.h"
-#include "compiler_internals.h"
+#include "base/logging.h"
#include "dataflow_iterator-inl.h"
+#include "dex_flags.h"
+#include "driver/compiler_driver.h"
+#include "driver/dex_compilation_unit.h"
#include "global_value_numbering.h"
#include "local_value_numbering.h"
#include "mir_field_info.h"
@@ -35,6 +38,7 @@
void MIRGraph::SetConstant(int32_t ssa_reg, int32_t value) {
is_constant_v_->SetBit(ssa_reg);
constant_values_[ssa_reg] = value;
+ reg_location_[ssa_reg].is_const = true;
}
void MIRGraph::SetConstantWide(int32_t ssa_reg, int64_t value) {
@@ -42,6 +46,8 @@
is_constant_v_->SetBit(ssa_reg + 1);
constant_values_[ssa_reg] = Low32Bits(value);
constant_values_[ssa_reg + 1] = High32Bits(value);
+ reg_location_[ssa_reg].is_const = true;
+ reg_location_[ssa_reg + 1].is_const = true;
}
void MIRGraph::DoConstantPropagation(BasicBlock* bb) {
@@ -110,13 +116,14 @@
BasicBlock* bb = *p_bb;
if (mir != NULL) {
mir = mir->next;
- if (mir == NULL) {
+ while (mir == NULL) {
bb = GetBasicBlock(bb->fall_through);
if ((bb == NULL) || Predecessors(bb) != 1) {
- mir = NULL;
+ // mir is null and we cannot proceed further.
+ break;
} else {
- *p_bb = bb;
- mir = bb->first_mir_insn;
+ *p_bb = bb;
+ mir = bb->first_mir_insn;
}
}
}
@@ -426,6 +433,10 @@
if (bb->block_type == kDead) {
return true;
}
+ // Currently multiply-accumulate backend supports are only available on arm32 and arm64.
+ if (cu_->instruction_set == kArm64 || cu_->instruction_set == kThumb2) {
+ MultiplyAddOpt(bb);
+ }
bool use_lvn = bb->use_lvn && (cu_->disable_opt & (1u << kLocalValueNumbering)) == 0u;
std::unique_ptr<ScopedArenaAllocator> allocator;
std::unique_ptr<GlobalValueNumbering> global_valnum;
@@ -481,9 +492,11 @@
mir->ssa_rep->num_uses = 0;
BasicBlock* successor_to_unlink = GetBasicBlock(edge_to_kill);
successor_to_unlink->ErasePredecessor(bb->id);
- if (successor_to_unlink->predecessors.empty()) {
- successor_to_unlink->KillUnreachable(this);
- }
+ // We have changed the graph structure.
+ dfs_orders_up_to_date_ = false;
+ domination_up_to_date_ = false;
+ topological_order_up_to_date_ = false;
+ // Keep MIR SSA rep, the worst that can happen is a Phi with just 1 input.
}
break;
case Instruction::CMPL_FLOAT:
@@ -526,6 +539,9 @@
default: LOG(ERROR) << "Unexpected opcode: " << opcode;
}
mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
+ // Clear use count of temp VR.
+ use_counts_[mir->ssa_rep->defs[0]] = 0;
+ raw_use_counts_[mir->ssa_rep->defs[0]] = 0;
// Copy the SSA information that is relevant.
mir_next->ssa_rep->num_uses = mir->ssa_rep->num_uses;
mir_next->ssa_rep->uses = mir->ssa_rep->uses;
@@ -539,36 +555,13 @@
}
}
break;
- case Instruction::RETURN_VOID:
- case Instruction::RETURN:
- case Instruction::RETURN_WIDE:
- case Instruction::RETURN_OBJECT:
- if (bb->GetFirstNonPhiInsn() == mir) {
- // This is a simple return BB. Eliminate suspend checks on predecessor back-edges.
- for (BasicBlockId pred_id : bb->predecessors) {
- BasicBlock* pred_bb = GetBasicBlock(pred_id);
- DCHECK(pred_bb != nullptr);
- if (IsBackedge(pred_bb, bb->id) && pred_bb->last_mir_insn != nullptr &&
- (IsInstructionIfCc(pred_bb->last_mir_insn->dalvikInsn.opcode) ||
- IsInstructionIfCcZ(pred_bb->last_mir_insn->dalvikInsn.opcode) ||
- IsInstructionGoto(pred_bb->last_mir_insn->dalvikInsn.opcode))) {
- pred_bb->last_mir_insn->optimization_flags |= MIR_IGNORE_SUSPEND_CHECK;
- if (cu_->verbose) {
- LOG(INFO) << "Suppressed suspend check on branch to return at 0x" << std::hex
- << pred_bb->last_mir_insn->offset;
- }
- }
- }
- }
- break;
default:
break;
}
// Is this the select pattern?
// TODO: flesh out support for Mips. NOTE: llvm's select op doesn't quite work here.
// TUNING: expand to support IF_xx compare & branches
- if (!cu_->compiler->IsPortable() &&
- (cu_->instruction_set == kArm64 || cu_->instruction_set == kThumb2 ||
+ if ((cu_->instruction_set == kArm64 || cu_->instruction_set == kThumb2 ||
cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) &&
IsInstructionIfCcZ(mir->dalvikInsn.opcode)) {
BasicBlock* ft = GetBasicBlock(bb->fall_through);
@@ -589,12 +582,8 @@
if ((tk_ft == NULL) && (ft_tk == NULL) && (tk_tk == ft_ft) &&
(Predecessors(tk) == 1) && (Predecessors(ft) == 1)) {
/*
- * Okay - we have the basic diamond shape. At the very least, we can eliminate the
- * suspend check on the taken-taken branch back to the join point.
+ * Okay - we have the basic diamond shape.
*/
- if (SelectKind(tk->last_mir_insn) == kSelectGoto) {
- tk->last_mir_insn->optimization_flags |= (MIR_IGNORE_SUSPEND_CHECK);
- }
// TODO: Add logic for LONG.
// Are the block bodies something we can handle?
@@ -669,36 +658,36 @@
* Phi node only contains our two cases as input, we will use the result
* SSA name of the Phi node as our select result and delete the Phi. If
* the Phi node has more than two operands, we will arbitrarily use the SSA
- * name of the "true" path, delete the SSA name of the "false" path from the
+ * name of the "false" path, delete the SSA name of the "true" path from the
* Phi node (and fix up the incoming arc list).
*/
if (phi->ssa_rep->num_uses == 2) {
mir->ssa_rep->defs[0] = phi->ssa_rep->defs[0];
- phi->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
+ // Rather than changing the Phi to kMirOpNop, remove it completely.
+ // This avoids leaving other Phis after kMirOpNop (i.e. a non-Phi) insn.
+ tk_tk->RemoveMIR(phi);
+ int dead_false_def = if_false->ssa_rep->defs[0];
+ raw_use_counts_[dead_false_def] = use_counts_[dead_false_def] = 0;
} else {
- int dead_def = if_false->ssa_rep->defs[0];
- int live_def = if_true->ssa_rep->defs[0];
+ int live_def = if_false->ssa_rep->defs[0];
mir->ssa_rep->defs[0] = live_def;
- BasicBlockId* incoming = phi->meta.phi_incoming;
- for (int i = 0; i < phi->ssa_rep->num_uses; i++) {
- if (phi->ssa_rep->uses[i] == live_def) {
- incoming[i] = bb->id;
- }
- }
- for (int i = 0; i < phi->ssa_rep->num_uses; i++) {
- if (phi->ssa_rep->uses[i] == dead_def) {
- int last_slot = phi->ssa_rep->num_uses - 1;
- phi->ssa_rep->uses[i] = phi->ssa_rep->uses[last_slot];
- incoming[i] = incoming[last_slot];
- }
- }
}
- phi->ssa_rep->num_uses--;
- bb->taken = NullBasicBlockId;
- tk->block_type = kDead;
- for (MIR* tmir = ft->first_mir_insn; tmir != NULL; tmir = tmir->next) {
- tmir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
- }
+ int dead_true_def = if_true->ssa_rep->defs[0];
+ raw_use_counts_[dead_true_def] = use_counts_[dead_true_def] = 0;
+ // We want to remove ft and tk and link bb directly to ft_ft. First, we need
+ // to update all Phi inputs correctly with UpdatePredecessor(ft->id, bb->id)
+ // since the live_def above comes from ft->first_mir_insn (if_false).
+ DCHECK(if_false == ft->first_mir_insn);
+ ft_ft->UpdatePredecessor(ft->id, bb->id);
+ // Correct the rest of the links between bb, ft and ft_ft.
+ ft->ErasePredecessor(bb->id);
+ ft->fall_through = NullBasicBlockId;
+ bb->fall_through = ft_ft->id;
+ // Now we can kill tk and ft.
+ tk->Kill(this);
+ ft->Kill(this);
+ // NOTE: DFS order, domination info and topological order are still usable
+ // despite the newly dead blocks.
}
}
}
@@ -795,10 +784,6 @@
break;
}
walker = prev;
-
- if (walker->visited) {
- break;
- }
}
return false;
}
@@ -812,43 +797,9 @@
MIR* mir = bb->last_mir_insn;
DCHECK(bb->first_mir_insn != nullptr);
- // Grab the attributes from the paired opcode.
+ // Get the paired insn and check if it can still throw.
MIR* throw_insn = mir->meta.throw_insn;
- uint64_t df_attributes = GetDataFlowAttributes(throw_insn);
-
- // Don't combine if the throw_insn can still throw NPE.
- if ((df_attributes & DF_HAS_NULL_CHKS) != 0 &&
- (throw_insn->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0) {
- break;
- }
- // Now whitelist specific instructions.
- bool ok = false;
- if ((df_attributes & DF_IFIELD) != 0) {
- // Combine only if fast, otherwise weird things can happen.
- const MirIFieldLoweringInfo& field_info = GetIFieldLoweringInfo(throw_insn);
- ok = (df_attributes & DF_DA) ? field_info.FastGet() : field_info.FastPut();
- } else if ((df_attributes & DF_SFIELD) != 0) {
- // Combine only if fast, otherwise weird things can happen.
- const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(throw_insn);
- bool fast = ((df_attributes & DF_DA) ? field_info.FastGet() : field_info.FastPut());
- // Don't combine if the SGET/SPUT can call <clinit>().
- bool clinit = !field_info.IsClassInitialized() &&
- (throw_insn->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 0;
- ok = fast && !clinit;
- } else if ((df_attributes & DF_HAS_RANGE_CHKS) != 0) {
- // Only AGET/APUT have range checks. We have processed the AGET/APUT null check above.
- DCHECK_NE(throw_insn->optimization_flags & MIR_IGNORE_NULL_CHECK, 0);
- ok = ((throw_insn->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0);
- } else if ((throw_insn->dalvikInsn.FlagsOf() & Instruction::kThrow) == 0) {
- // We can encounter a non-throwing insn here thanks to inlining or other optimizations.
- ok = true;
- } else if (throw_insn->dalvikInsn.opcode == Instruction::ARRAY_LENGTH ||
- throw_insn->dalvikInsn.opcode == Instruction::FILL_ARRAY_DATA ||
- static_cast<int>(throw_insn->dalvikInsn.opcode) == kMirOpNullCheck) {
- // No more checks for these (null check was processed above).
- ok = true;
- }
- if (!ok) {
+ if (CanThrow(throw_insn)) {
break;
}
@@ -887,9 +838,6 @@
BasicBlock* succ_bb = GetBasicBlock(succ_info->block);
DCHECK(succ_bb->catch_entry);
succ_bb->ErasePredecessor(bb->id);
- if (succ_bb->predecessors.empty()) {
- succ_bb->KillUnreachable(this);
- }
}
}
}
@@ -932,8 +880,10 @@
child->UpdatePredecessor(bb_next->id, bb->id);
}
- // DFS orders are not up to date anymore.
+ // DFS orders, domination and topological order are not up to date anymore.
dfs_orders_up_to_date_ = false;
+ domination_up_to_date_ = false;
+ topological_order_up_to_date_ = false;
// Now, loop back and see if we can keep going
}
@@ -1605,7 +1555,7 @@
return false; // Not iterative - return value will be ignored
}
-void MIRGraph::BasicBlockOptimization() {
+void MIRGraph::BasicBlockOptimizationStart() {
if ((cu_->disable_opt & (1 << kLocalValueNumbering)) == 0) {
temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
temp_.gvn.ifield_ids_ =
@@ -1613,7 +1563,9 @@
temp_.gvn.sfield_ids_ =
GlobalValueNumbering::PrepareGvnFieldIds(temp_scoped_alloc_.get(), sfield_lowering_infos_);
}
+}
+void MIRGraph::BasicBlockOptimization() {
if ((cu_->disable_opt & (1 << kSuppressExceptionEdges)) != 0) {
ClearAllVisitedFlags();
PreOrderDfsIterator iter2(this);
@@ -1630,11 +1582,318 @@
BasicBlockOpt(bb);
}
}
+}
+void MIRGraph::BasicBlockOptimizationEnd() {
// Clean up after LVN.
temp_.gvn.ifield_ids_ = nullptr;
temp_.gvn.sfield_ids_ = nullptr;
temp_scoped_alloc_.reset();
}
+bool MIRGraph::EliminateSuspendChecksGate() {
+ if ((cu_->disable_opt & (1 << kSuspendCheckElimination)) != 0 || // Disabled.
+ GetMaxNestedLoops() == 0u || // Nothing to do.
+ GetMaxNestedLoops() >= 32u || // Only 32 bits in suspend_checks_in_loops_[.].
+ // Exclude 32 as well to keep bit shifts well-defined.
+ !HasInvokes()) { // No invokes to actually eliminate any suspend checks.
+ return false;
+ }
+ if (cu_->compiler_driver != nullptr && cu_->compiler_driver->GetMethodInlinerMap() != nullptr) {
+ temp_.sce.inliner =
+ cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file);
+ }
+ suspend_checks_in_loops_ = static_cast<uint32_t*>(
+ arena_->Alloc(GetNumBlocks() * sizeof(*suspend_checks_in_loops_), kArenaAllocMisc));
+ return true;
+}
+
+bool MIRGraph::EliminateSuspendChecks(BasicBlock* bb) {
+ if (bb->block_type != kDalvikByteCode) {
+ return false;
+ }
+ DCHECK_EQ(GetTopologicalSortOrderLoopHeadStack()->size(), bb->nesting_depth);
+ if (bb->nesting_depth == 0u) {
+ // Out of loops.
+ DCHECK_EQ(suspend_checks_in_loops_[bb->id], 0u); // The array was zero-initialized.
+ return false;
+ }
+ uint32_t suspend_checks_in_loops = (1u << bb->nesting_depth) - 1u; // Start with all loop heads.
+ bool found_invoke = false;
+ for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
+ if (IsInstructionInvoke(mir->dalvikInsn.opcode) &&
+ (temp_.sce.inliner == nullptr ||
+ !temp_.sce.inliner->IsIntrinsic(mir->dalvikInsn.vB, nullptr))) {
+ // Non-intrinsic invoke, rely on a suspend point in the invoked method.
+ found_invoke = true;
+ break;
+ }
+ }
+ if (!found_invoke) {
+ // Intersect suspend checks from predecessors.
+ uint16_t bb_topo_idx = topological_order_indexes_[bb->id];
+ uint32_t pred_mask_union = 0u;
+ for (BasicBlockId pred_id : bb->predecessors) {
+ uint16_t pred_topo_idx = topological_order_indexes_[pred_id];
+ if (pred_topo_idx < bb_topo_idx) {
+ // Determine the loop depth of the predecessors relative to this block.
+ size_t pred_loop_depth = topological_order_loop_head_stack_.size();
+ while (pred_loop_depth != 0u &&
+ pred_topo_idx < topological_order_loop_head_stack_[pred_loop_depth - 1].first) {
+ --pred_loop_depth;
+ }
+ DCHECK_LE(pred_loop_depth, GetBasicBlock(pred_id)->nesting_depth);
+ uint32_t pred_mask = (1u << pred_loop_depth) - 1u;
+ // Intersect pred_mask bits in suspend_checks_in_loops with
+ // suspend_checks_in_loops_[pred_id].
+ uint32_t pred_loops_without_checks = pred_mask & ~suspend_checks_in_loops_[pred_id];
+ suspend_checks_in_loops = suspend_checks_in_loops & ~pred_loops_without_checks;
+ pred_mask_union |= pred_mask;
+ }
+ }
+ DCHECK_EQ(((1u << (IsLoopHead(bb->id) ? bb->nesting_depth - 1u: bb->nesting_depth)) - 1u),
+ pred_mask_union);
+ suspend_checks_in_loops &= pred_mask_union;
+ }
+ suspend_checks_in_loops_[bb->id] = suspend_checks_in_loops;
+ if (suspend_checks_in_loops == 0u) {
+ return false;
+ }
+ // Apply MIR_IGNORE_SUSPEND_CHECK if appropriate.
+ if (bb->taken != NullBasicBlockId) {
+ DCHECK(bb->last_mir_insn != nullptr);
+ DCHECK(IsInstructionIfCc(bb->last_mir_insn->dalvikInsn.opcode) ||
+ IsInstructionIfCcZ(bb->last_mir_insn->dalvikInsn.opcode) ||
+ IsInstructionGoto(bb->last_mir_insn->dalvikInsn.opcode) ||
+ (static_cast<int>(bb->last_mir_insn->dalvikInsn.opcode) >= kMirOpFusedCmplFloat &&
+ static_cast<int>(bb->last_mir_insn->dalvikInsn.opcode) <= kMirOpFusedCmpLong));
+ if (!IsSuspendCheckEdge(bb, bb->taken) &&
+ (bb->fall_through == NullBasicBlockId || !IsSuspendCheckEdge(bb, bb->fall_through))) {
+ bb->last_mir_insn->optimization_flags |= MIR_IGNORE_SUSPEND_CHECK;
+ }
+ } else if (bb->fall_through != NullBasicBlockId && IsSuspendCheckEdge(bb, bb->fall_through)) {
+ // We've got a fall-through suspend edge. Add an artificial GOTO to force suspend check.
+ MIR* mir = NewMIR();
+ mir->dalvikInsn.opcode = Instruction::GOTO;
+ mir->dalvikInsn.vA = 0; // Branch offset.
+ mir->offset = GetBasicBlock(bb->fall_through)->start_offset;
+ mir->m_unit_index = current_method_;
+ mir->ssa_rep = reinterpret_cast<SSARepresentation*>(
+ arena_->Alloc(sizeof(SSARepresentation), kArenaAllocDFInfo)); // Zero-initialized.
+ bb->AppendMIR(mir);
+ std::swap(bb->fall_through, bb->taken); // The fall-through has become taken.
+ }
+ return true;
+}
+
+void MIRGraph::EliminateSuspendChecksEnd() {
+ temp_.sce.inliner = nullptr;
+}
+
+bool MIRGraph::CanThrow(MIR* mir) {
+ if ((mir->dalvikInsn.FlagsOf() & Instruction::kThrow) == 0) {
+ return false;
+ }
+ const int opt_flags = mir->optimization_flags;
+ uint64_t df_attributes = GetDataFlowAttributes(mir);
+
+ // First, check if the insn can still throw NPE.
+ if (((df_attributes & DF_HAS_NULL_CHKS) != 0) && ((opt_flags & MIR_IGNORE_NULL_CHECK) == 0)) {
+ return true;
+ }
+
+ // Now process specific instructions.
+ if ((df_attributes & DF_IFIELD) != 0) {
+ // The IGET/IPUT family. We have processed the IGET/IPUT null check above.
+ DCHECK_NE(opt_flags & MIR_IGNORE_NULL_CHECK, 0);
+ // If not fast, weird things can happen and the insn can throw.
+ const MirIFieldLoweringInfo& field_info = GetIFieldLoweringInfo(mir);
+ bool fast = (df_attributes & DF_DA) != 0 ? field_info.FastGet() : field_info.FastPut();
+ return !fast;
+ } else if ((df_attributes & DF_SFIELD) != 0) {
+ // The SGET/SPUT family. Check for potentially throwing class initialization.
+ // Also, if not fast, weird things can happen and the insn can throw.
+ const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(mir);
+ bool fast = (df_attributes & DF_DA) != 0 ? field_info.FastGet() : field_info.FastPut();
+ bool is_class_initialized = field_info.IsClassInitialized() ||
+ ((mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0);
+ return !(fast && is_class_initialized);
+ } else if ((df_attributes & DF_HAS_RANGE_CHKS) != 0) {
+ // Only AGET/APUT have range checks. We have processed the AGET/APUT null check above.
+ DCHECK_NE(opt_flags & MIR_IGNORE_NULL_CHECK, 0);
+ // Non-throwing only if range check has been eliminated.
+ return ((opt_flags & MIR_IGNORE_RANGE_CHECK) == 0);
+ } else if (mir->dalvikInsn.opcode == Instruction::ARRAY_LENGTH ||
+ mir->dalvikInsn.opcode == Instruction::FILL_ARRAY_DATA ||
+ static_cast<int>(mir->dalvikInsn.opcode) == kMirOpNullCheck) {
+ // No more checks for these (null check was processed above).
+ return false;
+ }
+ return true;
+}
+
+bool MIRGraph::HasAntiDependency(MIR* first, MIR* second) {
+ DCHECK(first->ssa_rep != nullptr);
+ DCHECK(second->ssa_rep != nullptr);
+ if ((second->ssa_rep->num_defs > 0) && (first->ssa_rep->num_uses > 0)) {
+ int vreg0 = SRegToVReg(second->ssa_rep->defs[0]);
+ int vreg1 = (second->ssa_rep->num_defs == 2) ?
+ SRegToVReg(second->ssa_rep->defs[1]) : INVALID_VREG;
+ for (int i = 0; i < first->ssa_rep->num_uses; i++) {
+ int32_t use = SRegToVReg(first->ssa_rep->uses[i]);
+ if (use == vreg0 || use == vreg1) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void MIRGraph::CombineMultiplyAdd(MIR* mul_mir, MIR* add_mir, bool mul_is_first_addend,
+ bool is_wide, bool is_sub) {
+ if (is_wide) {
+ if (is_sub) {
+ add_mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpMsubLong);
+ } else {
+ add_mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpMaddLong);
+ }
+ } else {
+ if (is_sub) {
+ add_mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpMsubInt);
+ } else {
+ add_mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpMaddInt);
+ }
+ }
+ add_mir->ssa_rep->num_uses = is_wide ? 6 : 3;
+ int32_t addend0 = INVALID_SREG;
+ int32_t addend1 = INVALID_SREG;
+ if (is_wide) {
+ addend0 = mul_is_first_addend ? add_mir->ssa_rep->uses[2] : add_mir->ssa_rep->uses[0];
+ addend1 = mul_is_first_addend ? add_mir->ssa_rep->uses[3] : add_mir->ssa_rep->uses[1];
+ } else {
+ addend0 = mul_is_first_addend ? add_mir->ssa_rep->uses[1] : add_mir->ssa_rep->uses[0];
+ }
+
+ AllocateSSAUseData(add_mir, add_mir->ssa_rep->num_uses);
+ add_mir->ssa_rep->uses[0] = mul_mir->ssa_rep->uses[0];
+ add_mir->ssa_rep->uses[1] = mul_mir->ssa_rep->uses[1];
+ // Clear the original multiply product ssa use count, as it is not used anymore.
+ raw_use_counts_[mul_mir->ssa_rep->defs[0]] = 0;
+ use_counts_[mul_mir->ssa_rep->defs[0]] = 0;
+ if (is_wide) {
+ DCHECK_EQ(add_mir->ssa_rep->num_uses, 6);
+ add_mir->ssa_rep->uses[2] = mul_mir->ssa_rep->uses[2];
+ add_mir->ssa_rep->uses[3] = mul_mir->ssa_rep->uses[3];
+ add_mir->ssa_rep->uses[4] = addend0;
+ add_mir->ssa_rep->uses[5] = addend1;
+ raw_use_counts_[mul_mir->ssa_rep->defs[1]] = 0;
+ use_counts_[mul_mir->ssa_rep->defs[1]] = 0;
+ } else {
+ DCHECK_EQ(add_mir->ssa_rep->num_uses, 3);
+ add_mir->ssa_rep->uses[2] = addend0;
+ }
+ // Copy in the decoded instruction information.
+ add_mir->dalvikInsn.vB = SRegToVReg(add_mir->ssa_rep->uses[0]);
+ if (is_wide) {
+ add_mir->dalvikInsn.vC = SRegToVReg(add_mir->ssa_rep->uses[2]);
+ add_mir->dalvikInsn.arg[0] = SRegToVReg(add_mir->ssa_rep->uses[4]);
+ } else {
+ add_mir->dalvikInsn.vC = SRegToVReg(add_mir->ssa_rep->uses[1]);
+ add_mir->dalvikInsn.arg[0] = SRegToVReg(add_mir->ssa_rep->uses[2]);
+ }
+ // Original multiply MIR is set to Nop.
+ mul_mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
+}
+
+void MIRGraph::MultiplyAddOpt(BasicBlock* bb) {
+ if (bb->block_type == kDead) {
+ return;
+ }
+ ScopedArenaAllocator allocator(&cu_->arena_stack);
+ ScopedArenaSafeMap<uint32_t, MIR*> ssa_mul_map(std::less<uint32_t>(), allocator.Adapter());
+ ScopedArenaSafeMap<uint32_t, MIR*>::iterator map_it;
+ for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
+ Instruction::Code opcode = mir->dalvikInsn.opcode;
+ bool is_sub = true;
+ bool is_candidate_multiply = false;
+ switch (opcode) {
+ case Instruction::MUL_INT:
+ case Instruction::MUL_INT_2ADDR:
+ is_candidate_multiply = true;
+ break;
+ case Instruction::MUL_LONG:
+ case Instruction::MUL_LONG_2ADDR:
+ if (cu_->target64) {
+ is_candidate_multiply = true;
+ }
+ break;
+ case Instruction::ADD_INT:
+ case Instruction::ADD_INT_2ADDR:
+ is_sub = false;
+ FALLTHROUGH_INTENDED;
+ case Instruction::SUB_INT:
+ case Instruction::SUB_INT_2ADDR:
+ if (((map_it = ssa_mul_map.find(mir->ssa_rep->uses[0])) != ssa_mul_map.end()) && !is_sub) {
+ // a*b+c
+ CombineMultiplyAdd(map_it->second, mir, true /* product is the first addend */,
+ false /* is_wide */, false /* is_sub */);
+ ssa_mul_map.erase(mir->ssa_rep->uses[0]);
+ } else if ((map_it = ssa_mul_map.find(mir->ssa_rep->uses[1])) != ssa_mul_map.end()) {
+ // c+a*b or c-a*b
+ CombineMultiplyAdd(map_it->second, mir, false /* product is the second addend */,
+ false /* is_wide */, is_sub);
+ ssa_mul_map.erase(map_it);
+ }
+ break;
+ case Instruction::ADD_LONG:
+ case Instruction::ADD_LONG_2ADDR:
+ is_sub = false;
+ FALLTHROUGH_INTENDED;
+ case Instruction::SUB_LONG:
+ case Instruction::SUB_LONG_2ADDR:
+ if (!cu_->target64) {
+ break;
+ }
+ if ((map_it = ssa_mul_map.find(mir->ssa_rep->uses[0])) != ssa_mul_map.end() && !is_sub) {
+ // a*b+c
+ CombineMultiplyAdd(map_it->second, mir, true /* product is the first addend */,
+ true /* is_wide */, false /* is_sub */);
+ ssa_mul_map.erase(map_it);
+ } else if ((map_it = ssa_mul_map.find(mir->ssa_rep->uses[2])) != ssa_mul_map.end()) {
+ // c+a*b or c-a*b
+ CombineMultiplyAdd(map_it->second, mir, false /* product is the second addend */,
+ true /* is_wide */, is_sub);
+ ssa_mul_map.erase(map_it);
+ }
+ break;
+ default:
+ if (!ssa_mul_map.empty() && CanThrow(mir)) {
+ // Should not combine multiply and add MIRs across potential exception.
+ ssa_mul_map.clear();
+ }
+ break;
+ }
+
+ // Exclude the case when an MIR writes a vreg which is previous candidate multiply MIR's uses.
+ // It is because that current RA may allocate the same physical register to them. For this
+ // kind of cases, the multiplier has been updated, we should not use updated value to the
+ // multiply-add insn.
+ if (ssa_mul_map.size() > 0) {
+ for (auto it = ssa_mul_map.begin(); it != ssa_mul_map.end();) {
+ MIR* mul = it->second;
+ if (HasAntiDependency(mul, mir)) {
+ it = ssa_mul_map.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ if (is_candidate_multiply &&
+ (GetRawUseCount(mir->ssa_rep->defs[0]) == 1) && (mir->next != nullptr)) {
+ ssa_mul_map.Put(mir->ssa_rep->defs[0], mir);
+ }
+ }
+}
+
} // namespace art
diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc
index c794cc6..199bc27 100644
--- a/compiler/dex/mir_optimization_test.cc
+++ b/compiler/dex/mir_optimization_test.cc
@@ -16,9 +16,10 @@
#include <vector>
-#include "compiler_internals.h"
+#include "base/logging.h"
#include "dataflow_iterator.h"
#include "dataflow_iterator-inl.h"
+#include "dex/compiler_ir.h"
#include "dex/mir_field_info.h"
#include "gtest/gtest.h"
@@ -88,6 +89,8 @@
{ bb, opcode, 0u, vA, vB, vC }
#define DEF_INVOKE(bb, opcode, vC, method_info) \
{ bb, opcode, method_info, 0u, 0u, vC }
+#define DEF_OTHER0(bb, opcode) \
+ { bb, opcode, 0u, 0u, 0u, 0u }
#define DEF_OTHER1(bb, opcode, vA) \
{ bb, opcode, 0u, vA, 0u, 0u }
#define DEF_OTHER2(bb, opcode, vA, vB) \
@@ -127,7 +130,6 @@
cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo));
}
}
- cu_.mir_graph->num_blocks_ = count;
ASSERT_EQ(count, cu_.mir_graph->block_list_.size());
cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1];
ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type);
@@ -175,6 +177,56 @@
PrepareBasicBlocks(bbs);
}
+ void PrepareNestedLoopsWhile_While() {
+ static const BBDef bbs[] = {
+ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(8)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 8), DEF_PRED2(3, 7)), // Outer while loop head.
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED2(4, 6)), // Inner while loop head.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(5)), // "taken" loops to inner head.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(5)), // "taken" loops to outer head.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
+ };
+ PrepareBasicBlocks(bbs);
+ }
+
+ void PrepareNestedLoopsWhile_WhileWhile() {
+ static const BBDef bbs[] = {
+ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(10)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 10), DEF_PRED2(3, 9)), // Outer while loop head.
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED2(4, 6)), // Inner while loop head 1.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(5)), // Loops to inner head 1.
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(8, 9), DEF_PRED2(5, 8)), // Inner while loop head 2.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED1(7)), // loops to inner head 2.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(7)), // loops to outer head.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
+ };
+ PrepareBasicBlocks(bbs);
+ }
+
+ void PrepareNestedLoopsWhile_WhileWhile_WithExtraEdge() {
+ // Extra edge from the first inner loop body to second inner loop body (6u->8u).
+ static const BBDef bbs[] = {
+ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(10)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 10), DEF_PRED2(3, 9)), // Outer while loop head.
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED2(4, 6)), // Inner while loop head 1.
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 8), DEF_PRED1(5)), // Loops to inner head 1.
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(8, 9), DEF_PRED2(5, 8)), // Inner while loop head 2.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED2(7, 6)), // loops to inner head 2.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(7)), // loops to outer head.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
+ };
+ PrepareBasicBlocks(bbs);
+ }
+
void PrepareCatch() {
static const BBDef bbs[] = {
DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
@@ -275,7 +327,7 @@
MirOptimizationTest()
: pool_(),
- cu_(&pool_),
+ cu_(&pool_, kRuntimeISA, nullptr, nullptr),
mir_count_(0u),
mirs_(nullptr),
code_item_(nullptr) {
@@ -397,6 +449,43 @@
}
};
+class SuspendCheckEliminationTest : public MirOptimizationTest {
+ protected:
+ bool IsBackEdge(BasicBlockId branch_bb, BasicBlockId target_bb) {
+ BasicBlock* branch = cu_.mir_graph->GetBasicBlock(branch_bb);
+ return target_bb != NullBasicBlockId && cu_.mir_graph->IsBackEdge(branch, target_bb);
+ }
+
+ bool IsSuspendCheckEdge(BasicBlockId branch_bb, BasicBlockId target_bb) {
+ BasicBlock* branch = cu_.mir_graph->GetBasicBlock(branch_bb);
+ return cu_.mir_graph->IsSuspendCheckEdge(branch, target_bb);
+ }
+
+ void PerformSuspendCheckElimination() {
+ cu_.mir_graph->SSATransformationStart();
+ cu_.mir_graph->ComputeDFSOrders();
+ cu_.mir_graph->ComputeDominators();
+ cu_.mir_graph->ComputeTopologicalSortOrder();
+ cu_.mir_graph->SSATransformationEnd();
+ bool gate_result = cu_.mir_graph->EliminateSuspendChecksGate();
+ ASSERT_TRUE(gate_result);
+ TopologicalSortIterator iterator(cu_.mir_graph.get());
+ bool change = false;
+ for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
+ change = cu_.mir_graph->EliminateSuspendChecks(bb);
+ }
+ cu_.mir_graph->EliminateSuspendChecksEnd();
+ }
+
+ SuspendCheckEliminationTest()
+ : MirOptimizationTest() {
+ static const MethodDef methods[] = {
+ { 0u, 1u, 0u, 0u, kDirect, kDirect, false, false }, // Dummy.
+ };
+ PrepareMethods(methods);
+ }
+};
+
TEST_F(ClassInitCheckEliminationTest, SingleBlock) {
static const SFieldDef sfields[] = {
{ 0u, 1u, 0u, 0u, kDexMemAccessWord },
@@ -882,7 +971,208 @@
}
}
-// Undefine MIR_DEF for null check elimination.
-#undef MIR_DEF
+TEST_F(SuspendCheckEliminationTest, LoopNoElimination) {
+ static const MIRDef mirs[] = {
+ DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u, 0u), // Force the pass to run.
+ DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge back.
+ };
+
+ PrepareLoop();
+ PrepareMIRs(mirs);
+ PerformSuspendCheckElimination();
+ ASSERT_TRUE(IsBackEdge(4u, 4u));
+ EXPECT_TRUE(IsSuspendCheckEdge(4u, 4u)); // Suspend point on loop to self.
+}
+
+TEST_F(SuspendCheckEliminationTest, LoopElimination) {
+ static const MIRDef mirs[] = {
+ DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in the loop.
+ DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge back.
+ };
+
+ PrepareLoop();
+ PrepareMIRs(mirs);
+ PerformSuspendCheckElimination();
+ ASSERT_TRUE(IsBackEdge(4u, 4u));
+ EXPECT_FALSE(IsSuspendCheckEdge(4u, 4u)); // No suspend point on loop to self.
+}
+
+TEST_F(SuspendCheckEliminationTest, While_While_NoElimination) {
+ static const MIRDef mirs[] = {
+ DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u, 0u), // Force the pass to run.
+ DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop.
+ DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop.
+ DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head.
+ DEF_OTHER0(7u, Instruction::GOTO), // Edge back to outer loop head.
+ };
+
+ PrepareNestedLoopsWhile_While();
+ PrepareMIRs(mirs);
+ PerformSuspendCheckElimination();
+ ASSERT_TRUE(IsBackEdge(6u, 5u));
+ EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u));
+ ASSERT_TRUE(IsBackEdge(7u, 4u));
+ EXPECT_TRUE(IsSuspendCheckEdge(7u, 4u));
+}
+
+TEST_F(SuspendCheckEliminationTest, While_While_InvokeInOuterLoopHead) {
+ static const MIRDef mirs[] = {
+ DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in outer loop head.
+ DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop.
+ DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop.
+ DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head.
+ DEF_OTHER0(7u, Instruction::GOTO), // Edge back to outer loop head.
+ };
+
+ PrepareNestedLoopsWhile_While();
+ PrepareMIRs(mirs);
+ PerformSuspendCheckElimination();
+ ASSERT_TRUE(IsBackEdge(6u, 5u));
+ EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u));
+ ASSERT_TRUE(IsBackEdge(7u, 4u));
+ EXPECT_FALSE(IsSuspendCheckEdge(7u, 4u));
+}
+
+TEST_F(SuspendCheckEliminationTest, While_While_InvokeInOuterLoopBody) {
+ static const MIRDef mirs[] = {
+ DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop.
+ DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop.
+ DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head.
+ DEF_INVOKE(7u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in outer loop body.
+ DEF_OTHER0(7u, Instruction::GOTO), // Edge back to outer loop head.
+ };
+
+ PrepareNestedLoopsWhile_While();
+ PrepareMIRs(mirs);
+ PerformSuspendCheckElimination();
+ ASSERT_TRUE(IsBackEdge(6u, 5u));
+ EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u));
+ ASSERT_TRUE(IsBackEdge(7u, 4u));
+ EXPECT_FALSE(IsSuspendCheckEdge(7u, 4u));
+}
+
+TEST_F(SuspendCheckEliminationTest, While_While_InvokeInInnerLoopHead) {
+ static const MIRDef mirs[] = {
+ DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop.
+ DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in inner loop head.
+ DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop.
+ DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head.
+ DEF_OTHER0(7u, Instruction::GOTO), // Edge back to outer loop head.
+ };
+
+ PrepareNestedLoopsWhile_While();
+ PrepareMIRs(mirs);
+ PerformSuspendCheckElimination();
+ ASSERT_TRUE(IsBackEdge(6u, 5u));
+ EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u));
+ ASSERT_TRUE(IsBackEdge(7u, 4u));
+ EXPECT_FALSE(IsSuspendCheckEdge(7u, 4u));
+}
+
+TEST_F(SuspendCheckEliminationTest, While_While_InvokeInInnerLoopBody) {
+ static const MIRDef mirs[] = {
+ DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop.
+ DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop.
+ DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in inner loop body.
+ DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head.
+ DEF_OTHER0(7u, Instruction::GOTO), // Edge back to outer loop head.
+ };
+
+ PrepareNestedLoopsWhile_While();
+ PrepareMIRs(mirs);
+ PerformSuspendCheckElimination();
+ ASSERT_TRUE(IsBackEdge(6u, 5u));
+ EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u));
+ ASSERT_TRUE(IsBackEdge(7u, 4u));
+ EXPECT_TRUE(IsSuspendCheckEdge(7u, 4u));
+}
+
+TEST_F(SuspendCheckEliminationTest, While_WhileWhile_InvokeInFirstInnerLoopHead) {
+ static const MIRDef mirs[] = {
+ DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop.
+ DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in first inner loop head.
+ DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 1.
+ DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head.
+ DEF_OTHER1(7u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 2.
+ DEF_OTHER0(8u, Instruction::GOTO), // Edge back to inner loop 2 head.
+ DEF_OTHER0(9u, Instruction::GOTO), // Edge back to outer loop head.
+ };
+
+ PrepareNestedLoopsWhile_WhileWhile();
+ PrepareMIRs(mirs);
+ PerformSuspendCheckElimination();
+ ASSERT_TRUE(IsBackEdge(6u, 5u));
+ EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u));
+ ASSERT_TRUE(IsBackEdge(8u, 7u));
+ EXPECT_TRUE(IsSuspendCheckEdge(8u, 7u));
+ ASSERT_TRUE(IsBackEdge(9u, 4u));
+ EXPECT_FALSE(IsSuspendCheckEdge(9u, 4u));
+}
+
+TEST_F(SuspendCheckEliminationTest, While_WhileWhile_InvokeInFirstInnerLoopBody) {
+ static const MIRDef mirs[] = {
+ DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop.
+ DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 1.
+ DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in first inner loop body.
+ DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head.
+ DEF_OTHER1(7u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 2.
+ DEF_OTHER0(8u, Instruction::GOTO), // Edge back to inner loop 2 head.
+ DEF_OTHER0(9u, Instruction::GOTO), // Edge back to outer loop head.
+ };
+
+ PrepareNestedLoopsWhile_WhileWhile();
+ PrepareMIRs(mirs);
+ PerformSuspendCheckElimination();
+ ASSERT_TRUE(IsBackEdge(6u, 5u));
+ EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u));
+ ASSERT_TRUE(IsBackEdge(8u, 7u));
+ EXPECT_TRUE(IsSuspendCheckEdge(8u, 7u));
+ ASSERT_TRUE(IsBackEdge(9u, 4u));
+ EXPECT_TRUE(IsSuspendCheckEdge(9u, 4u));
+}
+
+TEST_F(SuspendCheckEliminationTest, While_WhileWhile_WithExtraEdge_InvokeInFirstInnerLoopBody) {
+ static const MIRDef mirs[] = {
+ DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop.
+ DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 1.
+ DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in first inner loop body.
+ DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head.
+ DEF_OTHER1(7u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 2.
+ DEF_OTHER0(8u, Instruction::GOTO), // Edge back to inner loop 2 head.
+ DEF_OTHER0(9u, Instruction::GOTO), // Edge back to outer loop head.
+ };
+
+ PrepareNestedLoopsWhile_WhileWhile_WithExtraEdge();
+ PrepareMIRs(mirs);
+ PerformSuspendCheckElimination();
+ ASSERT_TRUE(IsBackEdge(6u, 5u));
+ EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u));
+ ASSERT_TRUE(IsBackEdge(8u, 7u));
+ EXPECT_TRUE(IsSuspendCheckEdge(8u, 7u)); // Unaffected by the extra edge.
+ ASSERT_TRUE(IsBackEdge(9u, 4u));
+ EXPECT_TRUE(IsSuspendCheckEdge(9u, 4u));
+}
+
+TEST_F(SuspendCheckEliminationTest, While_WhileWhile_WithExtraEdge_InvokeInSecondInnerLoopHead) {
+ static const MIRDef mirs[] = {
+ DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop.
+ DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 1.
+ DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head.
+ DEF_INVOKE(7u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in second inner loop head.
+ DEF_OTHER1(7u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 2.
+ DEF_OTHER0(8u, Instruction::GOTO), // Edge back to inner loop 2 head.
+ DEF_OTHER0(9u, Instruction::GOTO), // Edge back to outer loop head.
+ };
+
+ PrepareNestedLoopsWhile_WhileWhile_WithExtraEdge();
+ PrepareMIRs(mirs);
+ PerformSuspendCheckElimination();
+ ASSERT_TRUE(IsBackEdge(6u, 5u));
+ EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u));
+ ASSERT_TRUE(IsBackEdge(8u, 7u));
+ EXPECT_FALSE(IsSuspendCheckEdge(8u, 7u)); // Unaffected by the extra edge.
+ ASSERT_TRUE(IsBackEdge(9u, 4u));
+ EXPECT_FALSE(IsSuspendCheckEdge(9u, 4u));
+}
} // namespace art
diff --git a/compiler/dex/pass.h b/compiler/dex/pass.h
index d3e54a0..0def056 100644
--- a/compiler/dex/pass.h
+++ b/compiler/dex/pass.h
@@ -19,14 +19,12 @@
#include <string>
-#include "compiler_ir.h"
#include "base/logging.h"
namespace art {
// Forward declarations.
class BasicBlock;
-struct CompilationUnit;
class Pass;
// Empty Pass Data Class, can be extended by any pass extending the base Pass class.
@@ -89,21 +87,6 @@
UNREACHABLE();
}
- static void BasePrintMessage(CompilationUnit* c_unit, const char* pass_name, const char* message, ...) {
- // Check if we want to log something or not.
- if (c_unit->print_pass) {
- // Stringify the message.
- va_list args;
- va_start(args, message);
- std::string stringified_message;
- StringAppendV(&stringified_message, message, args);
- va_end(args);
-
- // Log the message and ensure to include pass name.
- LOG(INFO) << pass_name << ": " << stringified_message;
- }
- }
-
protected:
/** @brief The pass name: used for searching for a pass when running a particular pass or debugging. */
const char* const pass_name_;
diff --git a/compiler/dex/pass_driver.h b/compiler/dex/pass_driver.h
index 8a3eae1..671bcec 100644
--- a/compiler/dex/pass_driver.h
+++ b/compiler/dex/pass_driver.h
@@ -18,22 +18,17 @@
#define ART_COMPILER_DEX_PASS_DRIVER_H_
#include <vector>
-#include "pass.h"
-#include "safe_map.h"
-// Forward Declarations.
-class Pass;
-class PassDriver;
+#include "base/logging.h"
+#include "pass.h"
+#include "pass_manager.h"
+
namespace art {
-/**
- * @brief Helper function to create a single instance of a given Pass and can be shared across
- * the threads.
- */
-template <typename PassType>
-const Pass* GetPassInstance() {
- static const PassType pass;
- return &pass;
-}
+
+class Pass;
+class PassDataHolder;
+class PassDriver;
+class PassManager;
// Empty holder for the constructor.
class PassDriverDataHolder {
@@ -43,11 +38,11 @@
* @class PassDriver
* @brief PassDriver is the wrapper around all Pass instances in order to execute them
*/
-template <typename PassDriverType>
class PassDriver {
public:
- explicit PassDriver() {
- InitializePasses();
+ explicit PassDriver(const PassManager* const pass_manager) : pass_manager_(pass_manager) {
+ pass_list_ = *pass_manager_->GetDefaultPassList();
+ DCHECK(!pass_list_.empty());
}
virtual ~PassDriver() {
@@ -58,12 +53,12 @@
*/
void InsertPass(const Pass* new_pass) {
DCHECK(new_pass != nullptr);
- DCHECK(new_pass->GetName() != nullptr && new_pass->GetName()[0] != 0);
+ DCHECK(new_pass->GetName() != nullptr);
+ DCHECK_NE(new_pass->GetName()[0], 0);
// It is an error to override an existing pass.
DCHECK(GetPass(new_pass->GetName()) == nullptr)
<< "Pass name " << new_pass->GetName() << " already used.";
-
// Now add to the list.
pass_list_.push_back(new_pass);
}
@@ -74,7 +69,8 @@
*/
virtual bool RunPass(const char* pass_name) {
// Paranoid: c_unit cannot be nullptr and we need a pass name.
- DCHECK(pass_name != nullptr && pass_name[0] != 0);
+ DCHECK(pass_name != nullptr);
+ DCHECK_NE(pass_name[0], 0);
const Pass* cur_pass = GetPass(pass_name);
@@ -108,21 +104,6 @@
return nullptr;
}
- static void CreateDefaultPassList(const std::string& disable_passes) {
- // Insert each pass from g_passes into g_default_pass_list.
- PassDriverType::g_default_pass_list.clear();
- PassDriverType::g_default_pass_list.reserve(PassDriver<PassDriverType>::g_passes_size);
- for (uint16_t i = 0; i < PassDriver<PassDriverType>::g_passes_size; ++i) {
- const Pass* pass = PassDriver<PassDriverType>::g_passes[i];
- // Check if we should disable this pass.
- if (disable_passes.find(pass->GetName()) != std::string::npos) {
- LOG(INFO) << "Skipping " << pass->GetName();
- } else {
- PassDriver<PassDriverType>::g_default_pass_list.push_back(pass);
- }
- }
- }
-
/**
* @brief Run a pass using the Pass itself.
* @param time_split do we want a time split request(default: false)?
@@ -130,57 +111,7 @@
*/
virtual bool RunPass(const Pass* pass, bool time_split = false) = 0;
- /**
- * @brief Print the pass names of all the passes available.
- */
- static void PrintPassNames() {
- LOG(INFO) << "Loop Passes are:";
-
- for (const Pass* cur_pass : PassDriver<PassDriverType>::g_default_pass_list) {
- LOG(INFO) << "\t-" << cur_pass->GetName();
- }
- }
-
- /**
- * @brief Gets the list of passes currently schedule to execute.
- * @return pass_list_
- */
- std::vector<const Pass*>& GetPasses() {
- return pass_list_;
- }
-
- static void SetPrintAllPasses() {
- default_print_passes_ = true;
- }
-
- static void SetDumpPassList(const std::string& list) {
- dump_pass_list_ = list;
- }
-
- static void SetPrintPassList(const std::string& list) {
- print_pass_list_ = list;
- }
-
- /**
- * @brief Used to set a string that contains the overridden pass options.
- * @details An overridden pass option means that the pass uses this option
- * instead of using its default option.
- * @param s The string passed by user with overridden options. The string is in format
- * Pass1Name:Pass1Option:Pass1Setting,Pass2Name:Pass2Option::Pass2Setting
- */
- static void SetOverriddenPassOptions(const std::string& s) {
- overridden_pass_options_list_ = s;
- }
-
- void SetDefaultPasses() {
- pass_list_ = PassDriver<PassDriverType>::g_default_pass_list;
- }
-
protected:
- virtual void InitializePasses() {
- SetDefaultPasses();
- }
-
/**
* @brief Apply a patch: perform start/work/end functions.
*/
@@ -189,6 +120,7 @@
DispatchPass(pass);
pass->End(data);
}
+
/**
* @brief Dispatch a patch.
* Gives the ability to add logic when running the patch.
@@ -197,29 +129,11 @@
UNUSED(pass);
}
- /** @brief List of passes: provides the order to execute the passes. */
+ /** @brief List of passes: provides the order to execute the passes.
+ * Passes are owned by pass_manager_. */
std::vector<const Pass*> pass_list_;
- /** @brief The number of passes within g_passes. */
- static const uint16_t g_passes_size;
-
- /** @brief The number of passes within g_passes. */
- static const Pass* const g_passes[];
-
- /** @brief The default pass list is used to initialize pass_list_. */
- static std::vector<const Pass*> g_default_pass_list;
-
- /** @brief Do we, by default, want to be printing the log messages? */
- static bool default_print_passes_;
-
- /** @brief What are the passes we want to be printing the log messages? */
- static std::string print_pass_list_;
-
- /** @brief What are the passes we want to be dumping the CFG? */
- static std::string dump_pass_list_;
-
- /** @brief String of all options that should be overridden for selected passes */
- static std::string overridden_pass_options_list_;
+ const PassManager* const pass_manager_;
};
} // namespace art
diff --git a/compiler/dex/pass_driver_me.h b/compiler/dex/pass_driver_me.h
index 7bfaf82..94eef22 100644
--- a/compiler/dex/pass_driver_me.h
+++ b/compiler/dex/pass_driver_me.h
@@ -19,19 +19,25 @@
#include <cstdlib>
#include <cstring>
+
#include "bb_optimizations.h"
#include "dataflow_iterator.h"
#include "dataflow_iterator-inl.h"
+#include "dex_flags.h"
#include "pass_driver.h"
+#include "pass_manager.h"
#include "pass_me.h"
+#include "safe_map.h"
namespace art {
-template <typename PassDriverType>
-class PassDriverME: public PassDriver<PassDriverType> {
+class PassManager;
+class PassManagerOptions;
+
+class PassDriverME: public PassDriver {
public:
- explicit PassDriverME(CompilationUnit* cu)
- : pass_me_data_holder_(), dump_cfg_folder_("/sdcard/") {
+ explicit PassDriverME(const PassManager* const pass_manager, CompilationUnit* cu)
+ : PassDriver(pass_manager), pass_me_data_holder_(), dump_cfg_folder_("/sdcard/") {
pass_me_data_holder_.bb = nullptr;
pass_me_data_holder_.c_unit = cu;
}
@@ -81,7 +87,7 @@
}
}
- bool RunPass(const Pass* pass, bool time_split) {
+ bool RunPass(const Pass* pass, bool time_split) OVERRIDE {
// Paranoid: c_unit and pass cannot be nullptr, and the pass should have a name
DCHECK(pass != nullptr);
DCHECK(pass->GetName() != nullptr && pass->GetName()[0] != 0);
@@ -95,15 +101,17 @@
// First, work on determining pass verbosity.
bool old_print_pass = c_unit->print_pass;
- c_unit->print_pass = PassDriver<PassDriverType>::default_print_passes_;
- const char* print_pass_list = PassDriver<PassDriverType>::print_pass_list_.c_str();
- if (print_pass_list != nullptr && strstr(print_pass_list, pass->GetName()) != nullptr) {
+ c_unit->print_pass = pass_manager_->GetOptions().GetPrintAllPasses();
+ auto* const options = &pass_manager_->GetOptions();
+ const std::string& print_pass_list = options->GetPrintPassList();
+ if (!print_pass_list.empty() && strstr(print_pass_list.c_str(), pass->GetName()) != nullptr) {
c_unit->print_pass = true;
}
- // Next, check if there are any overridden settings for the pass that change default configuration.
+ // Next, check if there are any overridden settings for the pass that change default
+ // configuration.
c_unit->overridden_pass_options.clear();
- FillOverriddenPassSettings(pass->GetName(), c_unit->overridden_pass_options);
+ FillOverriddenPassSettings(options, pass->GetName(), c_unit->overridden_pass_options);
if (c_unit->print_pass) {
for (auto setting_it : c_unit->overridden_pass_options) {
LOG(INFO) << "Overridden option \"" << setting_it.first << ":"
@@ -117,13 +125,12 @@
// Applying the pass: first start, doWork, and end calls.
this->ApplyPass(&pass_me_data_holder_, pass);
- bool should_dump = ((c_unit->enable_debug & (1 << kDebugDumpCFG)) != 0);
+ bool should_dump = (c_unit->enable_debug & (1 << kDebugDumpCFG)) != 0;
- const char* dump_pass_list = PassDriver<PassDriverType>::dump_pass_list_.c_str();
-
- if (dump_pass_list != nullptr) {
- bool found = strstr(dump_pass_list, pass->GetName());
- should_dump = (should_dump || found);
+ const std::string& dump_pass_list = pass_manager_->GetOptions().GetDumpPassList();
+ if (!dump_pass_list.empty()) {
+ const bool found = strstr(dump_pass_list.c_str(), pass->GetName());
+ should_dump = should_dump || found;
}
if (should_dump) {
@@ -153,22 +160,23 @@
return should_apply_pass;
}
- const char* GetDumpCFGFolder() const {
- return dump_cfg_folder_;
- }
-
- static void PrintPassOptions() {
- for (auto pass : PassDriver<PassDriverType>::g_default_pass_list) {
+ static void PrintPassOptions(PassManager* manager) {
+ for (const auto* pass : *manager->GetDefaultPassList()) {
const PassME* me_pass = down_cast<const PassME*>(pass);
if (me_pass->HasOptions()) {
LOG(INFO) << "Pass options for \"" << me_pass->GetName() << "\" are:";
- SafeMap<const std::string, int> overridden_settings;
- FillOverriddenPassSettings(me_pass->GetName(), overridden_settings);
+ SafeMap<const std::string, const OptionContent> overridden_settings;
+ FillOverriddenPassSettings(&manager->GetOptions(), me_pass->GetName(),
+ overridden_settings);
me_pass->PrintPassOptions(overridden_settings);
}
}
}
+ const char* GetDumpCFGFolder() const {
+ return dump_cfg_folder_;
+ }
+
protected:
/** @brief The data holder that contains data needed for the PassDriverME. */
PassMEDataHolder pass_me_data_holder_;
@@ -197,12 +205,15 @@
}
/**
- * @brief Fills the settings_to_fill by finding all of the applicable options in the overridden_pass_options_list_.
+ * @brief Fills the settings_to_fill by finding all of the applicable options in the
+ * overridden_pass_options_list_.
* @param pass_name The pass name for which to fill settings.
- * @param settings_to_fill Fills the options to contain the mapping of name of option to the new configuration.
+ * @param settings_to_fill Fills the options to contain the mapping of name of option to the new
+ * configuration.
*/
- static void FillOverriddenPassSettings(const char* pass_name, SafeMap<const std::string, int>& settings_to_fill) {
- const std::string& settings = PassDriver<PassDriverType>::overridden_pass_options_list_;
+ static void FillOverriddenPassSettings(const PassManagerOptions* options, const char* pass_name,
+ SafeMap<const std::string, const OptionContent>& settings_to_fill) {
+ const std::string& settings = options->GetOverriddenPassOptions();
const size_t settings_len = settings.size();
// Before anything, check if we care about anything right now.
@@ -274,15 +285,28 @@
continue;
}
- // Get the actual setting itself. Strtol is being used to convert because it is
- // exception safe. If the input is not sane, it will set a setting of 0.
- std::string setting_string = settings.substr(setting_pos, next_configuration_separator - setting_pos);
- int setting = std::strtol(setting_string.c_str(), 0, 0);
+ // Get the actual setting itself.
+ std::string setting_string =
+ settings.substr(setting_pos, next_configuration_separator - setting_pos);
- std::string setting_name = settings.substr(setting_name_pos, setting_pos - setting_name_pos - 1);
+ std::string setting_name =
+ settings.substr(setting_name_pos, setting_pos - setting_name_pos - 1);
- settings_to_fill.Put(setting_name, setting);
+ // We attempt to convert the option value to integer. Strtoll is being used to
+ // convert because it is exception safe.
+ char* end_ptr = nullptr;
+ const char* setting_ptr = setting_string.c_str();
+ DCHECK(setting_ptr != nullptr); // Paranoid: setting_ptr must be a valid pointer.
+ int64_t int_value = strtoll(setting_ptr, &end_ptr, 0);
+ DCHECK(end_ptr != nullptr); // Paranoid: end_ptr must be set by the strtoll call.
+ // If strtoll call succeeded, the option is now considered as integer.
+ if (*setting_ptr != '\0' && end_ptr != setting_ptr && *end_ptr == '\0') {
+ settings_to_fill.Put(setting_name, OptionContent(int_value));
+ } else {
+ // Otherwise, it is considered as a string.
+ settings_to_fill.Put(setting_name, OptionContent(setting_string.c_str()));
+ }
search_pos = next_configuration_separator;
} while (true);
}
diff --git a/compiler/dex/pass_driver_me_opts.cc b/compiler/dex/pass_driver_me_opts.cc
index a2bf8b4..8c8bde6 100644
--- a/compiler/dex/pass_driver_me_opts.cc
+++ b/compiler/dex/pass_driver_me_opts.cc
@@ -14,81 +14,51 @@
* limitations under the License.
*/
+#include "pass_driver_me_opts.h"
+
+#include "base/logging.h"
#include "base/macros.h"
#include "bb_optimizations.h"
-#include "compiler_internals.h"
#include "dataflow_iterator.h"
#include "dataflow_iterator-inl.h"
#include "pass_driver_me_opts.h"
+#include "pass_manager.h"
#include "post_opt_passes.h"
namespace art {
-/*
- * Create the pass list. These passes are immutable and are shared across the threads.
- *
- * Advantage is that there will be no race conditions here.
- * Disadvantage is the passes can't change their internal states depending on CompilationUnit:
- * - This is not yet an issue: no current pass would require it.
- */
-// The initial list of passes to be used by the PassDriveMEOpts.
-template<>
-const Pass* const PassDriver<PassDriverMEOpts>::g_passes[] = {
- GetPassInstance<CacheFieldLoweringInfo>(),
- GetPassInstance<CacheMethodLoweringInfo>(),
- GetPassInstance<CalculatePredecessors>(),
- GetPassInstance<DFSOrders>(),
- GetPassInstance<ClassInitCheckElimination>(),
- GetPassInstance<SpecialMethodInliner>(),
- GetPassInstance<NullCheckElimination>(),
- GetPassInstance<BBCombine>(),
- GetPassInstance<CodeLayout>(),
- GetPassInstance<TypeInference>(),
- GetPassInstance<GlobalValueNumberingPass>(),
- GetPassInstance<BBOptimizations>(),
-};
-
-// The number of the passes in the initial list of Passes (g_passes).
-template<>
-uint16_t const PassDriver<PassDriverMEOpts>::g_passes_size =
- arraysize(PassDriver<PassDriverMEOpts>::g_passes);
-
-// The default pass list is used by the PassDriverME instance of PassDriver
-// to initialize pass_list_.
-template<>
-std::vector<const Pass*> PassDriver<PassDriverMEOpts>::g_default_pass_list(
- PassDriver<PassDriverMEOpts>::g_passes,
- PassDriver<PassDriverMEOpts>::g_passes +
- PassDriver<PassDriverMEOpts>::g_passes_size);
-
-// By default, do not have a dump pass list.
-template<>
-std::string PassDriver<PassDriverMEOpts>::dump_pass_list_ = std::string();
-
-// By default, do not have a print pass list.
-template<>
-std::string PassDriver<PassDriverMEOpts>::print_pass_list_ = std::string();
-
-// By default, we do not print the pass' information.
-template<>
-bool PassDriver<PassDriverMEOpts>::default_print_passes_ = false;
-
-// By default, there are no overridden pass settings.
-template<>
-std::string PassDriver<PassDriverMEOpts>::overridden_pass_options_list_ = std::string();
+void PassDriverMEOpts::SetupPasses(PassManager* pass_manager) {
+ /*
+ * Create the pass list. These passes are immutable and are shared across the threads.
+ *
+ * Advantage is that there will be no race conditions here.
+ * Disadvantage is the passes can't change their internal states depending on CompilationUnit:
+ * - This is not yet an issue: no current pass would require it.
+ */
+ pass_manager->AddPass(new CacheFieldLoweringInfo);
+ pass_manager->AddPass(new CacheMethodLoweringInfo);
+ pass_manager->AddPass(new CalculatePredecessors);
+ pass_manager->AddPass(new DFSOrders);
+ pass_manager->AddPass(new ClassInitCheckElimination);
+ pass_manager->AddPass(new SpecialMethodInliner);
+ pass_manager->AddPass(new NullCheckElimination);
+ pass_manager->AddPass(new BBCombine);
+ pass_manager->AddPass(new CodeLayout);
+ pass_manager->AddPass(new GlobalValueNumberingPass);
+ pass_manager->AddPass(new ConstantPropagation);
+ pass_manager->AddPass(new MethodUseCount);
+ pass_manager->AddPass(new BBOptimizations);
+ pass_manager->AddPass(new SuspendCheckElimination);
+}
void PassDriverMEOpts::ApplyPass(PassDataHolder* data, const Pass* pass) {
- const PassME* pass_me = down_cast<const PassME*> (pass);
+ const PassME* const pass_me = down_cast<const PassME*>(pass);
DCHECK(pass_me != nullptr);
-
- PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
-
+ PassMEDataHolder* const pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
// Set to dirty.
pass_me_data_holder->dirty = true;
-
// First call the base class' version.
PassDriver::ApplyPass(data, pass);
-
// Now we care about flags.
if ((pass_me->GetFlag(kOptimizationBasicBlockChange) == true) ||
(pass_me->GetFlag(kOptimizationDefUsesChange) == true)) {
diff --git a/compiler/dex/pass_driver_me_opts.h b/compiler/dex/pass_driver_me_opts.h
index 0a5b5ae..b930d02 100644
--- a/compiler/dex/pass_driver_me_opts.h
+++ b/compiler/dex/pass_driver_me_opts.h
@@ -25,19 +25,26 @@
struct CompilationUnit;
class Pass;
class PassDataHolder;
+class PassManager;
-class PassDriverMEOpts : public PassDriverME<PassDriverMEOpts> {
+class PassDriverMEOpts : public PassDriverME {
public:
- explicit PassDriverMEOpts(CompilationUnit* cu):PassDriverME<PassDriverMEOpts>(cu) {
+ explicit PassDriverMEOpts(const PassManager* const manager, CompilationUnit* cu)
+ : PassDriverME(manager, cu) {
}
~PassDriverMEOpts() {
}
/**
+ * @brief Write and allocate corresponding passes into the pass manager.
+ */
+ static void SetupPasses(PassManager* pass_manasger);
+
+ /**
* @brief Apply a patch: perform start/work/end functions.
*/
- virtual void ApplyPass(PassDataHolder* data, const Pass* pass);
+ virtual void ApplyPass(PassDataHolder* data, const Pass* pass) OVERRIDE;
};
} // namespace art
diff --git a/compiler/dex/pass_driver_me_post_opt.cc b/compiler/dex/pass_driver_me_post_opt.cc
index e6238e9..a8b8a54 100644
--- a/compiler/dex/pass_driver_me_post_opt.cc
+++ b/compiler/dex/pass_driver_me_post_opt.cc
@@ -14,66 +14,35 @@
* limitations under the License.
*/
+#include "pass_driver_me_post_opt.h"
+
#include "base/macros.h"
#include "post_opt_passes.h"
-#include "compiler_internals.h"
-#include "pass_driver_me_post_opt.h"
+#include "pass_manager.h"
namespace art {
-/*
- * Create the pass list. These passes are immutable and are shared across the threads.
- *
- * Advantage is that there will be no race conditions here.
- * Disadvantage is the passes can't change their internal states depending on CompilationUnit:
- * - This is not yet an issue: no current pass would require it.
- */
-// The initial list of passes to be used by the PassDriveMEPostOpt.
-template<>
-const Pass* const PassDriver<PassDriverMEPostOpt>::g_passes[] = {
- GetPassInstance<InitializeData>(),
- GetPassInstance<ClearPhiInstructions>(),
- GetPassInstance<DFSOrders>(),
- GetPassInstance<BuildDomination>(),
- GetPassInstance<TopologicalSortOrders>(),
- GetPassInstance<DefBlockMatrix>(),
- GetPassInstance<CreatePhiNodes>(),
- GetPassInstance<ClearVisitedFlag>(),
- GetPassInstance<SSAConversion>(),
- GetPassInstance<PhiNodeOperands>(),
- GetPassInstance<ConstantPropagation>(),
- GetPassInstance<PerformInitRegLocations>(),
- GetPassInstance<MethodUseCount>(),
- GetPassInstance<FreeData>(),
-};
-
-// The number of the passes in the initial list of Passes (g_passes).
-template<>
-uint16_t const PassDriver<PassDriverMEPostOpt>::g_passes_size =
- arraysize(PassDriver<PassDriverMEPostOpt>::g_passes);
-
-// The default pass list is used by the PassDriverME instance of PassDriver
-// to initialize pass_list_.
-template<>
-std::vector<const Pass*> PassDriver<PassDriverMEPostOpt>::g_default_pass_list(
- PassDriver<PassDriverMEPostOpt>::g_passes,
- PassDriver<PassDriverMEPostOpt>::g_passes +
- PassDriver<PassDriverMEPostOpt>::g_passes_size);
-
-// By default, do not have a dump pass list.
-template<>
-std::string PassDriver<PassDriverMEPostOpt>::dump_pass_list_ = std::string();
-
-// By default, do not have a print pass list.
-template<>
-std::string PassDriver<PassDriverMEPostOpt>::print_pass_list_ = std::string();
-
-// By default, we do not print the pass' information.
-template<>
-bool PassDriver<PassDriverMEPostOpt>::default_print_passes_ = false;
-
-// By default, there are no overridden pass settings.
-template<>
-std::string PassDriver<PassDriverMEPostOpt>::overridden_pass_options_list_ = std::string();
+void PassDriverMEPostOpt::SetupPasses(PassManager* pass_manager) {
+ /*
+ * Create the pass list. These passes are immutable and are shared across the threads.
+ *
+ * Advantage is that there will be no race conditions here.
+ * Disadvantage is the passes can't change their internal states depending on CompilationUnit:
+ * - This is not yet an issue: no current pass would require it.
+ */
+ // The initial list of passes to be used by the PassDriveMEPostOpt.
+ pass_manager->AddPass(new DFSOrders);
+ pass_manager->AddPass(new BuildDomination);
+ pass_manager->AddPass(new TopologicalSortOrders);
+ pass_manager->AddPass(new InitializeSSATransformation);
+ pass_manager->AddPass(new ClearPhiInstructions);
+ pass_manager->AddPass(new DefBlockMatrix);
+ pass_manager->AddPass(new FindPhiNodeBlocksPass);
+ pass_manager->AddPass(new SSAConversion);
+ pass_manager->AddPass(new PhiNodeOperands);
+ pass_manager->AddPass(new PerformInitRegLocations);
+ pass_manager->AddPass(new TypeInference);
+ pass_manager->AddPass(new FinishSSATransformation);
+}
} // namespace art
diff --git a/compiler/dex/pass_driver_me_post_opt.h b/compiler/dex/pass_driver_me_post_opt.h
index 574a6ba..9e03c4e 100644
--- a/compiler/dex/pass_driver_me_post_opt.h
+++ b/compiler/dex/pass_driver_me_post_opt.h
@@ -26,13 +26,19 @@
class Pass;
class PassDataHolder;
-class PassDriverMEPostOpt : public PassDriverME<PassDriverMEPostOpt> {
+class PassDriverMEPostOpt : public PassDriverME {
public:
- explicit PassDriverMEPostOpt(CompilationUnit* cu) : PassDriverME<PassDriverMEPostOpt>(cu) {
+ explicit PassDriverMEPostOpt(const PassManager* const manager, CompilationUnit* cu)
+ : PassDriverME(manager, cu) {
}
~PassDriverMEPostOpt() {
}
+
+ /**
+ * @brief Write and allocate corresponding passes into the pass manager.
+ */
+ static void SetupPasses(PassManager* pass_manager);
};
} // namespace art
diff --git a/compiler/dex/pass_manager.cc b/compiler/dex/pass_manager.cc
new file mode 100644
index 0000000..6d58f65
--- /dev/null
+++ b/compiler/dex/pass_manager.cc
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#include "pass_manager.h"
+
+#include "base/stl_util.h"
+#include "pass_me.h"
+
+namespace art {
+
+PassManager::PassManager(const PassManagerOptions& options) : options_(options) {
+}
+
+PassManager::~PassManager() {
+ STLDeleteElements(&passes_);
+}
+
+void PassManager::CreateDefaultPassList() {
+ default_pass_list_.clear();
+ // Add each pass which isn't disabled into default_pass_list_.
+ for (const auto* pass : passes_) {
+ if (options_.GetDisablePassList().find(pass->GetName()) != std::string::npos) {
+ LOG(INFO) << "Skipping disabled pass " << pass->GetName();
+ } else {
+ default_pass_list_.push_back(pass);
+ }
+ }
+}
+
+void PassManager::PrintPassNames() const {
+ LOG(INFO) << "Loop Passes are:";
+ for (const Pass* cur_pass : default_pass_list_) {
+ LOG(INFO) << "\t-" << cur_pass->GetName();
+ }
+}
+
+} // namespace art
diff --git a/compiler/dex/pass_manager.h b/compiler/dex/pass_manager.h
new file mode 100644
index 0000000..68e488d
--- /dev/null
+++ b/compiler/dex/pass_manager.h
@@ -0,0 +1,150 @@
+/*
+ * 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_DEX_PASS_MANAGER_H_
+#define ART_COMPILER_DEX_PASS_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+
+namespace art {
+
+class Pass;
+
+class PassManagerOptions {
+ public:
+ PassManagerOptions()
+ : default_print_passes_(false),
+ print_pass_names_(false),
+ print_pass_options_(false) {
+ }
+ explicit PassManagerOptions(const PassManagerOptions&) = default;
+
+ void SetPrintPassNames(bool b) {
+ print_pass_names_ = b;
+ }
+
+ void SetPrintAllPasses() {
+ default_print_passes_ = true;
+ }
+ bool GetPrintAllPasses() const {
+ return default_print_passes_;
+ }
+
+ void SetDisablePassList(const std::string& list) {
+ disable_pass_list_ = list;
+ }
+ const std::string& GetDisablePassList() const {
+ return disable_pass_list_;
+ }
+
+ void SetPrintPassList(const std::string& list) {
+ print_pass_list_ = list;
+ }
+ const std::string& GetPrintPassList() const {
+ return print_pass_list_;
+ }
+
+ void SetDumpPassList(const std::string& list) {
+ dump_pass_list_ = list;
+ }
+ const std::string& GetDumpPassList() const {
+ return dump_pass_list_;
+ }
+
+ /**
+ * @brief Used to set a string that contains the overridden pass options.
+ * @details An overridden pass option means that the pass uses this option
+ * instead of using its default option.
+ * @param s The string passed by user with overridden options. The string is in format
+ * Pass1Name:Pass1Option:Pass1Setting,Pass2Name:Pass2Option::Pass2Setting
+ */
+ void SetOverriddenPassOptions(const std::string& list) {
+ overridden_pass_options_list_ = list;
+ }
+ const std::string& GetOverriddenPassOptions() const {
+ return overridden_pass_options_list_;
+ }
+
+ void SetPrintPassOptions(bool b) {
+ print_pass_options_ = b;
+ }
+ bool GetPrintPassOptions() const {
+ return print_pass_options_;
+ }
+
+ private:
+ /** @brief Do we, by default, want to be printing the log messages? */
+ bool default_print_passes_;
+
+ /** @brief What are the passes we want to be printing the log messages? */
+ std::string print_pass_list_;
+
+ /** @brief What are the passes we want to be dumping the CFG? */
+ std::string dump_pass_list_;
+
+ /** @brief String of all options that should be overridden for selected passes */
+ std::string overridden_pass_options_list_;
+
+ /** @brief String of all options that should be overridden for selected passes */
+ std::string disable_pass_list_;
+
+ /** @brief Whether or not we print all the passes when we create the pass manager */
+ bool print_pass_names_;
+
+ /** @brief Whether or not we print all the pass options when we create the pass manager */
+ bool print_pass_options_;
+};
+
+/**
+ * @class PassManager
+ * @brief Owns passes
+ */
+class PassManager {
+ public:
+ explicit PassManager(const PassManagerOptions& options);
+ virtual ~PassManager();
+ void CreateDefaultPassList();
+ void AddPass(const Pass* pass) {
+ passes_.push_back(pass);
+ }
+ /**
+ * @brief Print the pass names of all the passes available.
+ */
+ void PrintPassNames() const;
+ const std::vector<const Pass*>* GetDefaultPassList() const {
+ return &default_pass_list_;
+ }
+ const PassManagerOptions& GetOptions() const {
+ return options_;
+ }
+
+ private:
+ /** @brief The set of possible passes. */
+ std::vector<const Pass*> passes_;
+
+ /** @brief The default pass list is used to initialize pass_list_. */
+ std::vector<const Pass*> default_pass_list_;
+
+ /** @brief Pass manager options. */
+ PassManagerOptions options_;
+
+ DISALLOW_COPY_AND_ASSIGN(PassManager);
+};
+} // namespace art
+#endif // ART_COMPILER_DEX_PASS_MANAGER_H_
diff --git a/compiler/dex/pass_me.h b/compiler/dex/pass_me.h
index d0b450a..d3cf393 100644
--- a/compiler/dex/pass_me.h
+++ b/compiler/dex/pass_me.h
@@ -18,14 +18,17 @@
#define ART_COMPILER_DEX_PASS_ME_H_
#include <string>
+
+#include "base/logging.h"
#include "pass.h"
+#include "compiler_ir.h"
+#include "safe_map.h"
namespace art {
// Forward declarations.
class BasicBlock;
struct CompilationUnit;
-class Pass;
/**
* @brief OptimizationFlag is an enumeration to perform certain tasks for a given pass.
@@ -40,11 +43,11 @@
// Data holder class.
class PassMEDataHolder: public PassDataHolder {
- public:
- CompilationUnit* c_unit;
- BasicBlock* bb;
- void* data; /**< @brief Any data the pass wants to use */
- bool dirty; /**< @brief Has the pass rendered the CFG dirty, requiring post-opt? */
+ public:
+ CompilationUnit* c_unit;
+ BasicBlock* bb;
+ void* data; /**< @brief Any data the pass wants to use */
+ bool dirty; /**< @brief Has the pass rendered the CFG dirty, requiring post-opt? */
};
enum DataFlowAnalysisMode {
@@ -101,8 +104,8 @@
* @details The printing is done using LOG(INFO).
*/
void PrintPassDefaultOptions() const {
- for (auto option_it = default_options_.begin(); option_it != default_options_.end(); option_it++) {
- LOG(INFO) << "\t" << option_it->first << ":" << std::dec << option_it->second;
+ for (const auto& option : default_options_) {
+ LOG(INFO) << "\t" << option.first << ":" << option.second;
}
}
@@ -110,25 +113,49 @@
* @brief Prints the pass options along with either default or overridden setting.
* @param overridden_options The overridden settings for this pass.
*/
- void PrintPassOptions(SafeMap<const std::string, int>& overridden_options) const {
+ void PrintPassOptions(SafeMap<const std::string, const OptionContent>& overridden_options) const {
// We walk through the default options only to get the pass names. We use GetPassOption to
// also consider the overridden ones.
- for (auto option_it = default_options_.begin(); option_it != default_options_.end(); option_it++) {
- LOG(INFO) << "\t" << option_it->first << ":" << std::dec << GetPassOption(option_it->first, overridden_options);
+ for (const auto& option : default_options_) {
+ LOG(INFO) << "\t" << option.first << ":"
+ << GetPassOption(option.first, overridden_options);
}
}
/**
- * @brief Used to obtain the option for a pass.
- * @details Will return the overridden option if it exists or default one.
+ * @brief Used to obtain the option structure for a pass.
+ * @details Will return the overridden option if it exists or default one otherwise.
* @param option_name The name of option whose setting to look for.
* @param c_unit The compilation unit currently being handled.
- * @return Returns the setting for the pass option.
- */
- int GetPassOption(const char* option_name, CompilationUnit* c_unit) const {
+ * @return Returns the option structure containing the option value.
+ */
+ const OptionContent& GetPassOption(const char* option_name, CompilationUnit* c_unit) const {
return GetPassOption(option_name, c_unit->overridden_pass_options);
}
+ /**
+ * @brief Used to obtain the option for a pass as a string.
+ * @details Will return the overridden option if it exists or default one otherwise.
+ * It will return nullptr if the required option value is not a string.
+ * @param option_name The name of option whose setting to look for.
+ * @param c_unit The compilation unit currently being handled.
+ * @return Returns the overridden option if it exists or the default one otherwise.
+ */
+ const char* GetStringPassOption(const char* option_name, CompilationUnit* c_unit) const {
+ return GetStringPassOption(option_name, c_unit->overridden_pass_options);
+ }
+
+ /**
+ * @brief Used to obtain the pass option value as an integer.
+ * @details Will return the overridden option if it exists or default one otherwise.
+ * It will return 0 if the required option value is not an integer.
+ * @param c_unit The compilation unit currently being handled.
+ * @return Returns the overriden option if it exists or the default one otherwise.
+ */
+ int64_t GetIntegerPassOption(const char* option_name, CompilationUnit* c_unit) const {
+ return GetIntegerPassOption(option_name, c_unit->overridden_pass_options);
+ }
+
const char* GetDumpCFGFolder() const {
return dump_cfg_folder_;
}
@@ -138,29 +165,51 @@
}
protected:
- int GetPassOption(const char* option_name, const SafeMap<const std::string, int>& overridden_options) const {
+ const OptionContent& GetPassOption(const char* option_name,
+ const SafeMap<const std::string, const OptionContent>& overridden_options) const {
+ DCHECK(option_name != nullptr);
+
// First check if there are any overridden settings.
auto overridden_it = overridden_options.find(std::string(option_name));
if (overridden_it != overridden_options.end()) {
return overridden_it->second;
+ } else {
+ // Otherwise, there must be a default value for this option name.
+ auto default_it = default_options_.find(option_name);
+ // An invalid option is being requested.
+ if (default_it == default_options_.end()) {
+ LOG(FATAL) << "Fatal: Cannot find an option named \"" << option_name << "\"";
+ }
+
+ return default_it->second;
+ }
+ }
+
+ const char* GetStringPassOption(const char* option_name,
+ const SafeMap<const std::string, const OptionContent>& overridden_options) const {
+ const OptionContent& option_content = GetPassOption(option_name, overridden_options);
+ if (option_content.type != OptionContent::kString) {
+ return nullptr;
}
- // Next check the default options.
- auto default_it = default_options_.find(option_name);
+ return option_content.GetString();
+ }
- if (default_it == default_options_.end()) {
- // An invalid option is being requested.
- DCHECK(false);
+ int64_t GetIntegerPassOption(const char* option_name,
+ const SafeMap<const std::string, const OptionContent>& overridden_options) const {
+ const OptionContent& option_content = GetPassOption(option_name, overridden_options);
+ if (option_content.type != OptionContent::kInteger) {
return 0;
}
- return default_it->second;
+ return option_content.GetInteger();
}
/** @brief Type of traversal: determines the order to execute the pass on the BasicBlocks. */
const DataFlowAnalysisMode traversal_type_;
- /** @brief Flags for additional directives: used to determine if a particular post-optimization pass is necessary. */
+ /** @brief Flags for additional directives: used to determine if a particular
+ * post-optimization pass is necessary. */
const unsigned int flags_;
/** @brief CFG Dump Folder: what sub-folder to use for dumping the CFGs post pass. */
@@ -171,7 +220,7 @@
* @details The constructor of the specific pass instance should fill this
* with default options.
* */
- SafeMap<const char*, int> default_options_;
+ SafeMap<const char*, const OptionContent> default_options_;
};
} // namespace art
#endif // ART_COMPILER_DEX_PASS_ME_H_
diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc
deleted file mode 100644
index ba255e0..0000000
--- a/compiler/dex/portable/mir_to_gbc.cc
+++ /dev/null
@@ -1,2003 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "object_utils.h"
-
-#include <llvm/ADT/DepthFirstIterator.h>
-#include <llvm/Analysis/Verifier.h>
-#include <llvm/Bitcode/ReaderWriter.h>
-#include <llvm/IR/Instruction.h>
-#include <llvm/IR/Instructions.h>
-#include <llvm/IR/Metadata.h>
-#include <llvm/IR/Type.h>
-#include <llvm/Support/Casting.h>
-#include <llvm/Support/InstIterator.h>
-#include <llvm/Support/ToolOutputFile.h>
-
-#include "dex/compiler_internals.h"
-#include "dex/dataflow_iterator-inl.h"
-#include "dex/frontend.h"
-#include "llvm/ir_builder.h"
-#include "llvm/llvm_compilation_unit.h"
-#include "llvm/utils_llvm.h"
-#include "mir_to_gbc.h"
-#include "thread-inl.h"
-
-const char* kLabelFormat = "%c0x%x_%d";
-const char kInvalidBlock = 0xff;
-const char kNormalBlock = 'L';
-const char kCatchBlock = 'C';
-
-namespace art {
-namespace llvm {
-::llvm::Module* makeLLVMModuleContents(::llvm::Module* module);
-}
-
-LLVMInfo::LLVMInfo() {
- // Create context, module, intrinsic helper & ir builder
- llvm_context_.reset(new ::llvm::LLVMContext());
- llvm_module_ = new ::llvm::Module("art", *llvm_context_);
- ::llvm::StructType::create(*llvm_context_, "JavaObject");
- art::llvm::makeLLVMModuleContents(llvm_module_);
- intrinsic_helper_.reset(new art::llvm::IntrinsicHelper(*llvm_context_, *llvm_module_));
- ir_builder_.reset(new art::llvm::IRBuilder(*llvm_context_, *llvm_module_, *intrinsic_helper_));
-}
-
-LLVMInfo::~LLVMInfo() {
-}
-
-::llvm::BasicBlock* MirConverter::GetLLVMBlock(int id) {
- return id_to_block_map_.Get(id);
-}
-
-::llvm::Value* MirConverter::GetLLVMValue(int s_reg) {
- return llvm_values_[s_reg];
-}
-
-void MirConverter::SetVregOnValue(::llvm::Value* val, int s_reg) {
- // Set vreg for debugging
- art::llvm::IntrinsicHelper::IntrinsicId id = art::llvm::IntrinsicHelper::SetVReg;
- ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(id);
- int v_reg = mir_graph_->SRegToVReg(s_reg);
- ::llvm::Value* table_slot = irb_->getInt32(v_reg);
- ::llvm::Value* args[] = { table_slot, val };
- irb_->CreateCall(func, args);
-}
-
-// Replace the placeholder value with the real definition
-void MirConverter::DefineValueOnly(::llvm::Value* val, int s_reg) {
- ::llvm::Value* placeholder = GetLLVMValue(s_reg);
- if (placeholder == NULL) {
- // This can happen on instruction rewrite on verification failure
- LOG(WARNING) << "Null placeholder";
- return;
- }
- placeholder->replaceAllUsesWith(val);
- val->takeName(placeholder);
- llvm_values_[s_reg] = val;
- ::llvm::Instruction* inst = ::llvm::dyn_cast< ::llvm::Instruction>(placeholder);
- DCHECK(inst != NULL);
- inst->eraseFromParent();
-}
-
-void MirConverter::DefineValue(::llvm::Value* val, int s_reg) {
- DefineValueOnly(val, s_reg);
- SetVregOnValue(val, s_reg);
-}
-
-::llvm::Type* MirConverter::LlvmTypeFromLocRec(RegLocation loc) {
- ::llvm::Type* res = NULL;
- if (loc.wide) {
- if (loc.fp)
- res = irb_->getDoubleTy();
- else
- res = irb_->getInt64Ty();
- } else {
- if (loc.fp) {
- res = irb_->getFloatTy();
- } else {
- if (loc.ref)
- res = irb_->getJObjectTy();
- else
- res = irb_->getInt32Ty();
- }
- }
- return res;
-}
-
-void MirConverter::InitIR() {
- if (llvm_info_ == NULL) {
- CompilerTls* tls = cu_->compiler_driver->GetTls();
- CHECK(tls != NULL);
- llvm_info_ = static_cast<LLVMInfo*>(tls->GetLLVMInfo());
- if (llvm_info_ == NULL) {
- llvm_info_ = new LLVMInfo();
- tls->SetLLVMInfo(llvm_info_);
- }
- }
- context_ = llvm_info_->GetLLVMContext();
- module_ = llvm_info_->GetLLVMModule();
- intrinsic_helper_ = llvm_info_->GetIntrinsicHelper();
- irb_ = llvm_info_->GetIRBuilder();
-}
-
-::llvm::BasicBlock* MirConverter::FindCaseTarget(uint32_t vaddr) {
- BasicBlock* bb = mir_graph_->FindBlock(vaddr);
- DCHECK(bb != NULL);
- return GetLLVMBlock(bb->id);
-}
-
-void MirConverter::ConvertPackedSwitch(BasicBlock* bb, MIR* mir,
- int32_t table_offset, RegLocation rl_src) {
- const Instruction::PackedSwitchPayload* payload =
- reinterpret_cast<const Instruction::PackedSwitchPayload*>(
- mir_graph_->GetTable(mir, table_offset));
-
- ::llvm::Value* value = GetLLVMValue(rl_src.orig_sreg);
-
- ::llvm::SwitchInst* sw =
- irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through),
- payload->case_count);
-
- for (uint16_t i = 0; i < payload->case_count; ++i) {
- ::llvm::BasicBlock* llvm_bb =
- FindCaseTarget(current_dalvik_offset_ + payload->targets[i]);
- sw->addCase(irb_->getInt32(payload->first_key + i), llvm_bb);
- }
- ::llvm::MDNode* switch_node =
- ::llvm::MDNode::get(*context_, irb_->getInt32(table_offset));
- sw->setMetadata("SwitchTable", switch_node);
- bb->taken = NullBasicBlockId;
- bb->fall_through = NullBasicBlockId;
-}
-
-void MirConverter::ConvertSparseSwitch(BasicBlock* bb, MIR* mir,
- int32_t table_offset, RegLocation rl_src) {
- const Instruction::SparseSwitchPayload* payload =
- reinterpret_cast<const Instruction::SparseSwitchPayload*>(
- mir_graph_->GetTable(mir, table_offset));
-
- const int32_t* keys = payload->GetKeys();
- const int32_t* targets = payload->GetTargets();
-
- ::llvm::Value* value = GetLLVMValue(rl_src.orig_sreg);
-
- ::llvm::SwitchInst* sw =
- irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through),
- payload->case_count);
-
- for (size_t i = 0; i < payload->case_count; ++i) {
- ::llvm::BasicBlock* llvm_bb =
- FindCaseTarget(current_dalvik_offset_ + targets[i]);
- sw->addCase(irb_->getInt32(keys[i]), llvm_bb);
- }
- ::llvm::MDNode* switch_node =
- ::llvm::MDNode::get(*context_, irb_->getInt32(table_offset));
- sw->setMetadata("SwitchTable", switch_node);
- bb->taken = NullBasicBlockId;
- bb->fall_through = NullBasicBlockId;
-}
-
-void MirConverter::ConvertSget(int32_t field_index,
- art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest) {
- ::llvm::Constant* field_idx = irb_->getInt32(field_index);
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::Value* res = irb_->CreateCall(intr, field_idx);
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertSput(int32_t field_index,
- art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_src) {
- ::llvm::SmallVector< ::llvm::Value*, 2> args;
- args.push_back(irb_->getInt32(field_index));
- args.push_back(GetLLVMValue(rl_src.orig_sreg));
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- irb_->CreateCall(intr, args);
-}
-
-void MirConverter::ConvertFillArrayData(int32_t offset, RegLocation rl_array) {
- art::llvm::IntrinsicHelper::IntrinsicId id;
- id = art::llvm::IntrinsicHelper::HLFillArrayData;
- ::llvm::SmallVector< ::llvm::Value*, 2> args;
- args.push_back(irb_->getInt32(offset));
- args.push_back(GetLLVMValue(rl_array.orig_sreg));
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- irb_->CreateCall(intr, args);
-}
-
-::llvm::Value* MirConverter::EmitConst(::llvm::ArrayRef< ::llvm::Value*> src,
- RegLocation loc) {
- art::llvm::IntrinsicHelper::IntrinsicId id;
- if (loc.wide) {
- if (loc.fp) {
- id = art::llvm::IntrinsicHelper::ConstDouble;
- } else {
- id = art::llvm::IntrinsicHelper::ConstLong;
- }
- } else {
- if (loc.fp) {
- id = art::llvm::IntrinsicHelper::ConstFloat;
- } else if (loc.ref) {
- id = art::llvm::IntrinsicHelper::ConstObj;
- } else {
- id = art::llvm::IntrinsicHelper::ConstInt;
- }
- }
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- return irb_->CreateCall(intr, src);
-}
-
-void MirConverter::EmitPopShadowFrame() {
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(
- art::llvm::IntrinsicHelper::PopShadowFrame);
- irb_->CreateCall(intr);
-}
-
-::llvm::Value* MirConverter::EmitCopy(::llvm::ArrayRef< ::llvm::Value*> src,
- RegLocation loc) {
- art::llvm::IntrinsicHelper::IntrinsicId id;
- if (loc.wide) {
- if (loc.fp) {
- id = art::llvm::IntrinsicHelper::CopyDouble;
- } else {
- id = art::llvm::IntrinsicHelper::CopyLong;
- }
- } else {
- if (loc.fp) {
- id = art::llvm::IntrinsicHelper::CopyFloat;
- } else if (loc.ref) {
- id = art::llvm::IntrinsicHelper::CopyObj;
- } else {
- id = art::llvm::IntrinsicHelper::CopyInt;
- }
- }
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- return irb_->CreateCall(intr, src);
-}
-
-void MirConverter::ConvertMoveException(RegLocation rl_dest) {
- ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(
- art::llvm::IntrinsicHelper::GetException);
- ::llvm::Value* res = irb_->CreateCall(func);
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertThrow(RegLocation rl_src) {
- ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg);
- ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(
- art::llvm::IntrinsicHelper::HLThrowException);
- irb_->CreateCall(func, src);
-}
-
-void MirConverter::ConvertMonitorEnterExit(int opt_flags,
- art::llvm::IntrinsicHelper::IntrinsicId id,
- RegLocation rl_src) {
- ::llvm::SmallVector< ::llvm::Value*, 2> args;
- args.push_back(irb_->getInt32(opt_flags));
- args.push_back(GetLLVMValue(rl_src.orig_sreg));
- ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(id);
- irb_->CreateCall(func, args);
-}
-
-void MirConverter::ConvertArrayLength(int opt_flags,
- RegLocation rl_dest, RegLocation rl_src) {
- ::llvm::SmallVector< ::llvm::Value*, 2> args;
- args.push_back(irb_->getInt32(opt_flags));
- args.push_back(GetLLVMValue(rl_src.orig_sreg));
- ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(
- art::llvm::IntrinsicHelper::OptArrayLength);
- ::llvm::Value* res = irb_->CreateCall(func, args);
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::EmitSuspendCheck() {
- art::llvm::IntrinsicHelper::IntrinsicId id =
- art::llvm::IntrinsicHelper::CheckSuspend;
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- irb_->CreateCall(intr);
-}
-
-::llvm::Value* MirConverter::ConvertCompare(ConditionCode cc,
- ::llvm::Value* src1, ::llvm::Value* src2) {
- ::llvm::Value* res = NULL;
- DCHECK_EQ(src1->getType(), src2->getType());
- switch (cc) {
- case kCondEq: res = irb_->CreateICmpEQ(src1, src2); break;
- case kCondNe: res = irb_->CreateICmpNE(src1, src2); break;
- case kCondLt: res = irb_->CreateICmpSLT(src1, src2); break;
- case kCondGe: res = irb_->CreateICmpSGE(src1, src2); break;
- case kCondGt: res = irb_->CreateICmpSGT(src1, src2); break;
- case kCondLe: res = irb_->CreateICmpSLE(src1, src2); break;
- default: LOG(FATAL) << "Unexpected cc value " << cc;
- }
- return res;
-}
-
-void MirConverter::ConvertCompareAndBranch(BasicBlock* bb, MIR* mir,
- ConditionCode cc, RegLocation rl_src1, RegLocation rl_src2) {
- if (mir_graph_->GetBasicBlock(bb->taken)->start_offset <= mir->offset) {
- EmitSuspendCheck();
- }
- ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
- ::llvm::Value* src2 = GetLLVMValue(rl_src2.orig_sreg);
- ::llvm::Value* cond_value = ConvertCompare(cc, src1, src2);
- cond_value->setName(StringPrintf("t%d", temp_name_++));
- irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken),
- GetLLVMBlock(bb->fall_through));
- // Don't redo the fallthrough branch in the BB driver
- bb->fall_through = NullBasicBlockId;
-}
-
-void MirConverter::ConvertCompareZeroAndBranch(BasicBlock* bb,
- MIR* mir, ConditionCode cc, RegLocation rl_src1) {
- if (mir_graph_->GetBasicBlock(bb->taken)->start_offset <= mir->offset) {
- EmitSuspendCheck();
- }
- ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
- ::llvm::Value* src2;
- if (rl_src1.ref) {
- src2 = irb_->getJNull();
- } else {
- src2 = irb_->getInt32(0);
- }
- ::llvm::Value* cond_value = ConvertCompare(cc, src1, src2);
- irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken),
- GetLLVMBlock(bb->fall_through));
- // Don't redo the fallthrough branch in the BB driver
- bb->fall_through = NullBasicBlockId;
-}
-
-::llvm::Value* MirConverter::GenDivModOp(bool is_div, bool is_long,
- ::llvm::Value* src1, ::llvm::Value* src2) {
- art::llvm::IntrinsicHelper::IntrinsicId id;
- if (is_long) {
- if (is_div) {
- id = art::llvm::IntrinsicHelper::DivLong;
- } else {
- id = art::llvm::IntrinsicHelper::RemLong;
- }
- } else {
- if (is_div) {
- id = art::llvm::IntrinsicHelper::DivInt;
- } else {
- id = art::llvm::IntrinsicHelper::RemInt;
- }
- }
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::SmallVector< ::llvm::Value*, 2>args;
- args.push_back(src1);
- args.push_back(src2);
- return irb_->CreateCall(intr, args);
-}
-
-::llvm::Value* MirConverter::GenArithOp(OpKind op, bool is_long,
- ::llvm::Value* src1, ::llvm::Value* src2) {
- ::llvm::Value* res = NULL;
- switch (op) {
- case kOpAdd: res = irb_->CreateAdd(src1, src2); break;
- case kOpSub: res = irb_->CreateSub(src1, src2); break;
- case kOpRsub: res = irb_->CreateSub(src2, src1); break;
- case kOpMul: res = irb_->CreateMul(src1, src2); break;
- case kOpOr: res = irb_->CreateOr(src1, src2); break;
- case kOpAnd: res = irb_->CreateAnd(src1, src2); break;
- case kOpXor: res = irb_->CreateXor(src1, src2); break;
- case kOpDiv: res = GenDivModOp(true, is_long, src1, src2); break;
- case kOpRem: res = GenDivModOp(false, is_long, src1, src2); break;
- case kOpLsl: res = irb_->CreateShl(src1, src2); break;
- case kOpLsr: res = irb_->CreateLShr(src1, src2); break;
- case kOpAsr: res = irb_->CreateAShr(src1, src2); break;
- default:
- LOG(FATAL) << "Invalid op " << op;
- }
- return res;
-}
-
-void MirConverter::ConvertFPArithOp(OpKind op, RegLocation rl_dest,
- RegLocation rl_src1, RegLocation rl_src2) {
- ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
- ::llvm::Value* src2 = GetLLVMValue(rl_src2.orig_sreg);
- ::llvm::Value* res = NULL;
- switch (op) {
- case kOpAdd: res = irb_->CreateFAdd(src1, src2); break;
- case kOpSub: res = irb_->CreateFSub(src1, src2); break;
- case kOpMul: res = irb_->CreateFMul(src1, src2); break;
- case kOpDiv: res = irb_->CreateFDiv(src1, src2); break;
- case kOpRem: res = irb_->CreateFRem(src1, src2); break;
- default:
- LOG(FATAL) << "Invalid op " << op;
- }
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertShift(art::llvm::IntrinsicHelper::IntrinsicId id,
- RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::SmallVector< ::llvm::Value*, 2>args;
- args.push_back(GetLLVMValue(rl_src1.orig_sreg));
- args.push_back(GetLLVMValue(rl_src2.orig_sreg));
- ::llvm::Value* res = irb_->CreateCall(intr, args);
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertShiftLit(art::llvm::IntrinsicHelper::IntrinsicId id,
- RegLocation rl_dest, RegLocation rl_src, int shift_amount) {
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::SmallVector< ::llvm::Value*, 2>args;
- args.push_back(GetLLVMValue(rl_src.orig_sreg));
- args.push_back(irb_->getInt32(shift_amount));
- ::llvm::Value* res = irb_->CreateCall(intr, args);
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertArithOp(OpKind op, RegLocation rl_dest,
- RegLocation rl_src1, RegLocation rl_src2) {
- ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
- ::llvm::Value* src2 = GetLLVMValue(rl_src2.orig_sreg);
- DCHECK_EQ(src1->getType(), src2->getType());
- ::llvm::Value* res = GenArithOp(op, rl_dest.wide, src1, src2);
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertArithOpLit(OpKind op, RegLocation rl_dest,
- RegLocation rl_src1, int32_t imm) {
- ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
- ::llvm::Value* src2 = irb_->getInt32(imm);
- ::llvm::Value* res = GenArithOp(op, rl_dest.wide, src1, src2);
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-/*
- * Process arguments for invoke. Note: this code is also used to
- * collect and process arguments for NEW_FILLED_ARRAY and NEW_FILLED_ARRAY_RANGE.
- * The requirements are similar.
- */
-void MirConverter::ConvertInvoke(BasicBlock* bb, MIR* mir,
- InvokeType invoke_type, bool is_range, bool is_filled_new_array) {
- CallInfo* info = mir_graph_->NewMemCallInfo(bb, mir, invoke_type, is_range);
- ::llvm::SmallVector< ::llvm::Value*, 10> args;
- // Insert the invoke_type
- args.push_back(irb_->getInt32(static_cast<int>(invoke_type)));
- // Insert the method_idx
- args.push_back(irb_->getInt32(info->index));
- // Insert the optimization flags
- args.push_back(irb_->getInt32(info->opt_flags));
- // Now, insert the actual arguments
- for (int i = 0; i < info->num_arg_words;) {
- ::llvm::Value* val = GetLLVMValue(info->args[i].orig_sreg);
- args.push_back(val);
- i += info->args[i].wide ? 2 : 1;
- }
- /*
- * Choose the invoke return type based on actual usage. Note: may
- * be different than shorty. For example, if a function return value
- * is not used, we'll treat this as a void invoke.
- */
- art::llvm::IntrinsicHelper::IntrinsicId id;
- if (is_filled_new_array) {
- id = art::llvm::IntrinsicHelper::HLFilledNewArray;
- } else if (info->result.location == kLocInvalid) {
- id = art::llvm::IntrinsicHelper::HLInvokeVoid;
- } else {
- if (info->result.wide) {
- if (info->result.fp) {
- id = art::llvm::IntrinsicHelper::HLInvokeDouble;
- } else {
- id = art::llvm::IntrinsicHelper::HLInvokeLong;
- }
- } else if (info->result.ref) {
- id = art::llvm::IntrinsicHelper::HLInvokeObj;
- } else if (info->result.fp) {
- id = art::llvm::IntrinsicHelper::HLInvokeFloat;
- } else {
- id = art::llvm::IntrinsicHelper::HLInvokeInt;
- }
- }
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::Value* res = irb_->CreateCall(intr, args);
- if (info->result.location != kLocInvalid) {
- DefineValue(res, info->result.orig_sreg);
- }
-}
-
-void MirConverter::ConvertConstObject(uint32_t idx,
- art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest) {
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::Value* index = irb_->getInt32(idx);
- ::llvm::Value* res = irb_->CreateCall(intr, index);
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertCheckCast(uint32_t type_idx, RegLocation rl_src) {
- art::llvm::IntrinsicHelper::IntrinsicId id;
- id = art::llvm::IntrinsicHelper::HLCheckCast;
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::SmallVector< ::llvm::Value*, 2> args;
- args.push_back(irb_->getInt32(type_idx));
- args.push_back(GetLLVMValue(rl_src.orig_sreg));
- irb_->CreateCall(intr, args);
-}
-
-void MirConverter::ConvertNewInstance(uint32_t type_idx, RegLocation rl_dest) {
- art::llvm::IntrinsicHelper::IntrinsicId id;
- id = art::llvm::IntrinsicHelper::NewInstance;
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::Value* index = irb_->getInt32(type_idx);
- ::llvm::Value* res = irb_->CreateCall(intr, index);
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertNewArray(uint32_t type_idx,
- RegLocation rl_dest, RegLocation rl_src) {
- art::llvm::IntrinsicHelper::IntrinsicId id;
- id = art::llvm::IntrinsicHelper::NewArray;
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::SmallVector< ::llvm::Value*, 2> args;
- args.push_back(irb_->getInt32(type_idx));
- args.push_back(GetLLVMValue(rl_src.orig_sreg));
- ::llvm::Value* res = irb_->CreateCall(intr, args);
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertAget(int opt_flags,
- art::llvm::IntrinsicHelper::IntrinsicId id,
- RegLocation rl_dest, RegLocation rl_array, RegLocation rl_index) {
- ::llvm::SmallVector< ::llvm::Value*, 3> args;
- args.push_back(irb_->getInt32(opt_flags));
- args.push_back(GetLLVMValue(rl_array.orig_sreg));
- args.push_back(GetLLVMValue(rl_index.orig_sreg));
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::Value* res = irb_->CreateCall(intr, args);
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertAput(int opt_flags,
- art::llvm::IntrinsicHelper::IntrinsicId id,
- RegLocation rl_src, RegLocation rl_array, RegLocation rl_index) {
- ::llvm::SmallVector< ::llvm::Value*, 4> args;
- args.push_back(irb_->getInt32(opt_flags));
- args.push_back(GetLLVMValue(rl_src.orig_sreg));
- args.push_back(GetLLVMValue(rl_array.orig_sreg));
- args.push_back(GetLLVMValue(rl_index.orig_sreg));
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- irb_->CreateCall(intr, args);
-}
-
-void MirConverter::ConvertIget(int opt_flags,
- art::llvm::IntrinsicHelper::IntrinsicId id,
- RegLocation rl_dest, RegLocation rl_obj, int field_index) {
- ::llvm::SmallVector< ::llvm::Value*, 3> args;
- args.push_back(irb_->getInt32(opt_flags));
- args.push_back(GetLLVMValue(rl_obj.orig_sreg));
- args.push_back(irb_->getInt32(field_index));
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::Value* res = irb_->CreateCall(intr, args);
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertIput(int opt_flags,
- art::llvm::IntrinsicHelper::IntrinsicId id,
- RegLocation rl_src, RegLocation rl_obj, int field_index) {
- ::llvm::SmallVector< ::llvm::Value*, 4> args;
- args.push_back(irb_->getInt32(opt_flags));
- args.push_back(GetLLVMValue(rl_src.orig_sreg));
- args.push_back(GetLLVMValue(rl_obj.orig_sreg));
- args.push_back(irb_->getInt32(field_index));
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- irb_->CreateCall(intr, args);
-}
-
-void MirConverter::ConvertInstanceOf(uint32_t type_idx,
- RegLocation rl_dest, RegLocation rl_src) {
- art::llvm::IntrinsicHelper::IntrinsicId id;
- id = art::llvm::IntrinsicHelper::InstanceOf;
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::SmallVector< ::llvm::Value*, 2> args;
- args.push_back(irb_->getInt32(type_idx));
- args.push_back(GetLLVMValue(rl_src.orig_sreg));
- ::llvm::Value* res = irb_->CreateCall(intr, args);
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertIntToLong(RegLocation rl_dest, RegLocation rl_src) {
- ::llvm::Value* res = irb_->CreateSExt(GetLLVMValue(rl_src.orig_sreg),
- irb_->getInt64Ty());
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertLongToInt(RegLocation rl_dest, RegLocation rl_src) {
- ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg);
- ::llvm::Value* res = irb_->CreateTrunc(src, irb_->getInt32Ty());
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertFloatToDouble(RegLocation rl_dest, RegLocation rl_src) {
- ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg);
- ::llvm::Value* res = irb_->CreateFPExt(src, irb_->getDoubleTy());
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertDoubleToFloat(RegLocation rl_dest, RegLocation rl_src) {
- ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg);
- ::llvm::Value* res = irb_->CreateFPTrunc(src, irb_->getFloatTy());
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertWideComparison(art::llvm::IntrinsicHelper::IntrinsicId id,
- RegLocation rl_dest, RegLocation rl_src1,
- RegLocation rl_src2) {
- DCHECK_EQ(rl_src1.fp, rl_src2.fp);
- DCHECK_EQ(rl_src1.wide, rl_src2.wide);
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::SmallVector< ::llvm::Value*, 2> args;
- args.push_back(GetLLVMValue(rl_src1.orig_sreg));
- args.push_back(GetLLVMValue(rl_src2.orig_sreg));
- ::llvm::Value* res = irb_->CreateCall(intr, args);
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertIntNarrowing(RegLocation rl_dest, RegLocation rl_src,
- art::llvm::IntrinsicHelper::IntrinsicId id) {
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::Value* res =
- irb_->CreateCall(intr, GetLLVMValue(rl_src.orig_sreg));
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertNeg(RegLocation rl_dest, RegLocation rl_src) {
- ::llvm::Value* res = irb_->CreateNeg(GetLLVMValue(rl_src.orig_sreg));
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertIntToFP(::llvm::Type* ty, RegLocation rl_dest,
- RegLocation rl_src) {
- ::llvm::Value* res =
- irb_->CreateSIToFP(GetLLVMValue(rl_src.orig_sreg), ty);
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertFPToInt(art::llvm::IntrinsicHelper::IntrinsicId id,
- RegLocation rl_dest,
- RegLocation rl_src) {
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::Value* res = irb_->CreateCall(intr, GetLLVMValue(rl_src.orig_sreg));
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-
-void MirConverter::ConvertNegFP(RegLocation rl_dest, RegLocation rl_src) {
- ::llvm::Value* res =
- irb_->CreateFNeg(GetLLVMValue(rl_src.orig_sreg));
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertNot(RegLocation rl_dest, RegLocation rl_src) {
- ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg);
- ::llvm::Value* res = irb_->CreateXor(src, static_cast<uint64_t>(-1));
- DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::EmitConstructorBarrier() {
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(
- art::llvm::IntrinsicHelper::ConstructorBarrier);
- irb_->CreateCall(intr);
-}
-
-/*
- * Target-independent code generation. Use only high-level
- * load/store utilities here, or target-dependent genXX() handlers
- * when necessary.
- */
-bool MirConverter::ConvertMIRNode(MIR* mir, BasicBlock* bb,
- ::llvm::BasicBlock* llvm_bb) {
- bool res = false; // Assume success
- RegLocation rl_src[3];
- RegLocation rl_dest = mir_graph_->GetBadLoc();
- Instruction::Code opcode = mir->dalvikInsn.opcode;
- int op_val = opcode;
- uint32_t vB = mir->dalvikInsn.vB;
- uint32_t vC = mir->dalvikInsn.vC;
- int opt_flags = mir->optimization_flags;
-
- if (cu_->verbose) {
- if (!IsPseudoMirOp(op_val)) {
- LOG(INFO) << ".. " << Instruction::Name(opcode) << " 0x" << std::hex << op_val;
- } else {
- LOG(INFO) << mir_graph_->extended_mir_op_names_[op_val - kMirOpFirst] << " 0x" << std::hex << op_val;
- }
- }
-
- /* Prep Src and Dest locations */
- int next_sreg = 0;
- int next_loc = 0;
- uint64_t attrs = MirGraph::GetDataFlowAttributes(opcode);
- rl_src[0] = rl_src[1] = rl_src[2] = mir_graph_->GetBadLoc();
- if (attrs & DF_UA) {
- if (attrs & DF_A_WIDE) {
- rl_src[next_loc++] = mir_graph_->GetSrcWide(mir, next_sreg);
- next_sreg+= 2;
- } else {
- rl_src[next_loc++] = mir_graph_->GetSrc(mir, next_sreg);
- next_sreg++;
- }
- }
- if (attrs & DF_UB) {
- if (attrs & DF_B_WIDE) {
- rl_src[next_loc++] = mir_graph_->GetSrcWide(mir, next_sreg);
- next_sreg+= 2;
- } else {
- rl_src[next_loc++] = mir_graph_->GetSrc(mir, next_sreg);
- next_sreg++;
- }
- }
- if (attrs & DF_UC) {
- if (attrs & DF_C_WIDE) {
- rl_src[next_loc++] = mir_graph_->GetSrcWide(mir, next_sreg);
- } else {
- rl_src[next_loc++] = mir_graph_->GetSrc(mir, next_sreg);
- }
- }
- if (attrs & DF_DA) {
- if (attrs & DF_A_WIDE) {
- rl_dest = mir_graph_->GetDestWide(mir);
- } else {
- rl_dest = mir_graph_->GetDest(mir);
- }
- }
-
- switch (opcode) {
- case Instruction::NOP:
- break;
-
- case Instruction::MOVE:
- case Instruction::MOVE_OBJECT:
- case Instruction::MOVE_16:
- case Instruction::MOVE_OBJECT_16:
- case Instruction::MOVE_OBJECT_FROM16:
- case Instruction::MOVE_FROM16:
- case Instruction::MOVE_WIDE:
- case Instruction::MOVE_WIDE_16:
- case Instruction::MOVE_WIDE_FROM16: {
- /*
- * Moves/copies are meaningless in pure SSA register form,
- * but we need to preserve them for the conversion back into
- * MIR (at least until we stop using the Dalvik register maps).
- * Insert a dummy intrinsic copy call, which will be recognized
- * by the quick path and removed by the portable path.
- */
- ::llvm::Value* src = GetLLVMValue(rl_src[0].orig_sreg);
- ::llvm::Value* res = EmitCopy(src, rl_dest);
- DefineValue(res, rl_dest.orig_sreg);
- }
- break;
-
- case Instruction::CONST:
- case Instruction::CONST_4:
- case Instruction::CONST_16: {
- ::llvm::Constant* imm_value = irb_->getJInt(vB);
- ::llvm::Value* res = EmitConst(imm_value, rl_dest);
- DefineValue(res, rl_dest.orig_sreg);
- }
- break;
-
- case Instruction::CONST_WIDE_16:
- case Instruction::CONST_WIDE_32: {
- // Sign extend to 64 bits
- int64_t imm = static_cast<int32_t>(vB);
- ::llvm::Constant* imm_value = irb_->getJLong(imm);
- ::llvm::Value* res = EmitConst(imm_value, rl_dest);
- DefineValue(res, rl_dest.orig_sreg);
- }
- break;
-
- case Instruction::CONST_HIGH16: {
- ::llvm::Constant* imm_value = irb_->getJInt(vB << 16);
- ::llvm::Value* res = EmitConst(imm_value, rl_dest);
- DefineValue(res, rl_dest.orig_sreg);
- }
- break;
-
- case Instruction::CONST_WIDE: {
- ::llvm::Constant* imm_value =
- irb_->getJLong(mir->dalvikInsn.vB_wide);
- ::llvm::Value* res = EmitConst(imm_value, rl_dest);
- DefineValue(res, rl_dest.orig_sreg);
- }
- break;
- case Instruction::CONST_WIDE_HIGH16: {
- int64_t imm = static_cast<int64_t>(vB) << 48;
- ::llvm::Constant* imm_value = irb_->getJLong(imm);
- ::llvm::Value* res = EmitConst(imm_value, rl_dest);
- DefineValue(res, rl_dest.orig_sreg);
- }
- break;
-
- case Instruction::SPUT_OBJECT:
- ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputObject,
- rl_src[0]);
- break;
- case Instruction::SPUT:
- if (rl_src[0].fp) {
- ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputFloat,
- rl_src[0]);
- } else {
- ConvertSput(vB, art::llvm::IntrinsicHelper::HLSput, rl_src[0]);
- }
- break;
- case Instruction::SPUT_BOOLEAN:
- ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputBoolean,
- rl_src[0]);
- break;
- case Instruction::SPUT_BYTE:
- ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputByte, rl_src[0]);
- break;
- case Instruction::SPUT_CHAR:
- ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputChar, rl_src[0]);
- break;
- case Instruction::SPUT_SHORT:
- ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputShort, rl_src[0]);
- break;
- case Instruction::SPUT_WIDE:
- if (rl_src[0].fp) {
- ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputDouble,
- rl_src[0]);
- } else {
- ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputWide,
- rl_src[0]);
- }
- break;
-
- case Instruction::SGET_OBJECT:
- ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetObject, rl_dest);
- break;
- case Instruction::SGET:
- if (rl_dest.fp) {
- ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetFloat, rl_dest);
- } else {
- ConvertSget(vB, art::llvm::IntrinsicHelper::HLSget, rl_dest);
- }
- break;
- case Instruction::SGET_BOOLEAN:
- ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetBoolean, rl_dest);
- break;
- case Instruction::SGET_BYTE:
- ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetByte, rl_dest);
- break;
- case Instruction::SGET_CHAR:
- ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetChar, rl_dest);
- break;
- case Instruction::SGET_SHORT:
- ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetShort, rl_dest);
- break;
- case Instruction::SGET_WIDE:
- if (rl_dest.fp) {
- ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetDouble,
- rl_dest);
- } else {
- ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetWide, rl_dest);
- }
- break;
-
- case Instruction::RETURN_WIDE:
- case Instruction::RETURN:
- case Instruction::RETURN_OBJECT: {
- if (!mir_graph_->MethodIsLeaf()) {
- EmitSuspendCheck();
- }
- EmitPopShadowFrame();
- irb_->CreateRet(GetLLVMValue(rl_src[0].orig_sreg));
- DCHECK(bb->terminated_by_return);
- }
- break;
-
- case Instruction::RETURN_VOID: {
- if (((cu_->access_flags & kAccConstructor) != 0) &&
- cu_->compiler_driver->RequiresConstructorBarrier(Thread::Current(),
- cu_->dex_file,
- cu_->class_def_idx)) {
- EmitConstructorBarrier();
- }
- if (!mir_graph_->MethodIsLeaf()) {
- EmitSuspendCheck();
- }
- EmitPopShadowFrame();
- irb_->CreateRetVoid();
- DCHECK(bb->terminated_by_return);
- }
- break;
-
- case Instruction::IF_EQ:
- ConvertCompareAndBranch(bb, mir, kCondEq, rl_src[0], rl_src[1]);
- break;
- case Instruction::IF_NE:
- ConvertCompareAndBranch(bb, mir, kCondNe, rl_src[0], rl_src[1]);
- break;
- case Instruction::IF_LT:
- ConvertCompareAndBranch(bb, mir, kCondLt, rl_src[0], rl_src[1]);
- break;
- case Instruction::IF_GE:
- ConvertCompareAndBranch(bb, mir, kCondGe, rl_src[0], rl_src[1]);
- break;
- case Instruction::IF_GT:
- ConvertCompareAndBranch(bb, mir, kCondGt, rl_src[0], rl_src[1]);
- break;
- case Instruction::IF_LE:
- ConvertCompareAndBranch(bb, mir, kCondLe, rl_src[0], rl_src[1]);
- break;
- case Instruction::IF_EQZ:
- ConvertCompareZeroAndBranch(bb, mir, kCondEq, rl_src[0]);
- break;
- case Instruction::IF_NEZ:
- ConvertCompareZeroAndBranch(bb, mir, kCondNe, rl_src[0]);
- break;
- case Instruction::IF_LTZ:
- ConvertCompareZeroAndBranch(bb, mir, kCondLt, rl_src[0]);
- break;
- case Instruction::IF_GEZ:
- ConvertCompareZeroAndBranch(bb, mir, kCondGe, rl_src[0]);
- break;
- case Instruction::IF_GTZ:
- ConvertCompareZeroAndBranch(bb, mir, kCondGt, rl_src[0]);
- break;
- case Instruction::IF_LEZ:
- ConvertCompareZeroAndBranch(bb, mir, kCondLe, rl_src[0]);
- break;
-
- case Instruction::GOTO:
- case Instruction::GOTO_16:
- case Instruction::GOTO_32: {
- if (mir_graph_->GetBasicBlock(bb->taken)->start_offset <= bb->start_offset) {
- EmitSuspendCheck();
- }
- irb_->CreateBr(GetLLVMBlock(bb->taken));
- }
- break;
-
- case Instruction::ADD_LONG:
- case Instruction::ADD_LONG_2ADDR:
- case Instruction::ADD_INT:
- case Instruction::ADD_INT_2ADDR:
- ConvertArithOp(kOpAdd, rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::SUB_LONG:
- case Instruction::SUB_LONG_2ADDR:
- case Instruction::SUB_INT:
- case Instruction::SUB_INT_2ADDR:
- ConvertArithOp(kOpSub, rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::MUL_LONG:
- case Instruction::MUL_LONG_2ADDR:
- case Instruction::MUL_INT:
- case Instruction::MUL_INT_2ADDR:
- ConvertArithOp(kOpMul, rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::DIV_LONG:
- case Instruction::DIV_LONG_2ADDR:
- case Instruction::DIV_INT:
- case Instruction::DIV_INT_2ADDR:
- ConvertArithOp(kOpDiv, rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::REM_LONG:
- case Instruction::REM_LONG_2ADDR:
- case Instruction::REM_INT:
- case Instruction::REM_INT_2ADDR:
- ConvertArithOp(kOpRem, rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::AND_LONG:
- case Instruction::AND_LONG_2ADDR:
- case Instruction::AND_INT:
- case Instruction::AND_INT_2ADDR:
- ConvertArithOp(kOpAnd, rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::OR_LONG:
- case Instruction::OR_LONG_2ADDR:
- case Instruction::OR_INT:
- case Instruction::OR_INT_2ADDR:
- ConvertArithOp(kOpOr, rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::XOR_LONG:
- case Instruction::XOR_LONG_2ADDR:
- case Instruction::XOR_INT:
- case Instruction::XOR_INT_2ADDR:
- ConvertArithOp(kOpXor, rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::SHL_LONG:
- case Instruction::SHL_LONG_2ADDR:
- ConvertShift(art::llvm::IntrinsicHelper::SHLLong,
- rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::SHL_INT:
- case Instruction::SHL_INT_2ADDR:
- ConvertShift(art::llvm::IntrinsicHelper::SHLInt,
- rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::SHR_LONG:
- case Instruction::SHR_LONG_2ADDR:
- ConvertShift(art::llvm::IntrinsicHelper::SHRLong,
- rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::SHR_INT:
- case Instruction::SHR_INT_2ADDR:
- ConvertShift(art::llvm::IntrinsicHelper::SHRInt,
- rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::USHR_LONG:
- case Instruction::USHR_LONG_2ADDR:
- ConvertShift(art::llvm::IntrinsicHelper::USHRLong,
- rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::USHR_INT:
- case Instruction::USHR_INT_2ADDR:
- ConvertShift(art::llvm::IntrinsicHelper::USHRInt,
- rl_dest, rl_src[0], rl_src[1]);
- break;
-
- case Instruction::ADD_INT_LIT16:
- case Instruction::ADD_INT_LIT8:
- ConvertArithOpLit(kOpAdd, rl_dest, rl_src[0], vC);
- break;
- case Instruction::RSUB_INT:
- case Instruction::RSUB_INT_LIT8:
- ConvertArithOpLit(kOpRsub, rl_dest, rl_src[0], vC);
- break;
- case Instruction::MUL_INT_LIT16:
- case Instruction::MUL_INT_LIT8:
- ConvertArithOpLit(kOpMul, rl_dest, rl_src[0], vC);
- break;
- case Instruction::DIV_INT_LIT16:
- case Instruction::DIV_INT_LIT8:
- ConvertArithOpLit(kOpDiv, rl_dest, rl_src[0], vC);
- break;
- case Instruction::REM_INT_LIT16:
- case Instruction::REM_INT_LIT8:
- ConvertArithOpLit(kOpRem, rl_dest, rl_src[0], vC);
- break;
- case Instruction::AND_INT_LIT16:
- case Instruction::AND_INT_LIT8:
- ConvertArithOpLit(kOpAnd, rl_dest, rl_src[0], vC);
- break;
- case Instruction::OR_INT_LIT16:
- case Instruction::OR_INT_LIT8:
- ConvertArithOpLit(kOpOr, rl_dest, rl_src[0], vC);
- break;
- case Instruction::XOR_INT_LIT16:
- case Instruction::XOR_INT_LIT8:
- ConvertArithOpLit(kOpXor, rl_dest, rl_src[0], vC);
- break;
- case Instruction::SHL_INT_LIT8:
- ConvertShiftLit(art::llvm::IntrinsicHelper::SHLInt,
- rl_dest, rl_src[0], vC & 0x1f);
- break;
- case Instruction::SHR_INT_LIT8:
- ConvertShiftLit(art::llvm::IntrinsicHelper::SHRInt,
- rl_dest, rl_src[0], vC & 0x1f);
- break;
- case Instruction::USHR_INT_LIT8:
- ConvertShiftLit(art::llvm::IntrinsicHelper::USHRInt,
- rl_dest, rl_src[0], vC & 0x1f);
- break;
-
- case Instruction::ADD_FLOAT:
- case Instruction::ADD_FLOAT_2ADDR:
- case Instruction::ADD_DOUBLE:
- case Instruction::ADD_DOUBLE_2ADDR:
- ConvertFPArithOp(kOpAdd, rl_dest, rl_src[0], rl_src[1]);
- break;
-
- case Instruction::SUB_FLOAT:
- case Instruction::SUB_FLOAT_2ADDR:
- case Instruction::SUB_DOUBLE:
- case Instruction::SUB_DOUBLE_2ADDR:
- ConvertFPArithOp(kOpSub, rl_dest, rl_src[0], rl_src[1]);
- break;
-
- case Instruction::MUL_FLOAT:
- case Instruction::MUL_FLOAT_2ADDR:
- case Instruction::MUL_DOUBLE:
- case Instruction::MUL_DOUBLE_2ADDR:
- ConvertFPArithOp(kOpMul, rl_dest, rl_src[0], rl_src[1]);
- break;
-
- case Instruction::DIV_FLOAT:
- case Instruction::DIV_FLOAT_2ADDR:
- case Instruction::DIV_DOUBLE:
- case Instruction::DIV_DOUBLE_2ADDR:
- ConvertFPArithOp(kOpDiv, rl_dest, rl_src[0], rl_src[1]);
- break;
-
- case Instruction::REM_FLOAT:
- case Instruction::REM_FLOAT_2ADDR:
- case Instruction::REM_DOUBLE:
- case Instruction::REM_DOUBLE_2ADDR:
- ConvertFPArithOp(kOpRem, rl_dest, rl_src[0], rl_src[1]);
- break;
-
- case Instruction::INVOKE_STATIC:
- ConvertInvoke(bb, mir, kStatic, false /*range*/,
- false /* NewFilledArray */);
- break;
- case Instruction::INVOKE_STATIC_RANGE:
- ConvertInvoke(bb, mir, kStatic, true /*range*/,
- false /* NewFilledArray */);
- break;
-
- case Instruction::INVOKE_DIRECT:
- ConvertInvoke(bb, mir, kDirect, false /*range*/,
- false /* NewFilledArray */);
- break;
- case Instruction::INVOKE_DIRECT_RANGE:
- ConvertInvoke(bb, mir, kDirect, true /*range*/,
- false /* NewFilledArray */);
- break;
-
- case Instruction::INVOKE_VIRTUAL:
- ConvertInvoke(bb, mir, kVirtual, false /*range*/,
- false /* NewFilledArray */);
- break;
- case Instruction::INVOKE_VIRTUAL_RANGE:
- ConvertInvoke(bb, mir, kVirtual, true /*range*/,
- false /* NewFilledArray */);
- break;
-
- case Instruction::INVOKE_SUPER:
- ConvertInvoke(bb, mir, kSuper, false /*range*/,
- false /* NewFilledArray */);
- break;
- case Instruction::INVOKE_SUPER_RANGE:
- ConvertInvoke(bb, mir, kSuper, true /*range*/,
- false /* NewFilledArray */);
- break;
-
- case Instruction::INVOKE_INTERFACE:
- ConvertInvoke(bb, mir, kInterface, false /*range*/,
- false /* NewFilledArray */);
- break;
- case Instruction::INVOKE_INTERFACE_RANGE:
- ConvertInvoke(bb, mir, kInterface, true /*range*/,
- false /* NewFilledArray */);
- break;
- case Instruction::FILLED_NEW_ARRAY:
- ConvertInvoke(bb, mir, kInterface, false /*range*/,
- true /* NewFilledArray */);
- break;
- case Instruction::FILLED_NEW_ARRAY_RANGE:
- ConvertInvoke(bb, mir, kInterface, true /*range*/,
- true /* NewFilledArray */);
- break;
-
- case Instruction::CONST_STRING:
- case Instruction::CONST_STRING_JUMBO:
- ConvertConstObject(vB, art::llvm::IntrinsicHelper::ConstString,
- rl_dest);
- break;
-
- case Instruction::CONST_CLASS:
- ConvertConstObject(vB, art::llvm::IntrinsicHelper::ConstClass,
- rl_dest);
- break;
-
- case Instruction::CHECK_CAST:
- ConvertCheckCast(vB, rl_src[0]);
- break;
-
- case Instruction::NEW_INSTANCE:
- ConvertNewInstance(vB, rl_dest);
- break;
-
- case Instruction::MOVE_EXCEPTION:
- ConvertMoveException(rl_dest);
- break;
-
- case Instruction::THROW:
- ConvertThrow(rl_src[0]);
- /*
- * If this throw is standalone, terminate.
- * If it might rethrow, force termination
- * of the following block.
- */
- if (bb->fall_through == NullBasicBlockId) {
- irb_->CreateUnreachable();
- } else {
- mir_graph_->GetBasicBlock(bb->fall_through)->fall_through = NullBasicBlockId;
- mir_graph_->GetBasicBlock(bb->fall_through)->taken = NullBasicBlockId;
- }
- break;
-
- case Instruction::MOVE_RESULT_WIDE:
- case Instruction::MOVE_RESULT:
- case Instruction::MOVE_RESULT_OBJECT:
- /*
- * All move_results should have been folded into the preceeding invoke.
- */
- LOG(FATAL) << "Unexpected move_result";
- break;
-
- case Instruction::MONITOR_ENTER:
- ConvertMonitorEnterExit(opt_flags,
- art::llvm::IntrinsicHelper::MonitorEnter,
- rl_src[0]);
- break;
-
- case Instruction::MONITOR_EXIT:
- ConvertMonitorEnterExit(opt_flags,
- art::llvm::IntrinsicHelper::MonitorExit,
- rl_src[0]);
- break;
-
- case Instruction::ARRAY_LENGTH:
- ConvertArrayLength(opt_flags, rl_dest, rl_src[0]);
- break;
-
- case Instruction::NEW_ARRAY:
- ConvertNewArray(vC, rl_dest, rl_src[0]);
- break;
-
- case Instruction::INSTANCE_OF:
- ConvertInstanceOf(vC, rl_dest, rl_src[0]);
- break;
-
- case Instruction::AGET:
- if (rl_dest.fp) {
- ConvertAget(opt_flags,
- art::llvm::IntrinsicHelper::HLArrayGetFloat,
- rl_dest, rl_src[0], rl_src[1]);
- } else {
- ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGet,
- rl_dest, rl_src[0], rl_src[1]);
- }
- break;
- case Instruction::AGET_OBJECT:
- ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetObject,
- rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::AGET_BOOLEAN:
- ConvertAget(opt_flags,
- art::llvm::IntrinsicHelper::HLArrayGetBoolean,
- rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::AGET_BYTE:
- ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetByte,
- rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::AGET_CHAR:
- ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetChar,
- rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::AGET_SHORT:
- ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetShort,
- rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::AGET_WIDE:
- if (rl_dest.fp) {
- ConvertAget(opt_flags,
- art::llvm::IntrinsicHelper::HLArrayGetDouble,
- rl_dest, rl_src[0], rl_src[1]);
- } else {
- ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetWide,
- rl_dest, rl_src[0], rl_src[1]);
- }
- break;
-
- case Instruction::APUT:
- if (rl_src[0].fp) {
- ConvertAput(opt_flags,
- art::llvm::IntrinsicHelper::HLArrayPutFloat,
- rl_src[0], rl_src[1], rl_src[2]);
- } else {
- ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPut,
- rl_src[0], rl_src[1], rl_src[2]);
- }
- break;
- case Instruction::APUT_OBJECT:
- ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutObject,
- rl_src[0], rl_src[1], rl_src[2]);
- break;
- case Instruction::APUT_BOOLEAN:
- ConvertAput(opt_flags,
- art::llvm::IntrinsicHelper::HLArrayPutBoolean,
- rl_src[0], rl_src[1], rl_src[2]);
- break;
- case Instruction::APUT_BYTE:
- ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutByte,
- rl_src[0], rl_src[1], rl_src[2]);
- break;
- case Instruction::APUT_CHAR:
- ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutChar,
- rl_src[0], rl_src[1], rl_src[2]);
- break;
- case Instruction::APUT_SHORT:
- ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutShort,
- rl_src[0], rl_src[1], rl_src[2]);
- break;
- case Instruction::APUT_WIDE:
- if (rl_src[0].fp) {
- ConvertAput(opt_flags,
- art::llvm::IntrinsicHelper::HLArrayPutDouble,
- rl_src[0], rl_src[1], rl_src[2]);
- } else {
- ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutWide,
- rl_src[0], rl_src[1], rl_src[2]);
- }
- break;
-
- case Instruction::IGET:
- if (rl_dest.fp) {
- ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetFloat,
- rl_dest, rl_src[0], vC);
- } else {
- ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGet,
- rl_dest, rl_src[0], vC);
- }
- break;
- case Instruction::IGET_OBJECT:
- ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetObject,
- rl_dest, rl_src[0], vC);
- break;
- case Instruction::IGET_BOOLEAN:
- ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetBoolean,
- rl_dest, rl_src[0], vC);
- break;
- case Instruction::IGET_BYTE:
- ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetByte,
- rl_dest, rl_src[0], vC);
- break;
- case Instruction::IGET_CHAR:
- ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetChar,
- rl_dest, rl_src[0], vC);
- break;
- case Instruction::IGET_SHORT:
- ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetShort,
- rl_dest, rl_src[0], vC);
- break;
- case Instruction::IGET_WIDE:
- if (rl_dest.fp) {
- ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetDouble,
- rl_dest, rl_src[0], vC);
- } else {
- ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetWide,
- rl_dest, rl_src[0], vC);
- }
- break;
- case Instruction::IPUT:
- if (rl_src[0].fp) {
- ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutFloat,
- rl_src[0], rl_src[1], vC);
- } else {
- ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPut,
- rl_src[0], rl_src[1], vC);
- }
- break;
- case Instruction::IPUT_OBJECT:
- ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutObject,
- rl_src[0], rl_src[1], vC);
- break;
- case Instruction::IPUT_BOOLEAN:
- ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutBoolean,
- rl_src[0], rl_src[1], vC);
- break;
- case Instruction::IPUT_BYTE:
- ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutByte,
- rl_src[0], rl_src[1], vC);
- break;
- case Instruction::IPUT_CHAR:
- ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutChar,
- rl_src[0], rl_src[1], vC);
- break;
- case Instruction::IPUT_SHORT:
- ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutShort,
- rl_src[0], rl_src[1], vC);
- break;
- case Instruction::IPUT_WIDE:
- if (rl_src[0].fp) {
- ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutDouble,
- rl_src[0], rl_src[1], vC);
- } else {
- ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutWide,
- rl_src[0], rl_src[1], vC);
- }
- break;
-
- case Instruction::FILL_ARRAY_DATA:
- ConvertFillArrayData(vB, rl_src[0]);
- break;
-
- case Instruction::LONG_TO_INT:
- ConvertLongToInt(rl_dest, rl_src[0]);
- break;
-
- case Instruction::INT_TO_LONG:
- ConvertIntToLong(rl_dest, rl_src[0]);
- break;
-
- case Instruction::INT_TO_CHAR:
- ConvertIntNarrowing(rl_dest, rl_src[0],
- art::llvm::IntrinsicHelper::IntToChar);
- break;
- case Instruction::INT_TO_BYTE:
- ConvertIntNarrowing(rl_dest, rl_src[0],
- art::llvm::IntrinsicHelper::IntToByte);
- break;
- case Instruction::INT_TO_SHORT:
- ConvertIntNarrowing(rl_dest, rl_src[0],
- art::llvm::IntrinsicHelper::IntToShort);
- break;
-
- case Instruction::INT_TO_FLOAT:
- case Instruction::LONG_TO_FLOAT:
- ConvertIntToFP(irb_->getFloatTy(), rl_dest, rl_src[0]);
- break;
-
- case Instruction::INT_TO_DOUBLE:
- case Instruction::LONG_TO_DOUBLE:
- ConvertIntToFP(irb_->getDoubleTy(), rl_dest, rl_src[0]);
- break;
-
- case Instruction::FLOAT_TO_DOUBLE:
- ConvertFloatToDouble(rl_dest, rl_src[0]);
- break;
-
- case Instruction::DOUBLE_TO_FLOAT:
- ConvertDoubleToFloat(rl_dest, rl_src[0]);
- break;
-
- case Instruction::NEG_LONG:
- case Instruction::NEG_INT:
- ConvertNeg(rl_dest, rl_src[0]);
- break;
-
- case Instruction::NEG_FLOAT:
- case Instruction::NEG_DOUBLE:
- ConvertNegFP(rl_dest, rl_src[0]);
- break;
-
- case Instruction::NOT_LONG:
- case Instruction::NOT_INT:
- ConvertNot(rl_dest, rl_src[0]);
- break;
-
- case Instruction::FLOAT_TO_INT:
- ConvertFPToInt(art::llvm::IntrinsicHelper::F2I, rl_dest, rl_src[0]);
- break;
-
- case Instruction::DOUBLE_TO_INT:
- ConvertFPToInt(art::llvm::IntrinsicHelper::D2I, rl_dest, rl_src[0]);
- break;
-
- case Instruction::FLOAT_TO_LONG:
- ConvertFPToInt(art::llvm::IntrinsicHelper::F2L, rl_dest, rl_src[0]);
- break;
-
- case Instruction::DOUBLE_TO_LONG:
- ConvertFPToInt(art::llvm::IntrinsicHelper::D2L, rl_dest, rl_src[0]);
- break;
-
- case Instruction::CMPL_FLOAT:
- ConvertWideComparison(art::llvm::IntrinsicHelper::CmplFloat,
- rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::CMPG_FLOAT:
- ConvertWideComparison(art::llvm::IntrinsicHelper::CmpgFloat,
- rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::CMPL_DOUBLE:
- ConvertWideComparison(art::llvm::IntrinsicHelper::CmplDouble,
- rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::CMPG_DOUBLE:
- ConvertWideComparison(art::llvm::IntrinsicHelper::CmpgDouble,
- rl_dest, rl_src[0], rl_src[1]);
- break;
- case Instruction::CMP_LONG:
- ConvertWideComparison(art::llvm::IntrinsicHelper::CmpLong,
- rl_dest, rl_src[0], rl_src[1]);
- break;
-
- case Instruction::PACKED_SWITCH:
- ConvertPackedSwitch(bb, vB, rl_src[0]);
- break;
-
- case Instruction::SPARSE_SWITCH:
- ConvertSparseSwitch(bb, vB, rl_src[0]);
- break;
-
- default:
- UNIMPLEMENTED(FATAL) << "Unsupported Dex opcode 0x" << std::hex << opcode;
- res = true;
- }
- return res;
-} // NOLINT(readability/fn_size)
-
-void MirConverter::SetDexOffset(int32_t offset) {
- current_dalvik_offset_ = offset;
- ::llvm::SmallVector< ::llvm::Value*, 1> array_ref;
- array_ref.push_back(irb_->getInt32(offset));
- ::llvm::MDNode* node = ::llvm::MDNode::get(*context_, array_ref);
- irb_->SetDexOffset(node);
-}
-
-// Attach method info as metadata to special intrinsic
-void MirConverter::SetMethodInfo() {
- // We don't want dex offset on this
- irb_->SetDexOffset(NULL);
- art::llvm::IntrinsicHelper::IntrinsicId id;
- id = art::llvm::IntrinsicHelper::MethodInfo;
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::Instruction* inst = irb_->CreateCall(intr);
- ::llvm::SmallVector< ::llvm::Value*, 2> reg_info;
- reg_info.push_back(irb_->getInt32(mir_graph_->GetNumOfInVRs()));
- reg_info.push_back(irb_->getInt32(mir_graph_->GetNumOfLocalCodeVRs()));
- reg_info.push_back(irb_->getInt32(mir_graph_->GetNumOfOutVRs()));
- reg_info.push_back(irb_->getInt32(mir_graph_->GetNumUsedCompilerTemps()));
- reg_info.push_back(irb_->getInt32(mir_graph_->GetNumSSARegs()));
- ::llvm::MDNode* reg_info_node = ::llvm::MDNode::get(*context_, reg_info);
- inst->setMetadata("RegInfo", reg_info_node);
- SetDexOffset(current_dalvik_offset_);
-}
-
-void MirConverter::HandlePhiNodes(BasicBlock* bb, ::llvm::BasicBlock* llvm_bb) {
- SetDexOffset(bb->start_offset);
- for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
- int opcode = mir->dalvikInsn.opcode;
- if (!IsPseudoMirOp(opcode)) {
- // Stop after first non-pseudo MIR op.
- continue;
- }
- if (opcode != kMirOpPhi) {
- // Skip other mir Pseudos.
- continue;
- }
- RegLocation rl_dest = mir_graph_->reg_location_[mir->ssa_rep->defs[0]];
- /*
- * The Art compiler's Phi nodes only handle 32-bit operands,
- * representing wide values using a matched set of Phi nodes
- * for the lower and upper halves. In the llvm world, we only
- * want a single Phi for wides. Here we will simply discard
- * the Phi node representing the high word.
- */
- if (rl_dest.high_word) {
- continue; // No Phi node - handled via low word
- }
- BasicBlockId* incoming = mir->meta.phi_incoming;
- ::llvm::Type* phi_type =
- LlvmTypeFromLocRec(rl_dest);
- ::llvm::PHINode* phi = irb_->CreatePHI(phi_type, mir->ssa_rep->num_uses);
- for (int i = 0; i < mir->ssa_rep->num_uses; i++) {
- RegLocation loc;
- // Don't check width here.
- loc = mir_graph_->GetRawSrc(mir, i);
- DCHECK_EQ(rl_dest.wide, loc.wide);
- DCHECK_EQ(rl_dest.wide & rl_dest.high_word, loc.wide & loc.high_word);
- DCHECK_EQ(rl_dest.fp, loc.fp);
- DCHECK_EQ(rl_dest.core, loc.core);
- DCHECK_EQ(rl_dest.ref, loc.ref);
- SafeMap<unsigned int, unsigned int>::iterator it;
- it = mir_graph_->block_id_map_.find(incoming[i]);
- DCHECK(it != mir_graph_->block_id_map_.end());
- DCHECK(GetLLVMValue(loc.orig_sreg) != NULL);
- DCHECK(GetLLVMBlock(it->second) != NULL);
- phi->addIncoming(GetLLVMValue(loc.orig_sreg),
- GetLLVMBlock(it->second));
- }
- DefineValueOnly(phi, rl_dest.orig_sreg);
- }
-}
-
-/* Extended MIR instructions like PHI */
-void MirConverter::ConvertExtendedMIR(BasicBlock* bb, MIR* mir,
- ::llvm::BasicBlock* llvm_bb) {
- switch (static_cast<ExtendedMIROpcode>(mir->dalvikInsn.opcode)) {
- case kMirOpPhi: {
- // The llvm Phi node already emitted - just DefineValue() here.
- RegLocation rl_dest = mir_graph_->reg_location_[mir->ssa_rep->defs[0]];
- if (!rl_dest.high_word) {
- // Only consider low word of pairs.
- DCHECK(GetLLVMValue(rl_dest.orig_sreg) != NULL);
- ::llvm::Value* phi = GetLLVMValue(rl_dest.orig_sreg);
- if (1) SetVregOnValue(phi, rl_dest.orig_sreg);
- }
- break;
- }
- case kMirOpCopy: {
- UNIMPLEMENTED(WARNING) << "unimp kMirOpPhi";
- break;
- }
- case kMirOpNop:
- if ((mir == bb->last_mir_insn) && (bb->taken == NullBasicBlockId) &&
- (bb->fall_through == NullBasicBlockId)) {
- irb_->CreateUnreachable();
- }
- break;
-
- // TODO: need GBC intrinsic to take advantage of fused operations
- case kMirOpFusedCmplFloat:
- UNIMPLEMENTED(FATAL) << "kMirOpFusedCmpFloat unsupported";
- break;
- case kMirOpFusedCmpgFloat:
- UNIMPLEMENTED(FATAL) << "kMirOpFusedCmgFloat unsupported";
- break;
- case kMirOpFusedCmplDouble:
- UNIMPLEMENTED(FATAL) << "kMirOpFusedCmplDouble unsupported";
- break;
- case kMirOpFusedCmpgDouble:
- UNIMPLEMENTED(FATAL) << "kMirOpFusedCmpgDouble unsupported";
- break;
- case kMirOpFusedCmpLong:
- UNIMPLEMENTED(FATAL) << "kMirOpLongCmpBranch unsupported";
- break;
- default:
- break;
- }
-}
-
-/* Handle the content in each basic block */
-bool MirConverter::BlockBitcodeConversion(BasicBlock* bb) {
- if (bb->block_type == kDead) return false;
- ::llvm::BasicBlock* llvm_bb = GetLLVMBlock(bb->id);
- if (llvm_bb == NULL) {
- CHECK(bb->block_type == kExitBlock);
- } else {
- irb_->SetInsertPoint(llvm_bb);
- SetDexOffset(bb->start_offset);
- }
-
- if (cu_->verbose) {
- LOG(INFO) << "................................";
- LOG(INFO) << "Block id " << bb->id;
- if (llvm_bb != NULL) {
- LOG(INFO) << "label " << llvm_bb->getName().str().c_str();
- } else {
- LOG(INFO) << "llvm_bb is NULL";
- }
- }
-
- if (bb->block_type == kEntryBlock) {
- SetMethodInfo();
-
- { // Allocate shadowframe.
- art::llvm::IntrinsicHelper::IntrinsicId id =
- art::llvm::IntrinsicHelper::AllocaShadowFrame;
- ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::Value* entries = irb_->getInt32(mir_graph_->GetNumOfCodeVRs());
- irb_->CreateCall(func, entries);
- }
-
- { // Store arguments to vregs.
- uint16_t arg_reg = mir_graph_->GetFirstInVR();
-
- ::llvm::Function::arg_iterator arg_iter(func_->arg_begin());
-
- const char* shorty = cu_->shorty;
- uint32_t shorty_size = strlen(shorty);
- CHECK_GE(shorty_size, 1u);
-
- ++arg_iter; // skip method object
-
- if ((cu_->access_flags & kAccStatic) == 0) {
- SetVregOnValue(arg_iter, arg_reg);
- ++arg_iter;
- ++arg_reg;
- }
-
- for (uint32_t i = 1; i < shorty_size; ++i, ++arg_iter) {
- SetVregOnValue(arg_iter, arg_reg);
-
- ++arg_reg;
- if (shorty[i] == 'J' || shorty[i] == 'D') {
- // Wide types, such as long and double, are using a pair of registers
- // to store the value, so we have to increase arg_reg again.
- ++arg_reg;
- }
- }
- }
- } else if (bb->block_type == kExitBlock) {
- /*
- * Because of the differences between how MIR/LIR and llvm handle exit
- * blocks, we won't explicitly covert them. On the llvm-to-lir
- * path, it will need to be regenereated.
- */
- return false;
- } else if (bb->block_type == kExceptionHandling) {
- /*
- * Because we're deferring null checking, delete the associated empty
- * exception block.
- */
- llvm_bb->eraseFromParent();
- return false;
- }
-
- HandlePhiNodes(bb, llvm_bb);
-
- for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
- SetDexOffset(mir->offset);
-
- int opcode = mir->dalvikInsn.opcode;
- Instruction::Format dalvik_format =
- Instruction::FormatOf(mir->dalvikInsn.opcode);
-
- if (opcode == kMirOpCheck) {
- // Combine check and work halves of throwing instruction.
- MIR* work_half = mir->meta.throw_insn;
- mir->dalvikInsn.opcode = work_half->dalvikInsn.opcode;
- opcode = mir->dalvikInsn.opcode;
- SSARepresentation* ssa_rep = work_half->ssa_rep;
- work_half->ssa_rep = mir->ssa_rep;
- mir->ssa_rep = ssa_rep;
- work_half->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
- if (bb->successor_block_list_type == kCatch) {
- ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(
- art::llvm::IntrinsicHelper::CatchTargets);
- ::llvm::Value* switch_key =
- irb_->CreateCall(intr, irb_->getInt32(mir->offset));
- // New basic block to use for work half
- ::llvm::BasicBlock* work_bb =
- ::llvm::BasicBlock::Create(*context_, "", func_);
- ::llvm::SwitchInst* sw =
- irb_->CreateSwitch(switch_key, work_bb, bb->successor_blocks.size());
- for (SuccessorBlockInfo *successor_block_info : bb->successor_blocks) {
- ::llvm::BasicBlock *target =
- GetLLVMBlock(successor_block_info->block);
- int type_index = successor_block_info->key;
- sw->addCase(irb_->getInt32(type_index), target);
- }
- llvm_bb = work_bb;
- irb_->SetInsertPoint(llvm_bb);
- }
- }
-
- if (IsPseudoMirOp(opcode)) {
- ConvertExtendedMIR(bb, mir, llvm_bb);
- continue;
- }
-
- bool not_handled = ConvertMIRNode(mir, bb, llvm_bb);
- if (not_handled) {
- Instruction::Code dalvik_opcode = static_cast<Instruction::Code>(opcode);
- LOG(WARNING) << StringPrintf("%#06x: Op %#x (%s) / Fmt %d not handled",
- mir->offset, opcode,
- Instruction::Name(dalvik_opcode),
- dalvik_format);
- }
- }
-
- if (bb->block_type == kEntryBlock) {
- entry_target_bb_ = GetLLVMBlock(bb->fall_through);
- } else if ((bb->fall_through != NullBasicBlockId) && !bb->terminated_by_return) {
- irb_->CreateBr(GetLLVMBlock(bb->fall_through));
- }
-
- return false;
-}
-
-char RemapShorty(char shorty_type) {
- /*
- * TODO: might want to revisit this. Dalvik registers are 32-bits wide,
- * and longs/doubles are represented as a pair of registers. When sub-word
- * arguments (and method results) are passed, they are extended to Dalvik
- * virtual register containers. Because llvm is picky about type consistency,
- * we must either cast the "real" type to 32-bit container multiple Dalvik
- * register types, or always use the expanded values.
- * Here, we're doing the latter. We map the shorty signature to container
- * types (which is valid so long as we always do a real expansion of passed
- * arguments and field loads).
- */
- switch (shorty_type) {
- case 'Z' : shorty_type = 'I'; break;
- case 'B' : shorty_type = 'I'; break;
- case 'S' : shorty_type = 'I'; break;
- case 'C' : shorty_type = 'I'; break;
- default: break;
- }
- return shorty_type;
-}
-
-::llvm::FunctionType* MirConverter::GetFunctionType() {
- // Get return type
- ::llvm::Type* ret_type = irb_->getJType(RemapShorty(cu_->shorty[0]));
-
- // Get argument type
- std::vector< ::llvm::Type*> args_type;
-
- // method object
- args_type.push_back(irb_->getJMethodTy());
-
- // Do we have a "this"?
- if ((cu_->access_flags & kAccStatic) == 0) {
- args_type.push_back(irb_->getJObjectTy());
- }
-
- for (uint32_t i = 1; i < strlen(cu_->shorty); ++i) {
- args_type.push_back(irb_->getJType(RemapShorty(cu_->shorty[i])));
- }
-
- return ::llvm::FunctionType::get(ret_type, args_type, false);
-}
-
-bool MirConverter::CreateFunction() {
- ::llvm::FunctionType* func_type = GetFunctionType();
- if (func_type == NULL) {
- return false;
- }
-
- func_ = ::llvm::Function::Create(func_type,
- ::llvm::Function::InternalLinkage,
- symbol_, module_);
-
- ::llvm::Function::arg_iterator arg_iter(func_->arg_begin());
- ::llvm::Function::arg_iterator arg_end(func_->arg_end());
-
- arg_iter->setName("method");
- ++arg_iter;
-
- int start_sreg = mir_graph_->GetFirstInVR();
-
- for (unsigned i = 0; arg_iter != arg_end; ++i, ++arg_iter) {
- arg_iter->setName(StringPrintf("v%i_0", start_sreg));
- start_sreg += mir_graph_->reg_location_[start_sreg].wide ? 2 : 1;
- }
-
- return true;
-}
-
-bool MirConverter::CreateLLVMBasicBlock(BasicBlock* bb) {
- // Skip the exit block
- if ((bb->block_type == kDead) ||(bb->block_type == kExitBlock)) {
- id_to_block_map_.Put(bb->id, NULL);
- } else {
- int offset = bb->start_offset;
- bool entry_block = (bb->block_type == kEntryBlock);
- ::llvm::BasicBlock* llvm_bb =
- ::llvm::BasicBlock::Create(*context_, entry_block ? "entry" :
- StringPrintf(kLabelFormat, bb->catch_entry ? kCatchBlock :
- kNormalBlock, offset, bb->id), func_);
- if (entry_block) {
- entry_bb_ = llvm_bb;
- placeholder_bb_ =
- ::llvm::BasicBlock::Create(*context_, "placeholder",
- func_);
- }
- id_to_block_map_.Put(bb->id, llvm_bb);
- }
- return false;
-}
-
-
-/*
- * Convert MIR to LLVM_IR
- * o For each ssa name, create LLVM named value. Type these
- * appropriately, and ignore high half of wide and double operands.
- * o For each MIR basic block, create an LLVM basic block.
- * o Iterate through the MIR a basic block at a time, setting arguments
- * to recovered ssa name.
- */
-void MirConverter::MethodMIR2Bitcode() {
- InitIR();
-
- // Create the function
- CreateFunction();
-
- // Create an LLVM basic block for each MIR block in dfs preorder
- PreOrderDfsIterator iter(mir_graph_);
- for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
- CreateLLVMBasicBlock(bb);
- }
-
- /*
- * Create an llvm named value for each MIR SSA name. Note: we'll use
- * placeholders for all non-argument values (because we haven't seen
- * the definition yet).
- */
- irb_->SetInsertPoint(placeholder_bb_);
- ::llvm::Function::arg_iterator arg_iter(func_->arg_begin());
- arg_iter++; /* Skip path method */
- for (int i = 0; i < mir_graph_->GetNumSSARegs(); i++) {
- ::llvm::Value* val;
- RegLocation rl_temp = mir_graph_->reg_location_[i];
- if ((mir_graph_->SRegToVReg(i) < 0) || rl_temp.high_word) {
- llvm_values_.push_back(0);
- } else if ((i < mir_graph_->GetFirstInVR()) ||
- (i >= (mir_graph_->GetFirstTempVR()))) {
- ::llvm::Constant* imm_value = mir_graph_->reg_location_[i].wide ?
- irb_->getJLong(0) : irb_->getJInt(0);
- val = EmitConst(imm_value, mir_graph_->reg_location_[i]);
- val->setName(mir_graph_->GetSSAName(i));
- llvm_values_.push_back(val);
- } else {
- // Recover previously-created argument values
- ::llvm::Value* arg_val = arg_iter++;
- llvm_values_.push_back(arg_val);
- }
- }
-
- PreOrderDfsIterator iter2(mir_graph_);
- for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) {
- BlockBitcodeConversion(bb);
- }
-
- /*
- * In a few rare cases of verification failure, the verifier will
- * replace one or more Dalvik opcodes with the special
- * throw-verification-failure opcode. This can leave the SSA graph
- * in an invalid state, as definitions may be lost, while uses retained.
- * To work around this problem, we insert placeholder definitions for
- * all Dalvik SSA regs in the "placeholder" block. Here, after
- * bitcode conversion is complete, we examine those placeholder definitions
- * and delete any with no references (which normally is all of them).
- *
- * If any definitions remain, we link the placeholder block into the
- * CFG. Otherwise, it is deleted.
- */
- for (::llvm::BasicBlock::iterator it = placeholder_bb_->begin(),
- it_end = placeholder_bb_->end(); it != it_end;) {
- ::llvm::Instruction* inst = ::llvm::dyn_cast< ::llvm::Instruction>(it++);
- DCHECK(inst != NULL);
- ::llvm::Value* val = ::llvm::dyn_cast< ::llvm::Value>(inst);
- DCHECK(val != NULL);
- if (val->getNumUses() == 0) {
- inst->eraseFromParent();
- }
- }
- SetDexOffset(0);
- if (placeholder_bb_->empty()) {
- placeholder_bb_->eraseFromParent();
- } else {
- irb_->SetInsertPoint(placeholder_bb_);
- irb_->CreateBr(entry_target_bb_);
- entry_target_bb_ = placeholder_bb_;
- }
- irb_->SetInsertPoint(entry_bb_);
- irb_->CreateBr(entry_target_bb_);
-
- if (cu_->enable_debug & (1 << kDebugVerifyBitcode)) {
- if (::llvm::verifyFunction(*func_, ::llvm::PrintMessageAction)) {
- LOG(INFO) << "Bitcode verification FAILED for "
- << PrettyMethod(cu_->method_idx, *cu_->dex_file)
- << " of size " << mir_graph_->GetNumDalvikInsns();
- cu_->enable_debug |= (1 << kDebugDumpBitcodeFile);
- }
- }
-
- if (cu_->enable_debug & (1 << kDebugDumpBitcodeFile)) {
- // Write bitcode to file
- std::string errmsg;
- std::string fname(PrettyMethod(cu_->method_idx, *cu_->dex_file));
- mir_graph_->ReplaceSpecialChars(fname);
- // TODO: make configurable change naming mechanism to avoid fname length issues.
- fname = StringPrintf("/sdcard/Bitcode/%s.bc", fname.c_str());
-
- if (fname.size() > 240) {
- LOG(INFO) << "Warning: bitcode filename too long. Truncated.";
- fname.resize(240);
- }
-
- ::llvm::OwningPtr< ::llvm::tool_output_file> out_file(
- new ::llvm::tool_output_file(fname.c_str(), errmsg,
- ::llvm::sys::fs::F_Binary));
-
- if (!errmsg.empty()) {
- LOG(ERROR) << "Failed to create bitcode output file: " << errmsg;
- }
-
- ::llvm::WriteBitcodeToFile(module_, out_file->os());
- out_file->keep();
- }
-}
-
-Backend* PortableCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
- ArenaAllocator* const arena,
- llvm::LlvmCompilationUnit* const llvm_compilation_unit) {
- return new MirConverter(cu, mir_graph, arena, llvm_compilation_unit);
-}
-
-} // namespace art
diff --git a/compiler/dex/portable/mir_to_gbc.h b/compiler/dex/portable/mir_to_gbc.h
deleted file mode 100644
index bc4f5c4..0000000
--- a/compiler/dex/portable/mir_to_gbc.h
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_DEX_PORTABLE_MIR_TO_GBC_H_
-#define ART_COMPILER_DEX_PORTABLE_MIR_TO_GBC_H_
-
-#include <llvm/ADT/ArrayRef.h>
-#include <llvm/IR/BasicBlock.h>
-#include <llvm/IR/IRBuilder.h>
-#include <llvm/IR/LLVMContext.h>
-#include <llvm/IR/Module.h>
-
-#include "invoke_type.h"
-#include "compiled_method.h"
-#include "dex/compiler_enums.h"
-#include "dex/compiler_ir.h"
-#include "dex/backend.h"
-#include "llvm/intrinsic_helper.h"
-#include "llvm/llvm_compilation_unit.h"
-#include "safe_map.h"
-#include "utils/arena_containers.h"
-
-namespace llvm {
- class Module;
- class LLVMContext;
-}
-
-namespace art {
-
-namespace llvm {
- class IntrinsicHelper;
- class IRBuilder;
-}
-
-class LLVMInfo {
- public:
- LLVMInfo();
- ~LLVMInfo();
-
- ::llvm::LLVMContext* GetLLVMContext() {
- return llvm_context_.get();
- }
-
- ::llvm::Module* GetLLVMModule() {
- return llvm_module_;
- }
-
- art::llvm::IntrinsicHelper* GetIntrinsicHelper() {
- return intrinsic_helper_.get();
- }
-
- art::llvm::IRBuilder* GetIRBuilder() {
- return ir_builder_.get();
- }
-
- private:
- std::unique_ptr< ::llvm::LLVMContext> llvm_context_;
- ::llvm::Module* llvm_module_; // Managed by context_.
- std::unique_ptr<art::llvm::IntrinsicHelper> intrinsic_helper_;
- std::unique_ptr<art::llvm::IRBuilder> ir_builder_;
-};
-
-class BasicBlock;
-struct CallInfo;
-struct CompilationUnit;
-struct MIR;
-struct RegLocation;
-struct RegisterInfo;
-class MIRGraph;
-
-// Target-specific initialization.
-Backend* PortableCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
- ArenaAllocator* const arena,
- llvm::LlvmCompilationUnit* const llvm_compilation_unit);
-
-class MirConverter : public Backend {
- public:
- // TODO: flesh out and integrate into new world order.
- MirConverter(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena,
- llvm::LlvmCompilationUnit* llvm_compilation_unit)
- : Backend(arena),
- cu_(cu),
- mir_graph_(mir_graph),
- llvm_compilation_unit_(llvm_compilation_unit),
- llvm_info_(llvm_compilation_unit->GetQuickContext()),
- symbol_(llvm_compilation_unit->GetDexCompilationUnit()->GetSymbol()),
- context_(NULL),
- module_(NULL),
- func_(NULL),
- intrinsic_helper_(NULL),
- irb_(NULL),
- placeholder_bb_(NULL),
- entry_bb_(NULL),
- entry_target_bb_(NULL),
- llvm_values_(arena->Adapter()),
- temp_name_(0),
- current_dalvik_offset_(0) {
- llvm_values_.reserve(mir_graph->GetNumSSARegs());
- if (kIsDebugBuild) {
- cu->enable_debug |= (1 << kDebugVerifyBitcode);
- }
- }
-
- void Materialize() {
- MethodMIR2Bitcode();
- }
-
- CompiledMethod* GetCompiledMethod() {
- return NULL;
- }
-
- private:
- ::llvm::BasicBlock* GetLLVMBlock(int id);
- ::llvm::Value* GetLLVMValue(int s_reg);
- void SetVregOnValue(::llvm::Value* val, int s_reg);
- void DefineValueOnly(::llvm::Value* val, int s_reg);
- void DefineValue(::llvm::Value* val, int s_reg);
- ::llvm::Type* LlvmTypeFromLocRec(RegLocation loc);
- void InitIR();
- ::llvm::BasicBlock* FindCaseTarget(uint32_t vaddr);
- void ConvertPackedSwitch(BasicBlock* bb, MIR* mir, int32_t table_offset,
- RegLocation rl_src);
- void ConvertSparseSwitch(BasicBlock* bb, MIR* mir, int32_t table_offset,
- RegLocation rl_src);
- void ConvertSget(int32_t field_index,
- art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest);
- void ConvertSput(int32_t field_index,
- art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_src);
- void ConvertFillArrayData(int32_t offset, RegLocation rl_array);
- ::llvm::Value* EmitConst(::llvm::ArrayRef< ::llvm::Value*> src,
- RegLocation loc);
- void EmitPopShadowFrame();
- ::llvm::Value* EmitCopy(::llvm::ArrayRef< ::llvm::Value*> src,
- RegLocation loc);
- void ConvertMoveException(RegLocation rl_dest);
- void ConvertThrow(RegLocation rl_src);
- void ConvertMonitorEnterExit(int opt_flags,
- art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_src);
- void ConvertArrayLength(int opt_flags, RegLocation rl_dest,
- RegLocation rl_src);
- void EmitSuspendCheck();
- ::llvm::Value* ConvertCompare(ConditionCode cc,
- ::llvm::Value* src1, ::llvm::Value* src2);
- void ConvertCompareAndBranch(BasicBlock* bb, MIR* mir, ConditionCode cc,
- RegLocation rl_src1, RegLocation rl_src2);
- void ConvertCompareZeroAndBranch(BasicBlock* bb, MIR* mir, ConditionCode cc,
- RegLocation rl_src1);
- ::llvm::Value* GenDivModOp(bool is_div, bool is_long, ::llvm::Value* src1,
- ::llvm::Value* src2);
- ::llvm::Value* GenArithOp(OpKind op, bool is_long, ::llvm::Value* src1,
- ::llvm::Value* src2);
- void ConvertFPArithOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
- RegLocation rl_src2);
- void ConvertShift(art::llvm::IntrinsicHelper::IntrinsicId id,
- RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
- void ConvertShiftLit(art::llvm::IntrinsicHelper::IntrinsicId id,
- RegLocation rl_dest, RegLocation rl_src, int shift_amount);
- void ConvertArithOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
- RegLocation rl_src2);
- void ConvertArithOpLit(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
- int32_t imm);
- void ConvertInvoke(BasicBlock* bb, MIR* mir, InvokeType invoke_type,
- bool is_range, bool is_filled_new_array);
- void ConvertConstObject(uint32_t idx,
- art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest);
- void ConvertCheckCast(uint32_t type_idx, RegLocation rl_src);
- void ConvertNewInstance(uint32_t type_idx, RegLocation rl_dest);
- void ConvertNewArray(uint32_t type_idx, RegLocation rl_dest,
- RegLocation rl_src);
- void ConvertAget(int opt_flags, art::llvm::IntrinsicHelper::IntrinsicId id,
- RegLocation rl_dest, RegLocation rl_array, RegLocation rl_index);
- void ConvertAput(int opt_flags, art::llvm::IntrinsicHelper::IntrinsicId id,
- RegLocation rl_src, RegLocation rl_array, RegLocation rl_index);
- void ConvertIget(int opt_flags, art::llvm::IntrinsicHelper::IntrinsicId id,
- RegLocation rl_dest, RegLocation rl_obj, int field_index);
- void ConvertIput(int opt_flags, art::llvm::IntrinsicHelper::IntrinsicId id,
- RegLocation rl_src, RegLocation rl_obj, int field_index);
- void ConvertInstanceOf(uint32_t type_idx, RegLocation rl_dest,
- RegLocation rl_src);
- void ConvertIntToLong(RegLocation rl_dest, RegLocation rl_src);
- void ConvertLongToInt(RegLocation rl_dest, RegLocation rl_src);
- void ConvertFloatToDouble(RegLocation rl_dest, RegLocation rl_src);
- void ConvertDoubleToFloat(RegLocation rl_dest, RegLocation rl_src);
- void ConvertWideComparison(art::llvm::IntrinsicHelper::IntrinsicId id,
- RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
- void ConvertIntNarrowing(RegLocation rl_dest, RegLocation rl_src,
- art::llvm::IntrinsicHelper::IntrinsicId id);
- void ConvertNeg(RegLocation rl_dest, RegLocation rl_src);
- void ConvertIntToFP(::llvm::Type* ty, RegLocation rl_dest, RegLocation rl_src);
- void ConvertFPToInt(art::llvm::IntrinsicHelper::IntrinsicId id,
- RegLocation rl_dest, RegLocation rl_src);
- void ConvertNegFP(RegLocation rl_dest, RegLocation rl_src);
- void ConvertNot(RegLocation rl_dest, RegLocation rl_src);
- void EmitConstructorBarrier();
- bool ConvertMIRNode(MIR* mir, BasicBlock* bb, ::llvm::BasicBlock* llvm_bb);
- void SetDexOffset(int32_t offset);
- void SetMethodInfo();
- void HandlePhiNodes(BasicBlock* bb, ::llvm::BasicBlock* llvm_bb);
- void ConvertExtendedMIR(BasicBlock* bb, MIR* mir, ::llvm::BasicBlock* llvm_bb);
- bool BlockBitcodeConversion(BasicBlock* bb);
- ::llvm::FunctionType* GetFunctionType();
- bool CreateFunction();
- bool CreateLLVMBasicBlock(BasicBlock* bb);
- void MethodMIR2Bitcode();
-
- CompilationUnit* cu_;
- MIRGraph* mir_graph_;
- llvm::LlvmCompilationUnit* const llvm_compilation_unit_;
- LLVMInfo* llvm_info_;
- std::string symbol_;
- ::llvm::LLVMContext* context_;
- ::llvm::Module* module_;
- ::llvm::Function* func_;
- art::llvm::IntrinsicHelper* intrinsic_helper_;
- art::llvm::IRBuilder* irb_;
- ::llvm::BasicBlock* placeholder_bb_;
- ::llvm::BasicBlock* entry_bb_;
- ::llvm::BasicBlock* entry_target_bb_;
- std::string bitcode_filename_;
- ArenaVector< ::llvm::Value*> llvm_values_;
- int32_t temp_name_;
- SafeMap<int32_t, ::llvm::BasicBlock*> id_to_block_map_; // block id -> llvm bb.
- int current_dalvik_offset_;
-}; // Class MirConverter
-
-} // namespace art
-
-#endif // ART_COMPILER_DEX_PORTABLE_MIR_TO_GBC_H_
diff --git a/compiler/dex/post_opt_passes.cc b/compiler/dex/post_opt_passes.cc
index 675dbcf..9262440 100644
--- a/compiler/dex/post_opt_passes.cc
+++ b/compiler/dex/post_opt_passes.cc
@@ -15,40 +15,11 @@
*/
#include "post_opt_passes.h"
-#include "dataflow_iterator.h"
+
#include "dataflow_iterator-inl.h"
namespace art {
-/*
- * MethodUseCount pass implementation start.
- */
-bool MethodUseCount::Gate(const PassDataHolder* data) const {
- DCHECK(data != nullptr);
- CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
- DCHECK(c_unit != nullptr);
- // First initialize the data.
- c_unit->mir_graph->InitializeMethodUses();
-
- // Now check if the pass is to be ignored.
- bool res = ((c_unit->disable_opt & (1 << kPromoteRegs)) == 0);
-
- return res;
-}
-
-bool MethodUseCount::Worker(PassDataHolder* data) const {
- DCHECK(data != nullptr);
- PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
- CompilationUnit* c_unit = pass_me_data_holder->c_unit;
- DCHECK(c_unit != nullptr);
- BasicBlock* bb = pass_me_data_holder->bb;
- DCHECK(bb != nullptr);
- c_unit->mir_graph->CountUses(bb);
- // No need of repeating, so just return false.
- return false;
-}
-
-
bool ClearPhiInstructions::Worker(PassDataHolder* data) const {
DCHECK(data != nullptr);
PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
diff --git a/compiler/dex/post_opt_passes.h b/compiler/dex/post_opt_passes.h
index 7b84ba8..1ab8625 100644
--- a/compiler/dex/post_opt_passes.h
+++ b/compiler/dex/post_opt_passes.h
@@ -17,20 +17,41 @@
#ifndef ART_COMPILER_DEX_POST_OPT_PASSES_H_
#define ART_COMPILER_DEX_POST_OPT_PASSES_H_
-#include "dex/quick/mir_to_lir.h"
-#include "compiler_internals.h"
+#include "base/casts.h"
+#include "base/logging.h"
+#include "compiler_ir.h"
+#include "dex_flags.h"
+#include "mir_graph.h"
#include "pass_me.h"
namespace art {
/**
- * @class InitializeData
+ * @class PassMEMirSsaRep
+ * @brief Convenience class for passes that check MIRGraph::MirSsaRepUpToDate().
+ */
+class PassMEMirSsaRep : public PassME {
+ public:
+ PassMEMirSsaRep(const char* name, DataFlowAnalysisMode type = kAllNodes)
+ : PassME(name, type) {
+ }
+
+ bool Gate(const PassDataHolder* data) const OVERRIDE {
+ DCHECK(data != nullptr);
+ CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ return !c_unit->mir_graph->MirSsaRepUpToDate();
+ }
+};
+
+/**
+ * @class InitializeSSATransformation
* @brief There is some data that needs to be initialized before performing
* the post optimization passes.
*/
-class InitializeData : public PassME {
+class InitializeSSATransformation : public PassMEMirSsaRep {
public:
- InitializeData() : PassME("InitializeData", kNoNodes) {
+ InitializeSSATransformation() : PassMEMirSsaRep("InitializeSSATransformation", kNoNodes) {
}
void Start(PassDataHolder* data) const {
@@ -39,32 +60,18 @@
DCHECK(data != nullptr);
CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
DCHECK(c_unit != nullptr);
- c_unit->mir_graph.get()->InitializeBasicBlockData();
- c_unit->mir_graph.get()->SSATransformationStart();
+ c_unit->mir_graph->SSATransformationStart();
+ c_unit->mir_graph->CompilerInitializeSSAConversion();
}
};
/**
- * @class MethodUseCount
- * @brief Count the register uses of the method
- */
-class MethodUseCount : public PassME {
- public:
- MethodUseCount() : PassME("UseCount") {
- }
-
- bool Worker(PassDataHolder* data) const;
-
- bool Gate(const PassDataHolder* data) const;
-};
-
-/**
* @class ClearPhiInformation
* @brief Clear the PHI nodes from the CFG.
*/
-class ClearPhiInstructions : public PassME {
+class ClearPhiInstructions : public PassMEMirSsaRep {
public:
- ClearPhiInstructions() : PassME("ClearPhiInstructions") {
+ ClearPhiInstructions() : PassMEMirSsaRep("ClearPhiInstructions") {
}
bool Worker(PassDataHolder* data) const;
@@ -115,12 +122,18 @@
BuildDomination() : PassME("BuildDomination", kNoNodes) {
}
+ bool Gate(const PassDataHolder* data) const {
+ DCHECK(data != nullptr);
+ CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ return !c_unit->mir_graph->DominationUpToDate();
+ }
+
void Start(PassDataHolder* data) const {
DCHECK(data != nullptr);
CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
DCHECK(c_unit != nullptr);
- c_unit->mir_graph.get()->ComputeDominators();
- c_unit->mir_graph.get()->CompilerInitializeSSAConversion();
+ c_unit->mir_graph->ComputeDominators();
}
void End(PassDataHolder* data) const {
@@ -143,6 +156,13 @@
TopologicalSortOrders() : PassME("TopologicalSortOrders", kNoNodes) {
}
+ bool Gate(const PassDataHolder* data) const {
+ DCHECK(data != nullptr);
+ CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ return !c_unit->mir_graph->TopologicalOrderUpToDate();
+ }
+
void Start(PassDataHolder* data) const {
DCHECK(data != nullptr);
CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
@@ -155,9 +175,9 @@
* @class DefBlockMatrix
* @brief Calculate the matrix of definition per basic block
*/
-class DefBlockMatrix : public PassME {
+class DefBlockMatrix : public PassMEMirSsaRep {
public:
- DefBlockMatrix() : PassME("DefBlockMatrix", kNoNodes) {
+ DefBlockMatrix() : PassMEMirSsaRep("DefBlockMatrix", kNoNodes) {
}
void Start(PassDataHolder* data) const {
@@ -169,37 +189,19 @@
};
/**
- * @class CreatePhiNodes
- * @brief Pass to create the phi nodes after SSA calculation
+ * @class FindPhiNodeBlocksPass
+ * @brief Pass to find out where we need to insert the phi nodes for the SSA conversion.
*/
-class CreatePhiNodes : public PassME {
+class FindPhiNodeBlocksPass : public PassMEMirSsaRep {
public:
- CreatePhiNodes() : PassME("CreatePhiNodes", kNoNodes) {
+ FindPhiNodeBlocksPass() : PassMEMirSsaRep("FindPhiNodeBlocks", kNoNodes) {
}
void Start(PassDataHolder* data) const {
DCHECK(data != nullptr);
CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
DCHECK(c_unit != nullptr);
- c_unit->mir_graph.get()->InsertPhiNodes();
- }
-};
-
-/**
- * @class ClearVisitedFlag
- * @brief Pass to clear the visited flag for all basic blocks.
- */
-
-class ClearVisitedFlag : public PassME {
- public:
- ClearVisitedFlag() : PassME("ClearVisitedFlag", kNoNodes) {
- }
-
- void Start(PassDataHolder* data) const {
- DCHECK(data != nullptr);
- CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
- DCHECK(c_unit != nullptr);
- c_unit->mir_graph.get()->ClearAllVisitedFlags();
+ c_unit->mir_graph.get()->FindPhiNodeBlocks();
}
};
@@ -207,9 +209,9 @@
* @class SSAConversion
* @brief Pass for SSA conversion of MIRs
*/
-class SSAConversion : public PassME {
+class SSAConversion : public PassMEMirSsaRep {
public:
- SSAConversion() : PassME("SSAConversion", kNoNodes) {
+ SSAConversion() : PassMEMirSsaRep("SSAConversion", kNoNodes) {
}
void Start(PassDataHolder* data) const {
@@ -217,6 +219,7 @@
CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
DCHECK(c_unit != nullptr);
MIRGraph *mir_graph = c_unit->mir_graph.get();
+ mir_graph->ClearAllVisitedFlags();
mir_graph->DoDFSPreOrderSSARename(mir_graph->GetEntryBlock());
}
};
@@ -225,9 +228,9 @@
* @class PhiNodeOperands
* @brief Pass to insert the Phi node operands to basic blocks
*/
-class PhiNodeOperands : public PassME {
+class PhiNodeOperands : public PassMEMirSsaRep {
public:
- PhiNodeOperands() : PassME("PhiNodeOperands", kPreOrderDFSTraversal) {
+ PhiNodeOperands() : PassMEMirSsaRep("PhiNodeOperands", kPreOrderDFSTraversal) {
}
bool Worker(PassDataHolder* data) const {
@@ -246,9 +249,9 @@
* @class InitRegLocations
* @brief Initialize Register Locations.
*/
-class PerformInitRegLocations : public PassME {
+class PerformInitRegLocations : public PassMEMirSsaRep {
public:
- PerformInitRegLocations() : PassME("PerformInitRegLocation", kNoNodes) {
+ PerformInitRegLocations() : PassMEMirSsaRep("PerformInitRegLocation", kNoNodes) {
}
void Start(PassDataHolder* data) const {
@@ -260,40 +263,32 @@
};
/**
- * @class ConstantPropagation
- * @brief Perform a constant propagation pass.
+ * @class TypeInference
+ * @brief Type inference pass.
*/
-class ConstantPropagation : public PassME {
+class TypeInference : public PassMEMirSsaRep {
public:
- ConstantPropagation() : PassME("ConstantPropagation") {
+ TypeInference() : PassMEMirSsaRep("TypeInference", kRepeatingPreOrderDFSTraversal) {
}
bool Worker(PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+ PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+ CompilationUnit* c_unit = pass_me_data_holder->c_unit;
DCHECK(c_unit != nullptr);
- BasicBlock* bb = down_cast<PassMEDataHolder*>(data)->bb;
+ BasicBlock* bb = pass_me_data_holder->bb;
DCHECK(bb != nullptr);
- c_unit->mir_graph->DoConstantPropagation(bb);
- // No need of repeating, so just return false.
- return false;
- }
-
- void Start(PassDataHolder* data) const {
- DCHECK(data != nullptr);
- CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
- DCHECK(c_unit != nullptr);
- c_unit->mir_graph->InitializeConstantPropagation();
+ return c_unit->mir_graph->InferTypes(bb);
}
};
/**
- * @class FreeData
+ * @class FinishSSATransformation
* @brief There is some data that needs to be freed after performing the post optimization passes.
*/
-class FreeData : public PassME {
+class FinishSSATransformation : public PassMEMirSsaRep {
public:
- FreeData() : PassME("FreeData", kNoNodes) {
+ FinishSSATransformation() : PassMEMirSsaRep("FinishSSATransformation", kNoNodes) {
}
void End(PassDataHolder* data) const {
diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h
index b9d9a11..9717459 100644
--- a/compiler/dex/quick/arm/arm_lir.h
+++ b/compiler/dex/quick/arm/arm_lir.h
@@ -17,7 +17,9 @@
#ifndef ART_COMPILER_DEX_QUICK_ARM_ARM_LIR_H_
#define ART_COMPILER_DEX_QUICK_ARM_ARM_LIR_H_
-#include "dex/compiler_internals.h"
+#include "dex/compiler_enums.h"
+#include "dex/reg_location.h"
+#include "dex/reg_storage.h"
namespace art {
@@ -481,10 +483,10 @@
kThumb2LsrRRR, // lsr [111110100010] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
kThumb2AsrRRR, // asr [111110100100] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
kThumb2RorRRR, // ror [111110100110] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
- kThumb2LslRRI5, // lsl [11101010010011110] imm[14.12] rd[11..8] [00] rm[3..0].
- kThumb2LsrRRI5, // lsr [11101010010011110] imm[14.12] rd[11..8] [01] rm[3..0].
- kThumb2AsrRRI5, // asr [11101010010011110] imm[14.12] rd[11..8] [10] rm[3..0].
- kThumb2RorRRI5, // ror [11101010010011110] imm[14.12] rd[11..8] [11] rm[3..0].
+ kThumb2LslRRI5, // lsl [11101010010011110] imm3[14..12] rd[11..8] imm2[7..6] [00] rm[3..0].
+ kThumb2LsrRRI5, // lsr [11101010010011110] imm3[14..12] rd[11..8] imm2[7..6] [01] rm[3..0].
+ kThumb2AsrRRI5, // asr [11101010010011110] imm3[14..12] rd[11..8] imm2[7..6] [10] rm[3..0].
+ kThumb2RorRRI5, // ror [11101010010011110] imm3[14..12] rd[11..8] imm2[7..6] [11] rm[3..0].
kThumb2BicRRI8M, // bic rd, rn, #<const> [11110] i [000010] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0].
kThumb2AndRRI8M, // and rd, rn, #<const> [11110] i [000000] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0].
kThumb2OrrRRI8M, // orr rd, rn, #<const> [11110] i [000100] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0].
@@ -512,7 +514,8 @@
kThumb2Vnegs, // vneg.f32 [111011101] D [110000] rd[15-12] [1010110] M [0] vm[3-0].
kThumb2Vmovs_IMM8, // vmov.f32 [111011101] D [11] imm4h[19-16] vd[15-12] [10100000] imm4l[3-0].
kThumb2Vmovd_IMM8, // vmov.f64 [111011101] D [11] imm4h[19-16] vd[15-12] [10110000] imm4l[3-0].
- kThumb2Mla, // mla [111110110000] rn[19-16] ra[15-12] rd[7-4] [0000] rm[3-0].
+ kThumb2Mla, // mla [111110110000] rn[19-16] ra[15-12] rd[11-8] [0000] rm[3-0].
+ kThumb2Mls, // mls [111110110000] rn[19-16] ra[15-12] rd[11-8] [0001] rm[3-0].
kThumb2Umull, // umull [111110111010] rn[19-16], rdlo[15-12] rdhi[11-8] [0000] rm[3-0].
kThumb2Ldrex, // ldrex [111010000101] rn[19-16] rt[15-12] [1111] imm8[7-0].
kThumb2Ldrexd, // ldrexd [111010001101] rn[19-16] rt[15-12] rt2[11-8] [11111111].
diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc
index de93e26..3d64833 100644
--- a/compiler/dex/quick/arm/assemble_arm.cc
+++ b/compiler/dex/quick/arm/assemble_arm.cc
@@ -14,8 +14,11 @@
* limitations under the License.
*/
-#include "arm_lir.h"
#include "codegen_arm.h"
+
+#include "arm_lir.h"
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
#include "dex/quick/mir_to_lir-inl.h"
namespace art {
@@ -896,6 +899,10 @@
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 15, 12, IS_QUAD_OP | REG_DEF0_USE123,
"mla", "!0C, !1C, !2C, !3C", 4, kFixupNone),
+ ENCODING_MAP(kThumb2Mls, 0xfb000010,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 15, 12, IS_QUAD_OP | REG_DEF0_USE123,
+ "mls", "!0C, !1C, !2C, !3C", 4, kFixupNone),
ENCODING_MAP(kThumb2Umull, 0xfba00000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
kFmtBitBlt, 3, 0,
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index 99b2166..1b5dde2 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -16,13 +16,18 @@
/* This file contains codegen for the Thumb2 ISA. */
-#include "arm_lir.h"
#include "codegen_arm.h"
+
+#include "arm_lir.h"
+#include "base/logging.h"
+#include "dex/mir_graph.h"
#include "dex/quick/mir_to_lir-inl.h"
+#include "driver/compiler_driver.h"
#include "gc/accounting/card_table.h"
#include "mirror/art_method.h"
#include "mirror/object_array-inl.h"
#include "entrypoints/quick/quick_entrypoints.h"
+#include "utils.h"
namespace art {
@@ -47,16 +52,13 @@
*/
void ArmMir2Lir::GenLargeSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
- if (cu_->verbose) {
- DumpSparseSwitchTable(table);
- }
// Add the table to the list - we'll process it later
SwitchTable *tab_rec =
static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
+ tab_rec->switch_mir = mir;
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
uint32_t size = table[1];
- tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), kArenaAllocLIR));
switch_tables_.push_back(tab_rec);
// Get the switch value
@@ -95,17 +97,13 @@
void ArmMir2Lir::GenLargePackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
- if (cu_->verbose) {
- DumpPackedSwitchTable(table);
- }
// Add the table to the list - we'll process it later
SwitchTable *tab_rec =
static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
+ tab_rec->switch_mir = mir;
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
uint32_t size = table[1];
- tab_rec->targets =
- static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), kArenaAllocLIR));
switch_tables_.push_back(tab_rec);
// Get the switch value
diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h
index 0bc4c3b..025e69f 100644
--- a/compiler/dex/quick/arm/codegen_arm.h
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -18,24 +18,16 @@
#define ART_COMPILER_DEX_QUICK_ARM_CODEGEN_ARM_H_
#include "arm_lir.h"
-#include "dex/compiler_internals.h"
+#include "base/logging.h"
#include "dex/quick/mir_to_lir.h"
#include "utils/arena_containers.h"
namespace art {
+struct CompilationUnit;
+
class ArmMir2Lir FINAL : public Mir2Lir {
protected:
- // TODO: Consolidate hard float target support.
- // InToRegStorageMapper and InToRegStorageMapping can be shared with all backends.
- // Base class used to get RegStorage for next argument.
- class InToRegStorageMapper {
- public:
- virtual RegStorage GetNextReg(bool is_double_or_float, bool is_wide) = 0;
- virtual ~InToRegStorageMapper() {
- }
- };
-
// Inherited class for ARM backend.
class InToRegStorageArmMapper FINAL : public InToRegStorageMapper {
public:
@@ -43,45 +35,25 @@
: cur_core_reg_(0), cur_fp_reg_(0), cur_fp_double_reg_(0) {
}
- virtual ~InToRegStorageArmMapper() {
- }
+ RegStorage GetNextReg(ShortyArg arg) OVERRIDE;
- RegStorage GetNextReg(bool is_double_or_float, bool is_wide) OVERRIDE;
+ virtual void Reset() OVERRIDE {
+ cur_core_reg_ = 0;
+ cur_fp_reg_ = 0;
+ cur_fp_double_reg_ = 0;
+ }
private:
- uint32_t cur_core_reg_;
- uint32_t cur_fp_reg_;
- uint32_t cur_fp_double_reg_;
+ size_t cur_core_reg_;
+ size_t cur_fp_reg_;
+ size_t cur_fp_double_reg_;
};
- // Class to map argument to RegStorage. The mapping object is initialized by a mapper.
- class InToRegStorageMapping FINAL {
- public:
- InToRegStorageMapping()
- : max_mapped_in_(0), is_there_stack_mapped_(false), initialized_(false) {
- }
-
- int GetMaxMappedIn() const {
- return max_mapped_in_;
- }
-
- bool IsThereStackMapped() const {
- return is_there_stack_mapped_;
- }
-
- bool IsInitialized() const {
- return initialized_;
- }
-
- void Initialize(RegLocation* arg_locs, int count, InToRegStorageMapper* mapper);
- RegStorage Get(int in_position) const;
-
- private:
- std::map<int, RegStorage> mapping_;
- int max_mapped_in_;
- bool is_there_stack_mapped_;
- bool initialized_;
- };
+ InToRegStorageArmMapper in_to_reg_storage_arm_mapper_;
+ InToRegStorageMapper* GetResetedInToRegStorageMapper() OVERRIDE {
+ in_to_reg_storage_arm_mapper_.Reset();
+ return &in_to_reg_storage_arm_mapper_;
+ }
public:
ArmMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
@@ -127,7 +99,6 @@
}
}
- RegStorage GetArgMappingToPhysicalReg(int arg_num) OVERRIDE;
RegLocation GetReturnAlt() OVERRIDE;
RegLocation GetReturnWideAlt() OVERRIDE;
RegLocation LocCReturn() OVERRIDE;
@@ -213,6 +184,8 @@
void GenNegFloat(RegLocation rl_dest, RegLocation rl_src);
void GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src);
void GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src);
+ void GenMaddMsubInt(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
+ RegLocation rl_src3, bool is_sub);
// Required for target - single operation generators.
LIR* OpUnconditionalBranch(LIR* target);
@@ -290,18 +263,10 @@
LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) OVERRIDE;
size_t GetInstructionOffset(LIR* lir);
- int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel,
- NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx,
- uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
- bool skip_this) OVERRIDE;
- int GenDalvikArgsRange(CallInfo* info, int call_state, LIR** pcrLabel,
- NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx,
- uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
- bool skip_this) OVERRIDE;
+ void GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) OVERRIDE;
+
+ bool HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div,
+ RegLocation rl_src, RegLocation rl_dest, int lit) OVERRIDE;
private:
void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
@@ -361,7 +326,7 @@
RegStorage::FloatSolo32(reg_num * 2 + 1));
}
- InToRegStorageMapping in_to_reg_storage_mapping_;
+ int GenDalvikArgsBulkCopy(CallInfo* info, int first, int count) OVERRIDE;
};
} // namespace art
diff --git a/compiler/dex/quick/arm/fp_arm.cc b/compiler/dex/quick/arm/fp_arm.cc
index 2b2592d..eb1383f 100644
--- a/compiler/dex/quick/arm/fp_arm.cc
+++ b/compiler/dex/quick/arm/fp_arm.cc
@@ -14,8 +14,11 @@
* limitations under the License.
*/
-#include "arm_lir.h"
#include "codegen_arm.h"
+
+#include "arm_lir.h"
+#include "base/logging.h"
+#include "dex/mir_graph.h"
#include "dex/quick/mir_to_lir-inl.h"
namespace art {
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index 1a7b439..3159886 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -16,13 +16,19 @@
/* This file contains codegen for the Thumb2 ISA. */
+#include "codegen_arm.h"
+
#include "arch/instruction_set_features.h"
#include "arm_lir.h"
-#include "codegen_arm.h"
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
+#include "dex/mir_graph.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "dex/reg_storage_eq.h"
+#include "driver/compiler_driver.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "mirror/array-inl.h"
+#include "utils.h"
namespace art {
@@ -567,21 +573,30 @@
// Try to convert *lit to 1 RegRegRegShift/RegRegShift form.
bool ArmMir2Lir::GetEasyMultiplyOp(int lit, ArmMir2Lir::EasyMultiplyOp* op) {
+ if (lit == 0) {
+ // Special case for *divide-by-zero*. The ops won't actually be used to generate code, as
+ // GenArithOpIntLit will directly generate exception-throwing code, and multiply-by-zero will
+ // have been optimized away earlier.
+ op->op = kOpInvalid;
+ op->shift = 0;
+ return true;
+ }
+
if (IsPowerOfTwo(lit)) {
op->op = kOpLsl;
- op->shift = LowestSetBit(lit);
+ op->shift = CTZ(lit);
return true;
}
if (IsPowerOfTwo(lit - 1)) {
op->op = kOpAdd;
- op->shift = LowestSetBit(lit - 1);
+ op->shift = CTZ(lit - 1);
return true;
}
if (IsPowerOfTwo(lit + 1)) {
op->op = kOpRsub;
- op->shift = LowestSetBit(lit + 1);
+ op->shift = CTZ(lit + 1);
return true;
}
@@ -599,7 +614,7 @@
}
int lit1 = lit;
- uint32_t shift = LowestSetBit(lit1);
+ uint32_t shift = CTZ(lit1);
if (GetEasyMultiplyOp(lit1 >> shift, &ops[0])) {
ops[1].op = kOpLsl;
ops[1].shift = shift;
@@ -607,7 +622,7 @@
}
lit1 = lit - 1;
- shift = LowestSetBit(lit1);
+ shift = CTZ(lit1);
if (GetEasyMultiplyOp(lit1 >> shift, &ops[0])) {
ops[1].op = kOpAdd;
ops[1].shift = shift;
@@ -615,7 +630,7 @@
}
lit1 = lit + 1;
- shift = LowestSetBit(lit1);
+ shift = CTZ(lit1);
if (GetEasyMultiplyOp(lit1 >> shift, &ops[0])) {
ops[1].op = kOpRsub;
ops[1].shift = shift;
@@ -1075,6 +1090,17 @@
return NewLIR3(kThumb2Vstms, r_base.GetReg(), rs_fr0.GetReg(), count);
}
+void ArmMir2Lir::GenMaddMsubInt(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
+ RegLocation rl_src3, bool is_sub) {
+ rl_src1 = LoadValue(rl_src1, kCoreReg);
+ rl_src2 = LoadValue(rl_src2, kCoreReg);
+ rl_src3 = LoadValue(rl_src3, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ NewLIR4(is_sub ? kThumb2Mls : kThumb2Mla, rl_result.reg.GetReg(), rl_src1.reg.GetReg(),
+ rl_src2.reg.GetReg(), rl_src3.reg.GetReg());
+ StoreValue(rl_dest, rl_result);
+}
+
void ArmMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
RegLocation rl_result, int lit,
int first_bit, int second_bit) {
@@ -1119,7 +1145,7 @@
}
bool ArmMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
- if (!cu_->GetInstructionSetFeatures()->IsSmp()) {
+ if (!cu_->compiler_driver->GetInstructionSetFeatures()->IsSmp()) {
return false;
}
// Start off with using the last LIR as the barrier. If it is not enough, then we will generate one.
@@ -1635,4 +1661,19 @@
StoreValueWide(rl_dest, rl_result);
}
+bool ArmMir2Lir::HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div,
+ RegLocation rl_src, RegLocation rl_dest, int lit) {
+ if (lit < 2) {
+ return false;
+ }
+
+ // ARM does either not support a division instruction, or it is potentially expensive. Look for
+ // more special cases.
+ if (!IsPowerOfTwo(lit)) {
+ return SmallLiteralDivRem(dalvik_opcode, is_div, rl_src, rl_dest, lit);
+ }
+
+ return Mir2Lir::HandleEasyDivRem(dalvik_opcode, is_div, rl_src, rl_dest, lit);
+}
+
} // namespace art
diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc
index 0e8f645..13f9072 100644
--- a/compiler/dex/quick/arm/target_arm.cc
+++ b/compiler/dex/quick/arm/target_arm.cc
@@ -19,9 +19,11 @@
#include <inttypes.h>
#include <string>
+#include <sstream>
#include "backend_arm.h"
-#include "dex/compiler_internals.h"
+#include "base/logging.h"
+#include "dex/mir_graph.h"
#include "dex/quick/mir_to_lir-inl.h"
namespace art {
@@ -489,6 +491,24 @@
buf += *fmt++;
}
}
+ // Dump thread offset.
+ std::string fmt_str = GetTargetInstFmt(lir->opcode);
+ if (std::string::npos != fmt_str.find(", [!1C, #!2") && rARM_SELF == lir->operands[1] &&
+ std::string::npos != buf.find(", [")) {
+ int offset = lir->operands[2];
+ if (std::string::npos != fmt_str.find("#!2d")) {
+ } else if (std::string::npos != fmt_str.find("#!2E")) {
+ offset *= 4;
+ } else if (std::string::npos != fmt_str.find("#!2F")) {
+ offset *= 2;
+ } else {
+ LOG(FATAL) << "Should not reach here";
+ }
+ std::ostringstream tmp_stream;
+ Thread::DumpThreadOffset<4>(tmp_stream, offset);
+ buf += " ; ";
+ buf += tmp_stream.str();
+ }
return buf;
}
@@ -749,6 +769,7 @@
FreeTemp(rs_r1);
FreeTemp(rs_r2);
FreeTemp(rs_r3);
+ FreeTemp(TargetReg(kHiddenArg));
if (!kArm32QuickCodeUseSoftFloat) {
FreeTemp(rs_fr0);
FreeTemp(rs_fr1);
@@ -896,7 +917,7 @@
Mir2Lir::InstallLiteralPools();
}
-RegStorage ArmMir2Lir::InToRegStorageArmMapper::GetNextReg(bool is_double_or_float, bool is_wide) {
+RegStorage ArmMir2Lir::InToRegStorageArmMapper::GetNextReg(ShortyArg arg) {
const RegStorage coreArgMappingToPhysicalReg[] =
{rs_r1, rs_r2, rs_r3};
const int coreArgMappingToPhysicalRegSize = arraysize(coreArgMappingToPhysicalReg);
@@ -906,28 +927,18 @@
constexpr uint32_t fpArgMappingToPhysicalRegSize = arraysize(fpArgMappingToPhysicalReg);
static_assert(fpArgMappingToPhysicalRegSize % 2 == 0, "Number of FP Arg regs is not even");
- if (kArm32QuickCodeUseSoftFloat) {
- is_double_or_float = false; // Regard double as long, float as int.
- is_wide = false; // Map long separately.
- }
-
RegStorage result = RegStorage::InvalidReg();
- if (is_double_or_float) {
- // TODO: Remove "cur_fp_double_reg_ % 2 != 0" when we return double as double.
- if (is_wide || cur_fp_double_reg_ % 2 != 0) {
+ // Regard double as long, float as int for kArm32QuickCodeUseSoftFloat.
+ if (arg.IsFP() && !kArm32QuickCodeUseSoftFloat) {
+ if (arg.IsWide()) {
cur_fp_double_reg_ = std::max(cur_fp_double_reg_, RoundUp(cur_fp_reg_, 2));
if (cur_fp_double_reg_ < fpArgMappingToPhysicalRegSize) {
- // TODO: Replace by following code in the branch when FlushIns() support 64-bit registers.
- // result = RegStorage::MakeRegPair(fpArgMappingToPhysicalReg[cur_fp_double_reg_],
- // fpArgMappingToPhysicalReg[cur_fp_double_reg_ + 1]);
- // result = As64BitFloatReg(result);
- // cur_fp_double_reg_ += 2;
- result = fpArgMappingToPhysicalReg[cur_fp_double_reg_];
- cur_fp_double_reg_++;
+ result = RegStorage::MakeRegPair(fpArgMappingToPhysicalReg[cur_fp_double_reg_],
+ fpArgMappingToPhysicalReg[cur_fp_double_reg_ + 1]);
+ result = As64BitFloatReg(result);
+ cur_fp_double_reg_ += 2;
}
} else {
- // TODO: Remove the check when we return double as double.
- DCHECK_EQ(cur_fp_double_reg_ % 2, 0U);
if (cur_fp_reg_ % 2 == 0) {
cur_fp_reg_ = std::max(cur_fp_double_reg_, cur_fp_reg_);
}
@@ -938,271 +949,54 @@
}
} else {
if (cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
+ if (!kArm32QuickCodeUseSoftFloat && arg.IsWide() && cur_core_reg_ == 0) {
+ // Skip r1, and use r2-r3 for the register pair.
+ cur_core_reg_++;
+ }
result = coreArgMappingToPhysicalReg[cur_core_reg_++];
- // TODO: Enable following code when FlushIns() support 64-bit registers.
- // if (is_wide && cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
- // result = RegStorage::MakeRegPair(result, coreArgMappingToPhysicalReg[cur_core_reg_++]);
- // }
+ if (arg.IsWide() && cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
+ result = RegStorage::MakeRegPair(result, coreArgMappingToPhysicalReg[cur_core_reg_++]);
+ }
}
}
return result;
}
-RegStorage ArmMir2Lir::InToRegStorageMapping::Get(int in_position) const {
- DCHECK(IsInitialized());
- auto res = mapping_.find(in_position);
- return res != mapping_.end() ? res->second : RegStorage::InvalidReg();
-}
-
-void ArmMir2Lir::InToRegStorageMapping::Initialize(RegLocation* arg_locs, int count,
- InToRegStorageMapper* mapper) {
- DCHECK(mapper != nullptr);
- max_mapped_in_ = -1;
- is_there_stack_mapped_ = false;
- for (int in_position = 0; in_position < count; in_position++) {
- RegStorage reg = mapper->GetNextReg(arg_locs[in_position].fp,
- arg_locs[in_position].wide);
- if (reg.Valid()) {
- mapping_[in_position] = reg;
- // TODO: Enable the following code when FlushIns() support 64-bit argument registers.
- // if (arg_locs[in_position].wide) {
- // if (reg.Is32Bit()) {
- // // As it is a split long, the hi-part is on stack.
- // is_there_stack_mapped_ = true;
- // }
- // // We covered 2 v-registers, so skip the next one
- // in_position++;
- // }
- max_mapped_in_ = std::max(max_mapped_in_, in_position);
- } else {
- is_there_stack_mapped_ = true;
- }
- }
- initialized_ = true;
-}
-
-// TODO: Should be able to return long, double registers.
-// Need check some common code as it will break some assumption.
-RegStorage ArmMir2Lir::GetArgMappingToPhysicalReg(int arg_num) {
- if (!in_to_reg_storage_mapping_.IsInitialized()) {
- int start_vreg = mir_graph_->GetFirstInVR();
- RegLocation* arg_locs = &mir_graph_->reg_location_[start_vreg];
-
- InToRegStorageArmMapper mapper;
- in_to_reg_storage_mapping_.Initialize(arg_locs, mir_graph_->GetNumOfInVRs(), &mapper);
- }
- return in_to_reg_storage_mapping_.Get(arg_num);
-}
-
-int ArmMir2Lir::GenDalvikArgsNoRange(CallInfo* info,
- int call_state, LIR** pcrLabel, NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx, uintptr_t direct_code,
- uintptr_t direct_method, InvokeType type, bool skip_this) {
+int ArmMir2Lir::GenDalvikArgsBulkCopy(CallInfo* info, int first, int count) {
if (kArm32QuickCodeUseSoftFloat) {
- return Mir2Lir::GenDalvikArgsNoRange(info, call_state, pcrLabel, next_call_insn, target_method,
- vtable_idx, direct_code, direct_method, type, skip_this);
- } else {
- return GenDalvikArgsRange(info, call_state, pcrLabel, next_call_insn, target_method, vtable_idx,
- direct_code, direct_method, type, skip_this);
+ return Mir2Lir::GenDalvikArgsBulkCopy(info, first, count);
}
+ /*
+ * TODO: Improve by adding block copy for large number of arguments. For now, just
+ * copy a Dalvik vreg at a time.
+ */
+ return count;
}
-int ArmMir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state,
- LIR** pcrLabel, NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx, uintptr_t direct_code,
- uintptr_t direct_method, InvokeType type, bool skip_this) {
- if (kArm32QuickCodeUseSoftFloat) {
- return Mir2Lir::GenDalvikArgsRange(info, call_state, pcrLabel, next_call_insn, target_method,
- vtable_idx, direct_code, direct_method, type, skip_this);
+void ArmMir2Lir::GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) {
+ UNUSED(bb);
+ DCHECK(MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode));
+ RegLocation rl_src[3];
+ RegLocation rl_dest = mir_graph_->GetBadLoc();
+ rl_src[0] = rl_src[1] = rl_src[2] = mir_graph_->GetBadLoc();
+ switch (static_cast<ExtendedMIROpcode>(mir->dalvikInsn.opcode)) {
+ case kMirOpMaddInt:
+ rl_dest = mir_graph_->GetDest(mir);
+ rl_src[0] = mir_graph_->GetSrc(mir, 0);
+ rl_src[1] = mir_graph_->GetSrc(mir, 1);
+ rl_src[2]= mir_graph_->GetSrc(mir, 2);
+ GenMaddMsubInt(rl_dest, rl_src[0], rl_src[1], rl_src[2], false);
+ break;
+ case kMirOpMsubInt:
+ rl_dest = mir_graph_->GetDest(mir);
+ rl_src[0] = mir_graph_->GetSrc(mir, 0);
+ rl_src[1] = mir_graph_->GetSrc(mir, 1);
+ rl_src[2]= mir_graph_->GetSrc(mir, 2);
+ GenMaddMsubInt(rl_dest, rl_src[0], rl_src[1], rl_src[2], true);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected opcode: " << mir->dalvikInsn.opcode;
}
-
- // TODO: Rework the implementation when argument register can be long or double.
-
- /* If no arguments, just return */
- if (info->num_arg_words == 0) {
- return call_state;
- }
-
- const int start_index = skip_this ? 1 : 0;
-
- InToRegStorageArmMapper mapper;
- InToRegStorageMapping in_to_reg_storage_mapping;
- in_to_reg_storage_mapping.Initialize(info->args, info->num_arg_words, &mapper);
- const int last_mapped_in = in_to_reg_storage_mapping.GetMaxMappedIn();
- int regs_left_to_pass_via_stack = info->num_arg_words - (last_mapped_in + 1);
-
- // First of all, check whether it makes sense to use bulk copying.
- // Bulk copying is done only for the range case.
- // TODO: make a constant instead of 2
- if (info->is_range && regs_left_to_pass_via_stack >= 2) {
- // Scan the rest of the args - if in phys_reg flush to memory
- for (int next_arg = last_mapped_in + 1; next_arg < info->num_arg_words;) {
- RegLocation loc = info->args[next_arg];
- if (loc.wide) {
- // TODO: Only flush hi-part.
- if (loc.high_word) {
- loc = info->args[--next_arg];
- }
- loc = UpdateLocWide(loc);
- if (loc.location == kLocPhysReg) {
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k64, kNotVolatile);
- }
- next_arg += 2;
- } else {
- loc = UpdateLoc(loc);
- if (loc.location == kLocPhysReg) {
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- if (loc.ref) {
- StoreRefDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, kNotVolatile);
- } else {
- StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k32,
- kNotVolatile);
- }
- }
- next_arg++;
- }
- }
-
- // The rest can be copied together
- int start_offset = SRegOffset(info->args[last_mapped_in + 1].s_reg_low);
- int outs_offset = StackVisitor::GetOutVROffset(last_mapped_in + 1,
- cu_->instruction_set);
-
- int current_src_offset = start_offset;
- int current_dest_offset = outs_offset;
-
- // Only davik regs are accessed in this loop; no next_call_insn() calls.
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- while (regs_left_to_pass_via_stack > 0) {
- /*
- * TODO: Improve by adding block copy for large number of arguments. This
- * should be done, if possible, as a target-depending helper. For now, just
- * copy a Dalvik vreg at a time.
- */
- // Moving 32-bits via general purpose register.
- size_t bytes_to_move = sizeof(uint32_t);
-
- // Instead of allocating a new temp, simply reuse one of the registers being used
- // for argument passing.
- RegStorage temp = TargetReg(kArg3, kNotWide);
-
- // Now load the argument VR and store to the outs.
- Load32Disp(TargetPtrReg(kSp), current_src_offset, temp);
- Store32Disp(TargetPtrReg(kSp), current_dest_offset, temp);
-
- current_src_offset += bytes_to_move;
- current_dest_offset += bytes_to_move;
- regs_left_to_pass_via_stack -= (bytes_to_move >> 2);
- }
- DCHECK_EQ(regs_left_to_pass_via_stack, 0);
- }
-
- // Now handle rest not registers if they are
- if (in_to_reg_storage_mapping.IsThereStackMapped()) {
- RegStorage regWide = TargetReg(kArg2, kWide);
- for (int i = start_index; i <= last_mapped_in + regs_left_to_pass_via_stack; i++) {
- RegLocation rl_arg = info->args[i];
- rl_arg = UpdateRawLoc(rl_arg);
- RegStorage reg = in_to_reg_storage_mapping.Get(i);
- // TODO: Only pass split wide hi-part via stack.
- if (!reg.Valid() || rl_arg.wide) {
- int out_offset = StackVisitor::GetOutVROffset(i, cu_->instruction_set);
-
- {
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- if (rl_arg.wide) {
- if (rl_arg.location == kLocPhysReg) {
- StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k64, kNotVolatile);
- } else {
- LoadValueDirectWideFixed(rl_arg, regWide);
- StoreBaseDisp(TargetPtrReg(kSp), out_offset, regWide, k64, kNotVolatile);
- }
- } else {
- if (rl_arg.location == kLocPhysReg) {
- if (rl_arg.ref) {
- StoreRefDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, kNotVolatile);
- } else {
- StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k32, kNotVolatile);
- }
- } else {
- if (rl_arg.ref) {
- RegStorage regSingle = TargetReg(kArg2, kRef);
- LoadValueDirectFixed(rl_arg, regSingle);
- StoreRefDisp(TargetPtrReg(kSp), out_offset, regSingle, kNotVolatile);
- } else {
- RegStorage regSingle = TargetReg(kArg2, kNotWide);
- LoadValueDirectFixed(rl_arg, regSingle);
- StoreBaseDisp(TargetPtrReg(kSp), out_offset, regSingle, k32, kNotVolatile);
- }
- }
- }
- }
-
- call_state = next_call_insn(cu_, info, call_state, target_method,
- vtable_idx, direct_code, direct_method, type);
- }
- if (rl_arg.wide) {
- i++;
- }
- }
- }
-
- // Finish with mapped registers
- for (int i = start_index; i <= last_mapped_in; i++) {
- RegLocation rl_arg = info->args[i];
- rl_arg = UpdateRawLoc(rl_arg);
- RegStorage reg = in_to_reg_storage_mapping.Get(i);
- if (reg.Valid()) {
- if (reg.Is64Bit()) {
- LoadValueDirectWideFixed(rl_arg, reg);
- } else {
- // TODO: Only split long should be the case we need to care about.
- if (rl_arg.wide) {
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- int high_word = rl_arg.high_word ? 1 : 0;
- rl_arg = high_word ? info->args[i - 1] : rl_arg;
- if (rl_arg.location == kLocPhysReg) {
- RegStorage rs_arg = rl_arg.reg;
- if (rs_arg.IsDouble() && rs_arg.Is64BitSolo()) {
- rs_arg = As64BitFloatRegPair(rs_arg);
- }
- RegStorage rs_arg_low = rs_arg.GetLow();
- RegStorage rs_arg_high = rs_arg.GetHigh();
- OpRegCopy(reg, high_word ? rs_arg_high : rs_arg_low);
- } else {
- Load32Disp(TargetPtrReg(kSp), SRegOffset(rl_arg.s_reg_low + high_word), reg);
- }
- } else {
- LoadValueDirectFixed(rl_arg, reg);
- }
- }
- call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
- direct_code, direct_method, type);
- }
- if (reg.Is64Bit()) {
- i++;
- }
- }
-
- call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
- direct_code, direct_method, type);
- if (pcrLabel) {
- if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) {
- *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags);
- } else {
- *pcrLabel = nullptr;
- // In lieu of generating a check for kArg1 being null, we need to
- // perform a load when doing implicit checks.
- RegStorage tmp = AllocTemp();
- Load32Disp(TargetReg(kArg1, kRef), 0, tmp);
- MarkPossibleNullPointerException(info->opt_flags);
- FreeTemp(tmp);
- }
- }
- return call_state;
}
} // namespace art
diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc
index 117d8f0..e4bd2a3 100644
--- a/compiler/dex/quick/arm/utility_arm.cc
+++ b/compiler/dex/quick/arm/utility_arm.cc
@@ -18,8 +18,10 @@
#include "arch/arm/instruction_set_features_arm.h"
#include "arm_lir.h"
+#include "base/logging.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "dex/reg_storage_eq.h"
+#include "driver/compiler_driver.h"
namespace art {
@@ -156,6 +158,13 @@
case Instruction::USHR_INT:
case Instruction::USHR_INT_2ADDR:
return true;
+ case Instruction::CONST:
+ case Instruction::CONST_4:
+ case Instruction::CONST_16:
+ if ((value >> 16) == 0) {
+ return true; // movw, 16-bit unsigned.
+ }
+ FALLTHROUGH_INTENDED;
case Instruction::AND_INT:
case Instruction::AND_INT_2ADDR:
case Instruction::AND_INT_LIT16:
@@ -899,12 +908,12 @@
*/
LIR* ArmMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStorage r_dest,
OpSize size) {
- LIR* load = NULL;
- ArmOpcode opcode = kThumbBkpt;
+ LIR* load = nullptr;
+ ArmOpcode opcode16 = kThumbBkpt; // 16-bit Thumb opcode.
+ ArmOpcode opcode32 = kThumbBkpt; // 32-bit Thumb2 opcode.
bool short_form = false;
- bool thumb2Form = (displacement < 4092 && displacement >= 0);
bool all_low = r_dest.Is32Bit() && r_base.Low8() && r_dest.Low8();
- int encoded_disp = displacement;
+ int scale = 0; // Used for opcode16 and some indexed loads.
bool already_generated = false;
switch (size) {
case kDouble:
@@ -932,57 +941,45 @@
already_generated = true;
break;
}
+ DCHECK_EQ((displacement & 0x3), 0);
+ scale = 2;
if (r_dest.Low8() && (r_base == rs_rARM_PC) && (displacement <= 1020) &&
(displacement >= 0)) {
short_form = true;
- encoded_disp >>= 2;
- opcode = kThumbLdrPcRel;
+ opcode16 = kThumbLdrPcRel;
} else if (r_dest.Low8() && (r_base == rs_rARM_SP) && (displacement <= 1020) &&
(displacement >= 0)) {
short_form = true;
- encoded_disp >>= 2;
- opcode = kThumbLdrSpRel;
- } else if (all_low && displacement < 128 && displacement >= 0) {
- DCHECK_EQ((displacement & 0x3), 0);
- short_form = true;
- encoded_disp >>= 2;
- opcode = kThumbLdrRRI5;
- } else if (thumb2Form) {
- short_form = true;
- opcode = kThumb2LdrRRI12;
+ opcode16 = kThumbLdrSpRel;
+ } else {
+ short_form = all_low && (displacement >> (5 + scale)) == 0;
+ opcode16 = kThumbLdrRRI5;
+ opcode32 = kThumb2LdrRRI12;
}
break;
case kUnsignedHalf:
- if (all_low && displacement < 64 && displacement >= 0) {
- DCHECK_EQ((displacement & 0x1), 0);
- short_form = true;
- encoded_disp >>= 1;
- opcode = kThumbLdrhRRI5;
- } else if (displacement < 4092 && displacement >= 0) {
- short_form = true;
- opcode = kThumb2LdrhRRI12;
- }
+ DCHECK_EQ((displacement & 0x1), 0);
+ scale = 1;
+ short_form = all_low && (displacement >> (5 + scale)) == 0;
+ opcode16 = kThumbLdrhRRI5;
+ opcode32 = kThumb2LdrhRRI12;
break;
case kSignedHalf:
- if (thumb2Form) {
- short_form = true;
- opcode = kThumb2LdrshRRI12;
- }
+ DCHECK_EQ((displacement & 0x1), 0);
+ scale = 1;
+ DCHECK_EQ(opcode16, kThumbBkpt); // Not available.
+ opcode32 = kThumb2LdrshRRI12;
break;
case kUnsignedByte:
- if (all_low && displacement < 32 && displacement >= 0) {
- short_form = true;
- opcode = kThumbLdrbRRI5;
- } else if (thumb2Form) {
- short_form = true;
- opcode = kThumb2LdrbRRI12;
- }
+ DCHECK_EQ(scale, 0); // Keep scale = 0.
+ short_form = all_low && (displacement >> (5 + scale)) == 0;
+ opcode16 = kThumbLdrbRRI5;
+ opcode32 = kThumb2LdrbRRI12;
break;
case kSignedByte:
- if (thumb2Form) {
- short_form = true;
- opcode = kThumb2LdrsbRRI12;
- }
+ DCHECK_EQ(scale, 0); // Keep scale = 0.
+ DCHECK_EQ(opcode16, kThumbBkpt); // Not available.
+ opcode32 = kThumb2LdrsbRRI12;
break;
default:
LOG(FATAL) << "Bad size: " << size;
@@ -990,12 +987,33 @@
if (!already_generated) {
if (short_form) {
- load = NewLIR3(opcode, r_dest.GetReg(), r_base.GetReg(), encoded_disp);
+ load = NewLIR3(opcode16, r_dest.GetReg(), r_base.GetReg(), displacement >> scale);
+ } else if ((displacement >> 12) == 0) { // Thumb2 form.
+ load = NewLIR3(opcode32, r_dest.GetReg(), r_base.GetReg(), displacement);
+ } else if (!InexpensiveConstantInt(displacement >> scale, Instruction::CONST) &&
+ InexpensiveConstantInt(displacement & ~0x00000fff, Instruction::ADD_INT)) {
+ // In this case, using LoadIndexed would emit 3 insns (movw+movt+ldr) but we can
+ // actually do it in two because we know that the kOpAdd is a single insn. On the
+ // other hand, we introduce an extra dependency, so this is not necessarily faster.
+ if (opcode16 != kThumbBkpt && r_dest.Low8() &&
+ InexpensiveConstantInt(displacement & ~(0x1f << scale), Instruction::ADD_INT)) {
+ // We can use the 16-bit Thumb opcode for the load.
+ OpRegRegImm(kOpAdd, r_dest, r_base, displacement & ~(0x1f << scale));
+ load = NewLIR3(opcode16, r_dest.GetReg(), r_dest.GetReg(), (displacement >> scale) & 0x1f);
+ } else {
+ DCHECK_NE(opcode32, kThumbBkpt);
+ OpRegRegImm(kOpAdd, r_dest, r_base, displacement & ~0x00000fff);
+ load = NewLIR3(opcode32, r_dest.GetReg(), r_dest.GetReg(), displacement & 0x00000fff);
+ }
} else {
+ if (!InexpensiveConstantInt(displacement >> scale, Instruction::CONST) ||
+ (scale != 0 && InexpensiveConstantInt(displacement, Instruction::CONST))) {
+ scale = 0; // Prefer unscaled indexing if the same number of insns.
+ }
RegStorage reg_offset = AllocTemp();
- LoadConstant(reg_offset, encoded_disp);
+ LoadConstant(reg_offset, displacement >> scale);
DCHECK(!r_dest.IsFloat());
- load = LoadBaseIndexed(r_base, reg_offset, r_dest, 0, size);
+ load = LoadBaseIndexed(r_base, reg_offset, r_dest, scale, size);
FreeTemp(reg_offset);
}
}
@@ -1024,9 +1042,8 @@
// Use LDREXD for the atomic load. (Expect displacement > 0, don't optimize for == 0.)
RegStorage r_ptr = AllocTemp();
OpRegRegImm(kOpAdd, r_ptr, r_base, displacement);
- LIR* lir = NewLIR3(kThumb2Ldrexd, r_dest.GetLowReg(), r_dest.GetHighReg(), r_ptr.GetReg());
+ load = NewLIR3(kThumb2Ldrexd, r_dest.GetLowReg(), r_dest.GetHighReg(), r_ptr.GetReg());
FreeTemp(r_ptr);
- return lir;
} else {
load = LoadBaseDispBody(r_base, displacement, r_dest, size);
}
@@ -1041,12 +1058,12 @@
LIR* ArmMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStorage r_src,
OpSize size) {
- LIR* store = NULL;
- ArmOpcode opcode = kThumbBkpt;
+ LIR* store = nullptr;
+ ArmOpcode opcode16 = kThumbBkpt; // 16-bit Thumb opcode.
+ ArmOpcode opcode32 = kThumbBkpt; // 32-bit Thumb2 opcode.
bool short_form = false;
- bool thumb2Form = (displacement < 4092 && displacement >= 0);
bool all_low = r_src.Is32Bit() && r_base.Low8() && r_src.Low8();
- int encoded_disp = displacement;
+ int scale = 0; // Used for opcode16 and some indexed loads.
bool already_generated = false;
switch (size) {
case kDouble:
@@ -1078,53 +1095,67 @@
already_generated = true;
break;
}
+ DCHECK_EQ((displacement & 0x3), 0);
+ scale = 2;
if (r_src.Low8() && (r_base == rs_r13sp) && (displacement <= 1020) && (displacement >= 0)) {
short_form = true;
- encoded_disp >>= 2;
- opcode = kThumbStrSpRel;
- } else if (all_low && displacement < 128 && displacement >= 0) {
- DCHECK_EQ((displacement & 0x3), 0);
- short_form = true;
- encoded_disp >>= 2;
- opcode = kThumbStrRRI5;
- } else if (thumb2Form) {
- short_form = true;
- opcode = kThumb2StrRRI12;
+ opcode16 = kThumbStrSpRel;
+ } else {
+ short_form = all_low && (displacement >> (5 + scale)) == 0;
+ opcode16 = kThumbStrRRI5;
+ opcode32 = kThumb2StrRRI12;
}
break;
case kUnsignedHalf:
case kSignedHalf:
- if (all_low && displacement < 64 && displacement >= 0) {
- DCHECK_EQ((displacement & 0x1), 0);
- short_form = true;
- encoded_disp >>= 1;
- opcode = kThumbStrhRRI5;
- } else if (thumb2Form) {
- short_form = true;
- opcode = kThumb2StrhRRI12;
- }
+ DCHECK_EQ((displacement & 0x1), 0);
+ scale = 1;
+ short_form = all_low && (displacement >> (5 + scale)) == 0;
+ opcode16 = kThumbStrhRRI5;
+ opcode32 = kThumb2StrhRRI12;
break;
case kUnsignedByte:
case kSignedByte:
- if (all_low && displacement < 32 && displacement >= 0) {
- short_form = true;
- opcode = kThumbStrbRRI5;
- } else if (thumb2Form) {
- short_form = true;
- opcode = kThumb2StrbRRI12;
- }
+ DCHECK_EQ(scale, 0); // Keep scale = 0.
+ short_form = all_low && (displacement >> (5 + scale)) == 0;
+ opcode16 = kThumbStrbRRI5;
+ opcode32 = kThumb2StrbRRI12;
break;
default:
LOG(FATAL) << "Bad size: " << size;
}
if (!already_generated) {
if (short_form) {
- store = NewLIR3(opcode, r_src.GetReg(), r_base.GetReg(), encoded_disp);
- } else {
+ store = NewLIR3(opcode16, r_src.GetReg(), r_base.GetReg(), displacement >> scale);
+ } else if ((displacement >> 12) == 0) {
+ store = NewLIR3(opcode32, r_src.GetReg(), r_base.GetReg(), displacement);
+ } else if (!InexpensiveConstantInt(displacement >> scale, Instruction::CONST) &&
+ InexpensiveConstantInt(displacement & ~0x00000fff, Instruction::ADD_INT)) {
+ // In this case, using StoreIndexed would emit 3 insns (movw+movt+str) but we can
+ // actually do it in two because we know that the kOpAdd is a single insn. On the
+ // other hand, we introduce an extra dependency, so this is not necessarily faster.
RegStorage r_scratch = AllocTemp();
- LoadConstant(r_scratch, encoded_disp);
+ if (opcode16 != kThumbBkpt && r_src.Low8() && r_scratch.Low8() &&
+ InexpensiveConstantInt(displacement & ~(0x1f << scale), Instruction::ADD_INT)) {
+ // We can use the 16-bit Thumb opcode for the load.
+ OpRegRegImm(kOpAdd, r_scratch, r_base, displacement & ~(0x1f << scale));
+ store = NewLIR3(opcode16, r_src.GetReg(), r_scratch.GetReg(),
+ (displacement >> scale) & 0x1f);
+ } else {
+ DCHECK_NE(opcode32, kThumbBkpt);
+ OpRegRegImm(kOpAdd, r_scratch, r_base, displacement & ~0x00000fff);
+ store = NewLIR3(opcode32, r_src.GetReg(), r_scratch.GetReg(), displacement & 0x00000fff);
+ }
+ FreeTemp(r_scratch);
+ } else {
+ if (!InexpensiveConstantInt(displacement >> scale, Instruction::CONST) ||
+ (scale != 0 && InexpensiveConstantInt(displacement, Instruction::CONST))) {
+ scale = 0; // Prefer unscaled indexing if the same number of insns.
+ }
+ RegStorage r_scratch = AllocTemp();
+ LoadConstant(r_scratch, displacement >> scale);
DCHECK(!r_src.IsFloat());
- store = StoreBaseIndexed(r_base, r_scratch, r_src, 0, size);
+ store = StoreBaseIndexed(r_base, r_scratch, r_src, scale, size);
FreeTemp(r_scratch);
}
}
@@ -1144,7 +1175,7 @@
GenMemBarrier(kAnyStore);
}
- LIR* store;
+ LIR* null_ck_insn;
if (is_volatile == kVolatile && (size == k64 || size == kDouble) &&
!cu_->compiler_driver->GetInstructionSetFeatures()->
AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd()) {
@@ -1161,17 +1192,16 @@
RegStorage r_temp = AllocTemp();
RegStorage r_temp_high = AllocTemp(false); // We may not have another temp.
if (r_temp_high.Valid()) {
- NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_temp_high.GetReg(), r_ptr.GetReg());
+ null_ck_insn = NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_temp_high.GetReg(), r_ptr.GetReg());
FreeTemp(r_temp_high);
FreeTemp(r_temp);
} else {
// If we don't have another temp, clobber r_ptr in LDREXD and reload it.
- NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_ptr.GetReg(), r_ptr.GetReg());
+ null_ck_insn = NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_ptr.GetReg(), r_ptr.GetReg());
FreeTemp(r_temp); // May need the temp for kOpAdd.
OpRegRegImm(kOpAdd, r_ptr, r_base, displacement);
}
- store = NewLIR4(kThumb2Strexd, r_temp.GetReg(), r_src.GetLowReg(), r_src.GetHighReg(),
- r_ptr.GetReg());
+ NewLIR4(kThumb2Strexd, r_temp.GetReg(), r_src.GetLowReg(), r_src.GetHighReg(), r_ptr.GetReg());
OpCmpImmBranch(kCondNe, r_temp, 0, fail_target);
FreeTemp(r_ptr);
} else {
@@ -1180,7 +1210,7 @@
size = k32;
}
- store = StoreBaseDispBody(r_base, displacement, r_src, size);
+ null_ck_insn = StoreBaseDispBody(r_base, displacement, r_src, size);
}
if (UNLIKELY(is_volatile == kVolatile)) {
@@ -1189,7 +1219,7 @@
GenMemBarrier(kAnyAny);
}
- return store;
+ return null_ck_insn;
}
LIR* ArmMir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) {
diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h
index f8a7310..d15412a 100644
--- a/compiler/dex/quick/arm64/arm64_lir.h
+++ b/compiler/dex/quick/arm64/arm64_lir.h
@@ -17,7 +17,9 @@
#ifndef ART_COMPILER_DEX_QUICK_ARM64_ARM64_LIR_H_
#define ART_COMPILER_DEX_QUICK_ARM64_ARM64_LIR_H_
-#include "dex/compiler_internals.h"
+#include "dex/compiler_enums.h"
+#include "dex/reg_location.h"
+#include "dex/reg_storage.h"
namespace art {
@@ -312,6 +314,7 @@
kA64Lsl3rrr, // lsl [s0011010110] rm[20-16] [001000] rn[9-5] rd[4-0].
kA64Lsr3rrd, // lsr alias of "ubfm arg0, arg1, arg2, #{31/63}".
kA64Lsr3rrr, // lsr [s0011010110] rm[20-16] [001001] rn[9-5] rd[4-0].
+ kA64Madd4rrrr, // madd[s0011011000] rm[20-16] [0] ra[14-10] rn[9-5] rd[4-0].
kA64Movk3rdM, // mov [010100101] hw[22-21] imm_16[20-5] rd[4-0].
kA64Movn3rdM, // mov [000100101] hw[22-21] imm_16[20-5] rd[4-0].
kA64Movz3rdM, // mov [011100101] hw[22-21] imm_16[20-5] rd[4-0].
diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc
index cab11cc..806617b 100644
--- a/compiler/dex/quick/arm64/assemble_arm64.cc
+++ b/compiler/dex/quick/arm64/assemble_arm64.cc
@@ -18,7 +18,10 @@
#include "arch/arm64/instruction_set_features_arm64.h"
#include "arm64_lir.h"
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
#include "dex/quick/mir_to_lir-inl.h"
+#include "driver/compiler_driver.h"
namespace art {
@@ -445,6 +448,10 @@
kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
"lsr", "!0r, !1r, !2r", kFixupNone),
+ ENCODING_MAP(WIDE(kA64Madd4rrrr), SF_VARIANTS(0x1b000000),
+ kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16,
+ kFmtRegR, 14, 10, IS_QUAD_OP | REG_DEF0_USE123 | NEEDS_FIXUP,
+ "madd", "!0r, !1r, !2r, !3r", kFixupA53Erratum835769),
ENCODING_MAP(WIDE(kA64Movk3rdM), SF_VARIANTS(0x72800000),
kFmtRegR, 4, 0, kFmtBitBlt, 20, 5, kFmtBitBlt, 22, 21,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE0,
@@ -840,6 +847,20 @@
// are better set directly from the code (they will require no more than 2 instructions).
#define ALIGNED_DATA_OFFSET(offset) (((offset) + 0x7) & ~0x7)
+/*
+ * Get the LIR which emits the instruction preceding the given LIR.
+ * Returns nullptr, if no previous emitting insn found.
+ */
+static LIR* GetPrevEmittingLIR(LIR* lir) {
+ DCHECK(lir != nullptr);
+ LIR* prev_lir = lir->prev;
+ while ((prev_lir != nullptr) &&
+ (prev_lir->flags.is_nop || Mir2Lir::IsPseudoLirOp(prev_lir->opcode))) {
+ prev_lir = prev_lir->prev;
+ }
+ return prev_lir;
+}
+
// Assemble the LIR into binary instruction format.
void Arm64Mir2Lir::AssembleLIR() {
LIR* lir;
@@ -998,11 +1019,15 @@
// Avoid emitting code that could trigger Cortex A53's erratum 835769.
// This fixup should be carried out for all multiply-accumulate instructions: madd, msub,
// smaddl, smsubl, umaddl and umsubl.
- if (cu_->GetInstructionSetFeatures()->AsArm64InstructionSetFeatures()
+ if (cu_->compiler_driver->GetInstructionSetFeatures()->AsArm64InstructionSetFeatures()
->NeedFixCortexA53_835769()) {
// Check that this is a 64-bit multiply-accumulate.
if (IS_WIDE(lir->opcode)) {
- uint64_t prev_insn_flags = EncodingMap[UNWIDE(lir->prev->opcode)].flags;
+ LIR* prev_insn = GetPrevEmittingLIR(lir);
+ if (prev_insn == nullptr) {
+ break;
+ }
+ uint64_t prev_insn_flags = EncodingMap[UNWIDE(prev_insn->opcode)].flags;
// Check that the instruction preceding the multiply-accumulate is a load or store.
if ((prev_insn_flags & IS_LOAD) != 0 || (prev_insn_flags & IS_STORE) != 0) {
// insert a NOP between the load/store and the multiply-accumulate.
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index 089e4b6..d1e4b7e 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -16,9 +16,13 @@
/* This file contains codegen for the Thumb2 ISA. */
-#include "arm64_lir.h"
#include "codegen_arm64.h"
+
+#include "arm64_lir.h"
+#include "base/logging.h"
+#include "dex/mir_graph.h"
#include "dex/quick/mir_to_lir-inl.h"
+#include "driver/compiler_driver.h"
#include "gc/accounting/card_table.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "mirror/art_method.h"
@@ -47,16 +51,13 @@
*/
void Arm64Mir2Lir::GenLargeSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
- if (cu_->verbose) {
- DumpSparseSwitchTable(table);
- }
// Add the table to the list - we'll process it later
SwitchTable *tab_rec =
static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
+ tab_rec->switch_mir = mir;
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
uint32_t size = table[1];
- tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), kArenaAllocLIR));
switch_tables_.push_back(tab_rec);
// Get the switch value
@@ -99,17 +100,13 @@
void Arm64Mir2Lir::GenLargePackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
- if (cu_->verbose) {
- DumpPackedSwitchTable(table);
- }
// Add the table to the list - we'll process it later
SwitchTable *tab_rec =
static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
+ tab_rec->switch_mir = mir;
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
uint32_t size = table[1];
- tab_rec->targets =
- static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), kArenaAllocLIR));
switch_tables_.push_back(tab_rec);
// Get the switch value
@@ -396,9 +393,8 @@
}
static bool Arm64UseRelativeCall(CompilationUnit* cu, const MethodReference& target_method) {
- UNUSED(cu, target_method);
- // Always emit relative calls.
- return true;
+ // Emit relative calls anywhere in the image or within a dex file otherwise.
+ return cu->compiler_driver->IsImage() || cu->dex_file == target_method.dex_file;
}
/*
diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h
index 5e10f80..49ca625 100644
--- a/compiler/dex/quick/arm64/codegen_arm64.h
+++ b/compiler/dex/quick/arm64/codegen_arm64.h
@@ -18,7 +18,7 @@
#define ART_COMPILER_DEX_QUICK_ARM64_CODEGEN_ARM64_H_
#include "arm64_lir.h"
-#include "dex/compiler_internals.h"
+#include "base/logging.h"
#include "dex/quick/mir_to_lir.h"
#include <map>
@@ -27,38 +27,25 @@
class Arm64Mir2Lir FINAL : public Mir2Lir {
protected:
- // TODO: consolidate 64-bit target support.
- class InToRegStorageMapper {
- public:
- virtual RegStorage GetNextReg(bool is_double_or_float, bool is_wide, bool is_ref) = 0;
- virtual ~InToRegStorageMapper() {}
- };
-
class InToRegStorageArm64Mapper : public InToRegStorageMapper {
public:
InToRegStorageArm64Mapper() : cur_core_reg_(0), cur_fp_reg_(0) {}
virtual ~InToRegStorageArm64Mapper() {}
- virtual RegStorage GetNextReg(bool is_double_or_float, bool is_wide, bool is_ref);
+ virtual RegStorage GetNextReg(ShortyArg arg);
+ virtual void Reset() OVERRIDE {
+ cur_core_reg_ = 0;
+ cur_fp_reg_ = 0;
+ }
private:
- int cur_core_reg_;
- int cur_fp_reg_;
+ size_t cur_core_reg_;
+ size_t cur_fp_reg_;
};
- class InToRegStorageMapping {
- public:
- InToRegStorageMapping() : max_mapped_in_(0), is_there_stack_mapped_(false),
- initialized_(false) {}
- void Initialize(RegLocation* arg_locs, int count, InToRegStorageMapper* mapper);
- int GetMaxMappedIn() { return max_mapped_in_; }
- bool IsThereStackMapped() { return is_there_stack_mapped_; }
- RegStorage Get(int in_position);
- bool IsInitialized() { return initialized_; }
- private:
- std::map<int, RegStorage> mapping_;
- int max_mapped_in_;
- bool is_there_stack_mapped_;
- bool initialized_;
- };
+ InToRegStorageArm64Mapper in_to_reg_storage_arm64_mapper_;
+ InToRegStorageMapper* GetResetedInToRegStorageMapper() OVERRIDE {
+ in_to_reg_storage_arm64_mapper_.Reset();
+ return &in_to_reg_storage_arm64_mapper_;
+ }
public:
Arm64Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
@@ -79,21 +66,14 @@
RegStorage LoadHelper(QuickEntrypointEnum trampoline) OVERRIDE;
LIR* LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest,
OpSize size, VolatileKind is_volatile) OVERRIDE;
- LIR* LoadRefDisp(RegStorage r_base, int displacement, RegStorage r_dest,
- VolatileKind is_volatile) OVERRIDE;
LIR* LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest, int scale,
OpSize size) OVERRIDE;
- LIR* LoadRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest, int scale)
- OVERRIDE;
LIR* LoadConstantNoClobber(RegStorage r_dest, int value) OVERRIDE;
LIR* LoadConstantWide(RegStorage r_dest, int64_t value) OVERRIDE;
LIR* StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, OpSize size,
VolatileKind is_volatile) OVERRIDE;
- LIR* StoreRefDisp(RegStorage r_base, int displacement, RegStorage r_src, VolatileKind is_volatile)
- OVERRIDE;
LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale,
OpSize size) OVERRIDE;
- LIR* StoreRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale) OVERRIDE;
/// @copydoc Mir2Lir::UnconditionallyMarkGCCard(RegStorage)
void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) OVERRIDE;
@@ -113,7 +93,6 @@
RegStorage TargetPtrReg(SpecialTargetRegister symbolic_reg) OVERRIDE {
return As64BitReg(TargetReg(symbolic_reg));
}
- RegStorage GetArgMappingToPhysicalReg(int arg_num) OVERRIDE;
RegLocation GetReturnAlt() OVERRIDE;
RegLocation GetReturnWideAlt() OVERRIDE;
RegLocation LocCReturn() OVERRIDE;
@@ -207,6 +186,10 @@
void GenNegFloat(RegLocation rl_dest, RegLocation rl_src) OVERRIDE;
void GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) OVERRIDE;
void GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) OVERRIDE;
+ void GenMaddMsubInt(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
+ RegLocation rl_src3, bool is_sub);
+ void GenMaddMsubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
+ RegLocation rl_src3, bool is_sub);
// Required for target - single operation generators.
LIR* OpUnconditionalBranch(LIR* target) OVERRIDE;
@@ -240,21 +223,7 @@
bool InexpensiveConstantLong(int64_t value) OVERRIDE;
bool InexpensiveConstantDouble(int64_t value) OVERRIDE;
- void FlushIns(RegLocation* ArgLocs, RegLocation rl_method) OVERRIDE;
-
- int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel,
- NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx,
- uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
- bool skip_this) OVERRIDE;
-
- int GenDalvikArgsRange(CallInfo* info, int call_state, LIR** pcrLabel,
- NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx,
- uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
- bool skip_this) OVERRIDE;
+ void GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) OVERRIDE;
bool WideGPRsAreAliases() const OVERRIDE {
return true; // 64b architecture.
@@ -422,10 +391,11 @@
void GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_src2, bool is_div, int flags);
- InToRegStorageMapping in_to_reg_storage_mapping_;
static const A64EncodingMap EncodingMap[kA64Last];
ArenaVector<LIR*> call_method_insns_;
+
+ int GenDalvikArgsBulkCopy(CallInfo* info, int first, int count) OVERRIDE;
};
} // namespace art
diff --git a/compiler/dex/quick/arm64/fp_arm64.cc b/compiler/dex/quick/arm64/fp_arm64.cc
index ff692b7..a8ec6c0 100644
--- a/compiler/dex/quick/arm64/fp_arm64.cc
+++ b/compiler/dex/quick/arm64/fp_arm64.cc
@@ -14,8 +14,11 @@
* limitations under the License.
*/
-#include "arm64_lir.h"
#include "codegen_arm64.h"
+
+#include "arm64_lir.h"
+#include "base/logging.h"
+#include "dex/mir_graph.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "utils.h"
diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc
index 57e67d5..92675f3 100644
--- a/compiler/dex/quick/arm64/int_arm64.cc
+++ b/compiler/dex/quick/arm64/int_arm64.cc
@@ -16,11 +16,16 @@
/* This file contains codegen for the Thumb2 ISA. */
+#include "codegen_arm64.h"
+
#include "arch/instruction_set_features.h"
#include "arm64_lir.h"
-#include "codegen_arm64.h"
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
+#include "dex/mir_graph.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "dex/reg_storage_eq.h"
+#include "driver/compiler_driver.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "mirror/array-inl.h"
#include "utils.h"
@@ -543,7 +548,7 @@
return SmallLiteralDivRem(dalvik_opcode, is_div, rl_src, rl_dest, static_cast<int32_t>(lit));
}
}
- int k = LowestSetBit(lit);
+ int k = CTZ(lit);
if (k >= nbits - 2) {
// Avoid special cases.
return false;
@@ -949,10 +954,33 @@
UNREACHABLE();
}
+void Arm64Mir2Lir::GenMaddMsubInt(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
+ RegLocation rl_src3, bool is_sub) {
+ rl_src1 = LoadValue(rl_src1, kCoreReg);
+ rl_src2 = LoadValue(rl_src2, kCoreReg);
+ rl_src3 = LoadValue(rl_src3, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ NewLIR4(is_sub ? kA64Msub4rrrr : kA64Madd4rrrr, rl_result.reg.GetReg(), rl_src1.reg.GetReg(),
+ rl_src2.reg.GetReg(), rl_src3.reg.GetReg());
+ StoreValue(rl_dest, rl_result);
+}
+
+void Arm64Mir2Lir::GenMaddMsubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
+ RegLocation rl_src3, bool is_sub) {
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ rl_src2 = LoadValueWide(rl_src2, kCoreReg);
+ rl_src3 = LoadValueWide(rl_src3, kCoreReg);
+ RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
+ NewLIR4(is_sub ? WIDE(kA64Msub4rrrr) : WIDE(kA64Madd4rrrr), rl_result.reg.GetReg(),
+ rl_src1.reg.GetReg(), rl_src2.reg.GetReg(), rl_src3.reg.GetReg());
+ StoreValueWide(rl_dest, rl_result);
+}
+
void Arm64Mir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
RegLocation rl_result, int lit ATTRIBUTE_UNUSED,
int first_bit, int second_bit) {
- OpRegRegRegShift(kOpAdd, rl_result.reg, rl_src.reg, rl_src.reg, EncodeShift(kA64Lsl, second_bit - first_bit));
+ OpRegRegRegShift(kOpAdd, rl_result.reg, rl_src.reg, rl_src.reg,
+ EncodeShift(kA64Lsl, second_bit - first_bit));
if (first_bit != 0) {
OpRegRegImm(kOpLsl, rl_result.reg, rl_result.reg, first_bit);
}
@@ -980,7 +1008,7 @@
}
bool Arm64Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
- if (!cu_->GetInstructionSetFeatures()->IsSmp()) {
+ if (!cu_->compiler_driver->GetInstructionSetFeatures()->IsSmp()) {
return false;
}
// Start off with using the last LIR as the barrier. If it is not enough, then we will generate one.
@@ -1686,7 +1714,8 @@
RegLocation rl_src_i = info->args[0];
RegLocation rl_dest = IsWide(size) ? InlineTargetWide(info) : InlineTarget(info); // result reg
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
- RegLocation rl_i = IsWide(size) ? LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
+ RegLocation rl_i = IsWide(size) ?
+ LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
NewLIR2(kA64Rbit2rr | wide, rl_result.reg.GetReg(), rl_i.reg.GetReg());
IsWide(size) ? StoreValueWide(rl_dest, rl_result) : StoreValue(rl_dest, rl_result);
return true;
diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc
index 094ff51..136be94 100644
--- a/compiler/dex/quick/arm64/target_arm64.cc
+++ b/compiler/dex/quick/arm64/target_arm64.cc
@@ -19,9 +19,11 @@
#include <inttypes.h>
#include <string>
+#include <sstream>
#include "backend_arm64.h"
-#include "dex/compiler_internals.h"
+#include "base/logging.h"
+#include "dex/mir_graph.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "dex/reg_storage_eq.h"
@@ -521,6 +523,24 @@
buf += *fmt++;
}
}
+ // Dump thread offset.
+ std::string fmt_str = GetTargetInstFmt(lir->opcode);
+ if (std::string::npos != fmt_str.find(", [!1X, #!2") && rxSELF == lir->operands[1] &&
+ std::string::npos != buf.find(", [")) {
+ int offset = lir->operands[2];
+ if (std::string::npos != fmt_str.find("#!2d")) {
+ } else if (std::string::npos != fmt_str.find("#!2D")) {
+ offset *= (IS_WIDE(lir->opcode)) ? 8 : 4;
+ } else if (std::string::npos != fmt_str.find("#!2F")) {
+ offset *= 2;
+ } else {
+ LOG(FATAL) << "Should not reach here";
+ }
+ std::ostringstream tmp_stream;
+ Thread::DumpThreadOffset<8>(tmp_stream, offset);
+ buf += " ; ";
+ buf += tmp_stream.str();
+ }
return buf;
}
@@ -759,6 +779,7 @@
FreeTemp(rs_f5);
FreeTemp(rs_f6);
FreeTemp(rs_f7);
+ FreeTemp(TargetReg(kHiddenArg));
}
RegStorage Arm64Mir2Lir::LoadHelper(QuickEntrypointEnum trampoline) {
@@ -790,27 +811,23 @@
return Arm64Mir2Lir::EncodingMap[UNWIDE(opcode)].fmt;
}
-RegStorage Arm64Mir2Lir::InToRegStorageArm64Mapper::GetNextReg(bool is_double_or_float,
- bool is_wide,
- bool is_ref) {
+RegStorage Arm64Mir2Lir::InToRegStorageArm64Mapper::GetNextReg(ShortyArg arg) {
const RegStorage coreArgMappingToPhysicalReg[] =
{rs_x1, rs_x2, rs_x3, rs_x4, rs_x5, rs_x6, rs_x7};
- const int coreArgMappingToPhysicalRegSize =
- sizeof(coreArgMappingToPhysicalReg) / sizeof(RegStorage);
+ const size_t coreArgMappingToPhysicalRegSize = arraysize(coreArgMappingToPhysicalReg);
const RegStorage fpArgMappingToPhysicalReg[] =
{rs_f0, rs_f1, rs_f2, rs_f3, rs_f4, rs_f5, rs_f6, rs_f7};
- const int fpArgMappingToPhysicalRegSize =
- sizeof(fpArgMappingToPhysicalReg) / sizeof(RegStorage);
+ const size_t fpArgMappingToPhysicalRegSize = arraysize(fpArgMappingToPhysicalReg);
RegStorage result = RegStorage::InvalidReg();
- if (is_double_or_float) {
+ if (arg.IsFP()) {
if (cur_fp_reg_ < fpArgMappingToPhysicalRegSize) {
- DCHECK(!is_ref);
+ DCHECK(!arg.IsRef());
result = fpArgMappingToPhysicalReg[cur_fp_reg_++];
if (result.Valid()) {
// TODO: switching between widths remains a bit ugly. Better way?
int res_reg = result.GetReg();
- result = is_wide ? RegStorage::FloatSolo64(res_reg) : RegStorage::FloatSolo32(res_reg);
+ result = arg.IsWide() ? RegStorage::FloatSolo64(res_reg) : RegStorage::FloatSolo32(res_reg);
}
}
} else {
@@ -819,388 +836,15 @@
if (result.Valid()) {
// TODO: switching between widths remains a bit ugly. Better way?
int res_reg = result.GetReg();
- DCHECK(!(is_wide && is_ref));
- result = (is_wide || is_ref) ? RegStorage::Solo64(res_reg) : RegStorage::Solo32(res_reg);
+ DCHECK(!(arg.IsWide() && arg.IsRef()));
+ result = (arg.IsWide() || arg.IsRef()) ?
+ RegStorage::Solo64(res_reg) : RegStorage::Solo32(res_reg);
}
}
}
return result;
}
-RegStorage Arm64Mir2Lir::InToRegStorageMapping::Get(int in_position) {
- DCHECK(IsInitialized());
- auto res = mapping_.find(in_position);
- return res != mapping_.end() ? res->second : RegStorage::InvalidReg();
-}
-
-void Arm64Mir2Lir::InToRegStorageMapping::Initialize(RegLocation* arg_locs, int count,
- InToRegStorageMapper* mapper) {
- DCHECK(mapper != nullptr);
- max_mapped_in_ = -1;
- is_there_stack_mapped_ = false;
- for (int in_position = 0; in_position < count; in_position++) {
- RegStorage reg = mapper->GetNextReg(arg_locs[in_position].fp,
- arg_locs[in_position].wide,
- arg_locs[in_position].ref);
- if (reg.Valid()) {
- mapping_[in_position] = reg;
- if (arg_locs[in_position].wide) {
- // We covered 2 args, so skip the next one
- in_position++;
- }
- max_mapped_in_ = std::max(max_mapped_in_, in_position);
- } else {
- is_there_stack_mapped_ = true;
- }
- }
- initialized_ = true;
-}
-
-
-// Deprecate. Use the new mechanism.
-// TODO(Arm64): reuse info in QuickArgumentVisitor?
-static RegStorage GetArgPhysicalReg(RegLocation* loc, int* num_gpr_used, int* num_fpr_used,
- OpSize* op_size) {
- if (loc->fp) {
- int n = *num_fpr_used;
- if (n < 8) {
- *num_fpr_used = n + 1;
- RegStorage::RegStorageKind reg_kind;
- if (loc->wide) {
- *op_size = kDouble;
- reg_kind = RegStorage::k64BitSolo;
- } else {
- *op_size = kSingle;
- reg_kind = RegStorage::k32BitSolo;
- }
- return RegStorage(RegStorage::kValid | reg_kind | RegStorage::kFloatingPoint | n);
- }
- } else {
- int n = *num_gpr_used;
- if (n < 8) {
- *num_gpr_used = n + 1;
- if (loc->wide || loc->ref) {
- *op_size = k64;
- return RegStorage::Solo64(n);
- } else {
- *op_size = k32;
- return RegStorage::Solo32(n);
- }
- }
- }
- *op_size = kWord;
- return RegStorage::InvalidReg();
-}
-
-RegStorage Arm64Mir2Lir::GetArgMappingToPhysicalReg(int arg_num) {
- if (!in_to_reg_storage_mapping_.IsInitialized()) {
- int start_vreg = mir_graph_->GetFirstInVR();
- RegLocation* arg_locs = &mir_graph_->reg_location_[start_vreg];
-
- InToRegStorageArm64Mapper mapper;
- in_to_reg_storage_mapping_.Initialize(arg_locs, mir_graph_->GetNumOfInVRs(), &mapper);
- }
- return in_to_reg_storage_mapping_.Get(arg_num);
-}
-
-
-/*
- * If there are any ins passed in registers that have not been promoted
- * to a callee-save register, flush them to the frame. Perform initial
- * assignment of promoted arguments.
- *
- * ArgLocs is an array of location records describing the incoming arguments
- * with one location record per word of argument.
- */
-void Arm64Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) {
- int num_gpr_used = 1;
- int num_fpr_used = 0;
-
- /*
- * Dummy up a RegLocation for the incoming StackReference<mirror::ArtMethod>
- * It will attempt to keep kArg0 live (or copy it to home location
- * if promoted).
- */
- RegLocation rl_src = rl_method;
- rl_src.location = kLocPhysReg;
- rl_src.reg = TargetReg(kArg0, kRef);
- rl_src.home = false;
- MarkLive(rl_src);
- StoreValue(rl_method, rl_src);
- // If Method* has been promoted, explicitly flush
- if (rl_method.location == kLocPhysReg) {
- StoreRefDisp(TargetPtrReg(kSp), 0, rl_src.reg, kNotVolatile);
- }
-
- if (mir_graph_->GetNumOfInVRs() == 0) {
- return;
- }
-
- // Handle dalvik registers.
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- int start_vreg = mir_graph_->GetFirstInVR();
- for (uint32_t i = 0; i < mir_graph_->GetNumOfInVRs(); i++) {
- RegLocation* t_loc = &ArgLocs[i];
- OpSize op_size;
- RegStorage reg = GetArgPhysicalReg(t_loc, &num_gpr_used, &num_fpr_used, &op_size);
-
- if (reg.Valid()) {
- // If arriving in register.
-
- // We have already updated the arg location with promoted info
- // so we can be based on it.
- if (t_loc->location == kLocPhysReg) {
- // Just copy it.
- OpRegCopy(t_loc->reg, reg);
- } else {
- // Needs flush.
- if (t_loc->ref) {
- StoreRefDisp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), reg, kNotVolatile);
- } else {
- StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), reg, t_loc->wide ? k64 : k32,
- kNotVolatile);
- }
- }
- } else {
- // If arriving in frame & promoted.
- if (t_loc->location == kLocPhysReg) {
- if (t_loc->ref) {
- LoadRefDisp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), t_loc->reg, kNotVolatile);
- } else {
- LoadBaseDisp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), t_loc->reg,
- t_loc->wide ? k64 : k32, kNotVolatile);
- }
- }
- }
- if (t_loc->wide) {
- // Increment i to skip the next one.
- i++;
- }
- // if ((v_map->core_location == kLocPhysReg) && !t_loc->fp) {
- // OpRegCopy(RegStorage::Solo32(v_map->core_reg), reg);
- // } else if ((v_map->fp_location == kLocPhysReg) && t_loc->fp) {
- // OpRegCopy(RegStorage::Solo32(v_map->fp_reg), reg);
- // } else {
- // StoreBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i), reg, op_size, kNotVolatile);
- // if (reg.Is64Bit()) {
- // if (SRegOffset(start_vreg + i) + 4 != SRegOffset(start_vreg + i + 1)) {
- // LOG(FATAL) << "64 bit value stored in non-consecutive 4 bytes slots";
- // }
- // i += 1;
- // }
- // }
- // } else {
- // // If arriving in frame & promoted
- // if (v_map->core_location == kLocPhysReg) {
- // LoadWordDisp(TargetReg(kSp), SRegOffset(start_vreg + i),
- // RegStorage::Solo32(v_map->core_reg));
- // }
- // if (v_map->fp_location == kLocPhysReg) {
- // LoadWordDisp(TargetReg(kSp), SRegOffset(start_vreg + i), RegStorage::Solo32(v_map->fp_reg));
- // }
- }
-}
-
-/*
- * Load up to 5 arguments, the first three of which will be in
- * kArg1 .. kArg3. On entry kArg0 contains the current method pointer,
- * and as part of the load sequence, it must be replaced with
- * the target method pointer.
- */
-int Arm64Mir2Lir::GenDalvikArgsNoRange(CallInfo* info,
- int call_state, LIR** pcrLabel, NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx, uintptr_t direct_code,
- uintptr_t direct_method, InvokeType type, bool skip_this) {
- return GenDalvikArgsRange(info,
- call_state, pcrLabel, next_call_insn,
- target_method,
- vtable_idx, direct_code,
- direct_method, type, skip_this);
-}
-
-/*
- * May have 0+ arguments (also used for jumbo). Note that
- * source virtual registers may be in physical registers, so may
- * need to be flushed to home location before copying. This
- * applies to arg3 and above (see below).
- *
- * FIXME: update comments.
- *
- * Two general strategies:
- * If < 20 arguments
- * Pass args 3-18 using vldm/vstm block copy
- * Pass arg0, arg1 & arg2 in kArg1-kArg3
- * If 20+ arguments
- * Pass args arg19+ using memcpy block copy
- * Pass arg0, arg1 & arg2 in kArg1-kArg3
- *
- */
-int Arm64Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state,
- LIR** pcrLabel, NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx, uintptr_t direct_code,
- uintptr_t direct_method, InvokeType type, bool skip_this) {
- /* If no arguments, just return */
- if (info->num_arg_words == 0)
- return call_state;
-
- const int start_index = skip_this ? 1 : 0;
-
- InToRegStorageArm64Mapper mapper;
- InToRegStorageMapping in_to_reg_storage_mapping;
- in_to_reg_storage_mapping.Initialize(info->args, info->num_arg_words, &mapper);
- const int last_mapped_in = in_to_reg_storage_mapping.GetMaxMappedIn();
- int regs_left_to_pass_via_stack = info->num_arg_words - (last_mapped_in + 1);
-
- // First of all, check whether it makes sense to use bulk copying.
- // Bulk copying is done only for the range case.
- // TODO: make a constant instead of 2
- if (info->is_range && regs_left_to_pass_via_stack >= 2) {
- // Scan the rest of the args - if in phys_reg flush to memory
- for (int next_arg = last_mapped_in + 1; next_arg < info->num_arg_words;) {
- RegLocation loc = info->args[next_arg];
- if (loc.wide) {
- loc = UpdateLocWide(loc);
- if (loc.location == kLocPhysReg) {
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k64, kNotVolatile);
- }
- next_arg += 2;
- } else {
- loc = UpdateLoc(loc);
- if (loc.location == kLocPhysReg) {
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- if (loc.ref) {
- StoreRefDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, kNotVolatile);
- } else {
- StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k32,
- kNotVolatile);
- }
- }
- next_arg++;
- }
- }
-
- // The rest can be copied together
- int start_offset = SRegOffset(info->args[last_mapped_in + 1].s_reg_low);
- int outs_offset = StackVisitor::GetOutVROffset(last_mapped_in + 1,
- cu_->instruction_set);
-
- int current_src_offset = start_offset;
- int current_dest_offset = outs_offset;
-
- // Only davik regs are accessed in this loop; no next_call_insn() calls.
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- while (regs_left_to_pass_via_stack > 0) {
- /*
- * TODO: Improve by adding block copy for large number of arguments. This
- * should be done, if possible, as a target-depending helper. For now, just
- * copy a Dalvik vreg at a time.
- */
- // Moving 32-bits via general purpose register.
- size_t bytes_to_move = sizeof(uint32_t);
-
- // Instead of allocating a new temp, simply reuse one of the registers being used
- // for argument passing.
- RegStorage temp = TargetReg(kArg3, kNotWide);
-
- // Now load the argument VR and store to the outs.
- Load32Disp(TargetPtrReg(kSp), current_src_offset, temp);
- Store32Disp(TargetPtrReg(kSp), current_dest_offset, temp);
-
- current_src_offset += bytes_to_move;
- current_dest_offset += bytes_to_move;
- regs_left_to_pass_via_stack -= (bytes_to_move >> 2);
- }
- DCHECK_EQ(regs_left_to_pass_via_stack, 0);
- }
-
- // Now handle rest not registers if they are
- if (in_to_reg_storage_mapping.IsThereStackMapped()) {
- RegStorage regWide = TargetReg(kArg3, kWide);
- for (int i = start_index; i <= last_mapped_in + regs_left_to_pass_via_stack; i++) {
- RegLocation rl_arg = info->args[i];
- rl_arg = UpdateRawLoc(rl_arg);
- RegStorage reg = in_to_reg_storage_mapping.Get(i);
- if (!reg.Valid()) {
- int out_offset = StackVisitor::GetOutVROffset(i, cu_->instruction_set);
-
- {
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- if (rl_arg.wide) {
- if (rl_arg.location == kLocPhysReg) {
- StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k64, kNotVolatile);
- } else {
- LoadValueDirectWideFixed(rl_arg, regWide);
- StoreBaseDisp(TargetPtrReg(kSp), out_offset, regWide, k64, kNotVolatile);
- }
- } else {
- if (rl_arg.location == kLocPhysReg) {
- if (rl_arg.ref) {
- StoreRefDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, kNotVolatile);
- } else {
- StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k32, kNotVolatile);
- }
- } else {
- if (rl_arg.ref) {
- RegStorage regSingle = TargetReg(kArg2, kRef);
- LoadValueDirectFixed(rl_arg, regSingle);
- StoreRefDisp(TargetPtrReg(kSp), out_offset, regSingle, kNotVolatile);
- } else {
- RegStorage regSingle = TargetReg(kArg2, kNotWide);
- LoadValueDirectFixed(rl_arg, regSingle);
- StoreBaseDisp(TargetPtrReg(kSp), out_offset, regSingle, k32, kNotVolatile);
- }
- }
- }
- }
- call_state = next_call_insn(cu_, info, call_state, target_method,
- vtable_idx, direct_code, direct_method, type);
- }
- if (rl_arg.wide) {
- i++;
- }
- }
- }
-
- // Finish with mapped registers
- for (int i = start_index; i <= last_mapped_in; i++) {
- RegLocation rl_arg = info->args[i];
- rl_arg = UpdateRawLoc(rl_arg);
- RegStorage reg = in_to_reg_storage_mapping.Get(i);
- if (reg.Valid()) {
- if (rl_arg.wide) {
- LoadValueDirectWideFixed(rl_arg, reg);
- } else {
- LoadValueDirectFixed(rl_arg, reg);
- }
- call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
- direct_code, direct_method, type);
- }
- if (rl_arg.wide) {
- i++;
- }
- }
-
- call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
- direct_code, direct_method, type);
- if (pcrLabel) {
- if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) {
- *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags);
- } else {
- *pcrLabel = nullptr;
- // In lieu of generating a check for kArg1 being null, we need to
- // perform a load when doing implicit checks.
- RegStorage tmp = AllocTemp();
- Load32Disp(TargetReg(kArg1, kRef), 0, tmp);
- MarkPossibleNullPointerException(info->opt_flags);
- FreeTemp(tmp);
- }
- }
- return call_state;
-}
-
void Arm64Mir2Lir::InstallLiteralPools() {
// PC-relative calls to methods.
patches_.reserve(call_method_insns_.size());
@@ -1218,4 +862,43 @@
Mir2Lir::InstallLiteralPools();
}
+int Arm64Mir2Lir::GenDalvikArgsBulkCopy(CallInfo* /*info*/, int /*first*/, int count) {
+ /*
+ * TODO: Improve by adding block copy for large number of arguments. For now, just
+ * copy a Dalvik vreg at a time.
+ */
+ return count;
+}
+
+void Arm64Mir2Lir::GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) {
+ UNUSED(bb);
+ DCHECK(MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode));
+ RegLocation rl_src[3];
+ RegLocation rl_dest = mir_graph_->GetBadLoc();
+ rl_src[0] = rl_src[1] = rl_src[2] = mir_graph_->GetBadLoc();
+ ExtendedMIROpcode opcode = static_cast<ExtendedMIROpcode>(mir->dalvikInsn.opcode);
+ switch (opcode) {
+ case kMirOpMaddInt:
+ case kMirOpMsubInt:
+ rl_dest = mir_graph_->GetDest(mir);
+ rl_src[0] = mir_graph_->GetSrc(mir, 0);
+ rl_src[1] = mir_graph_->GetSrc(mir, 1);
+ rl_src[2]= mir_graph_->GetSrc(mir, 2);
+ GenMaddMsubInt(rl_dest, rl_src[0], rl_src[1], rl_src[2],
+ (opcode == kMirOpMsubInt) ? true : false);
+ break;
+ case kMirOpMaddLong:
+ case kMirOpMsubLong:
+ rl_dest = mir_graph_->GetDestWide(mir);
+ rl_src[0] = mir_graph_->GetSrcWide(mir, 0);
+ rl_src[1] = mir_graph_->GetSrcWide(mir, 2);
+ rl_src[2] = mir_graph_->GetSrcWide(mir, 4);
+ GenMaddMsubLong(rl_dest, rl_src[0], rl_src[1], rl_src[2],
+ (opcode == kMirOpMsubLong) ? true : false);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected opcode: " << static_cast<int>(opcode);
+ }
+}
+
} // namespace art
diff --git a/compiler/dex/quick/arm64/utility_arm64.cc b/compiler/dex/quick/arm64/utility_arm64.cc
index 78a6df8..f48290d 100644
--- a/compiler/dex/quick/arm64/utility_arm64.cc
+++ b/compiler/dex/quick/arm64/utility_arm64.cc
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-#include "arm64_lir.h"
#include "codegen_arm64.h"
+
+#include "arm64_lir.h"
+#include "base/logging.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "dex/reg_storage_eq.h"
@@ -1062,9 +1064,11 @@
opcode = WIDE(kA64Ldr4rXxG);
expected_scale = 3;
break;
- case kSingle: // Intentional fall-through.
- case k32: // Intentional fall-through.
case kReference:
+ r_dest = As32BitReg(r_dest);
+ FALLTHROUGH_INTENDED;
+ case kSingle: // Intentional fall-through.
+ case k32:
r_dest = Check32BitReg(r_dest);
opcode = kA64Ldr4rXxG;
expected_scale = 2;
@@ -1105,11 +1109,6 @@
return load;
}
-LIR* Arm64Mir2Lir::LoadRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest,
- int scale) {
- return LoadBaseIndexed(r_base, r_index, As32BitReg(r_dest), scale, kReference);
-}
-
LIR* Arm64Mir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src,
int scale, OpSize size) {
LIR* store;
@@ -1150,9 +1149,11 @@
opcode = WIDE(kA64Str4rXxG);
expected_scale = 3;
break;
- case kSingle: // Intentional fall-trough.
- case k32: // Intentional fall-trough.
case kReference:
+ r_src = As32BitReg(r_src);
+ FALLTHROUGH_INTENDED;
+ case kSingle: // Intentional fall-trough.
+ case k32:
r_src = Check32BitReg(r_src);
opcode = kA64Str4rXxG;
expected_scale = 2;
@@ -1185,11 +1186,6 @@
return store;
}
-LIR* Arm64Mir2Lir::StoreRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src,
- int scale) {
- return StoreBaseIndexed(r_base, r_index, As32BitReg(r_src), scale, kReference);
-}
-
/*
* Load value from base + displacement. Optionally perform null check
* on base (which must have an associated s_reg and MIR). If not
@@ -1217,9 +1213,11 @@
alt_opcode = WIDE(kA64Ldur3rXd);
}
break;
- case kSingle: // Intentional fall-through.
- case k32: // Intentional fall-trough.
case kReference:
+ r_dest = As32BitReg(r_dest);
+ FALLTHROUGH_INTENDED;
+ case kSingle: // Intentional fall-through.
+ case k32:
r_dest = Check32BitReg(r_dest);
scale = 2;
if (r_dest.IsFloat()) {
@@ -1260,7 +1258,9 @@
// TODO: cleaner support for index/displacement registers? Not a reference, but must match width.
RegStorage r_scratch = AllocTempWide();
LoadConstantWide(r_scratch, displacement);
- load = LoadBaseIndexed(r_base, r_scratch, r_dest, 0, size);
+ load = LoadBaseIndexed(r_base, r_scratch,
+ (size == kReference) ? As64BitReg(r_dest) : r_dest,
+ 0, size);
FreeTemp(r_scratch);
}
@@ -1287,11 +1287,6 @@
return load;
}
-LIR* Arm64Mir2Lir::LoadRefDisp(RegStorage r_base, int displacement, RegStorage r_dest,
- VolatileKind is_volatile) {
- return LoadBaseDisp(r_base, displacement, As32BitReg(r_dest), kReference, is_volatile);
-}
-
LIR* Arm64Mir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStorage r_src,
OpSize size) {
LIR* store = NULL;
@@ -1314,9 +1309,11 @@
alt_opcode = WIDE(kA64Stur3rXd);
}
break;
- case kSingle: // Intentional fall-through.
- case k32: // Intentional fall-trough.
case kReference:
+ r_src = As32BitReg(r_src);
+ FALLTHROUGH_INTENDED;
+ case kSingle: // Intentional fall-through.
+ case k32:
r_src = Check32BitReg(r_src);
scale = 2;
if (r_src.IsFloat()) {
@@ -1351,7 +1348,9 @@
// Use long sequence.
RegStorage r_scratch = AllocTempWide();
LoadConstantWide(r_scratch, displacement);
- store = StoreBaseIndexed(r_base, r_scratch, r_src, 0, size);
+ store = StoreBaseIndexed(r_base, r_scratch,
+ (size == kReference) ? As64BitReg(r_src) : r_src,
+ 0, size);
FreeTemp(r_scratch);
}
@@ -1385,11 +1384,6 @@
return store;
}
-LIR* Arm64Mir2Lir::StoreRefDisp(RegStorage r_base, int displacement, RegStorage r_src,
- VolatileKind is_volatile) {
- return StoreBaseDisp(r_base, displacement, As32BitReg(r_src), kReference, is_volatile);
-}
-
LIR* Arm64Mir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) {
UNUSED(r_dest, r_src);
LOG(FATAL) << "Unexpected use of OpFpRegCopy for Arm64";
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index 58bcee2..88a4605 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -14,13 +14,16 @@
* limitations under the License.
*/
-#include "dex/compiler_internals.h"
+#include "mir_to_lir-inl.h"
+
+#include "dex/mir_graph.h"
+#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
+#include "driver/dex_compilation_unit.h"
#include "dex_file-inl.h"
#include "gc_map.h"
#include "gc_map_builder.h"
#include "mapping_table.h"
-#include "mir_to_lir-inl.h"
#include "dex/quick/dex_file_method_inliner.h"
#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "dex/verification_results.h"
@@ -538,9 +541,12 @@
bx_offset = tab_rec->anchor->offset + 4;
break;
case kX86:
- case kX86_64:
bx_offset = 0;
break;
+ case kX86_64:
+ // RIP relative to switch table.
+ bx_offset = tab_rec->offset;
+ break;
case kArm64:
case kMips:
bx_offset = tab_rec->anchor->offset;
@@ -551,29 +557,49 @@
LOG(INFO) << "Switch table for offset 0x" << std::hex << bx_offset;
}
if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
- const int32_t* keys = reinterpret_cast<const int32_t*>(&(tab_rec->table[2]));
- for (int elems = 0; elems < tab_rec->table[1]; elems++) {
- int disp = tab_rec->targets[elems]->offset - bx_offset;
+ DCHECK(tab_rec->switch_mir != nullptr);
+ BasicBlock* bb = mir_graph_->GetBasicBlock(tab_rec->switch_mir->bb);
+ DCHECK(bb != nullptr);
+ int elems = 0;
+ for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) {
+ int key = successor_block_info->key;
+ int target = successor_block_info->block;
+ LIR* boundary_lir = InsertCaseLabel(target, key);
+ DCHECK(boundary_lir != nullptr);
+ int disp = boundary_lir->offset - bx_offset;
+ Push32(code_buffer_, key);
+ Push32(code_buffer_, disp);
if (cu_->verbose) {
LOG(INFO) << " Case[" << elems << "] key: 0x"
- << std::hex << keys[elems] << ", disp: 0x"
+ << std::hex << key << ", disp: 0x"
<< std::hex << disp;
}
- Push32(code_buffer_, keys[elems]);
- Push32(code_buffer_,
- tab_rec->targets[elems]->offset - bx_offset);
+ elems++;
}
+ DCHECK_EQ(elems, tab_rec->table[1]);
} else {
DCHECK_EQ(static_cast<int>(tab_rec->table[0]),
static_cast<int>(Instruction::kPackedSwitchSignature));
- for (int elems = 0; elems < tab_rec->table[1]; elems++) {
- int disp = tab_rec->targets[elems]->offset - bx_offset;
+ DCHECK(tab_rec->switch_mir != nullptr);
+ BasicBlock* bb = mir_graph_->GetBasicBlock(tab_rec->switch_mir->bb);
+ DCHECK(bb != nullptr);
+ int elems = 0;
+ int low_key = s4FromSwitchData(&tab_rec->table[2]);
+ for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) {
+ int key = successor_block_info->key;
+ DCHECK_EQ(elems + low_key, key);
+ int target = successor_block_info->block;
+ LIR* boundary_lir = InsertCaseLabel(target, key);
+ DCHECK(boundary_lir != nullptr);
+ int disp = boundary_lir->offset - bx_offset;
+ Push32(code_buffer_, disp);
if (cu_->verbose) {
LOG(INFO) << " Case[" << elems << "] disp: 0x"
<< std::hex << disp;
}
- Push32(code_buffer_, tab_rec->targets[elems]->offset - bx_offset);
+ elems++;
}
+ DCHECK_EQ(elems, tab_rec->table[1]);
}
}
}
@@ -775,6 +801,10 @@
": " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
native_gc_map_builder.AddEntry(native_offset, references);
}
+
+ // Maybe not necessary, but this could help prevent errors where we access the verified method
+ // after it has been deleted.
+ mir_graph_->GetCurrentDexCompilationUnit()->ClearVerifiedMethod();
}
/* Determine the offset of each literal field */
@@ -820,13 +850,15 @@
* branch table during the assembly phase. All resource flags
* are set to prevent code motion. KeyVal is just there for debugging.
*/
-LIR* Mir2Lir::InsertCaseLabel(DexOffset vaddr, int keyVal) {
- LIR* boundary_lir = &block_label_list_[mir_graph_->FindBlock(vaddr)->id];
+LIR* Mir2Lir::InsertCaseLabel(uint32_t bbid, int keyVal) {
+ LIR* boundary_lir = &block_label_list_[bbid];
LIR* res = boundary_lir;
if (cu_->verbose) {
// Only pay the expense if we're pretty-printing.
LIR* new_label = static_cast<LIR*>(arena_->Alloc(sizeof(LIR), kArenaAllocLIR));
- new_label->dalvik_offset = vaddr;
+ BasicBlock* bb = mir_graph_->GetBasicBlock(bbid);
+ DCHECK(bb != nullptr);
+ new_label->dalvik_offset = bb->start_offset;
new_label->opcode = kPseudoCaseLabel;
new_label->operands[0] = keyVal;
new_label->flags.fixup = kFixupLabel;
@@ -838,40 +870,6 @@
return res;
}
-void Mir2Lir::MarkPackedCaseLabels(Mir2Lir::SwitchTable* tab_rec) {
- const uint16_t* table = tab_rec->table;
- DexOffset base_vaddr = tab_rec->vaddr;
- const int32_t *targets = reinterpret_cast<const int32_t*>(&table[4]);
- int entries = table[1];
- int low_key = s4FromSwitchData(&table[2]);
- for (int i = 0; i < entries; i++) {
- tab_rec->targets[i] = InsertCaseLabel(base_vaddr + targets[i], i + low_key);
- }
-}
-
-void Mir2Lir::MarkSparseCaseLabels(Mir2Lir::SwitchTable* tab_rec) {
- const uint16_t* table = tab_rec->table;
- DexOffset base_vaddr = tab_rec->vaddr;
- int entries = table[1];
- const int32_t* keys = reinterpret_cast<const int32_t*>(&table[2]);
- const int32_t* targets = &keys[entries];
- for (int i = 0; i < entries; i++) {
- tab_rec->targets[i] = InsertCaseLabel(base_vaddr + targets[i], keys[i]);
- }
-}
-
-void Mir2Lir::ProcessSwitchTables() {
- for (Mir2Lir::SwitchTable* tab_rec : switch_tables_) {
- if (tab_rec->table[0] == Instruction::kPackedSwitchSignature) {
- MarkPackedCaseLabels(tab_rec);
- } else if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
- MarkSparseCaseLabels(tab_rec);
- } else {
- LOG(FATAL) << "Invalid switch table";
- }
- }
-}
-
void Mir2Lir::DumpSparseSwitchTable(const uint16_t* table) {
/*
* Sparse switch data format:
@@ -957,12 +955,12 @@
// TODO: move to mir_to_lir.cc
Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
- : Backend(arena),
- literal_list_(nullptr),
+ : literal_list_(nullptr),
method_literal_list_(nullptr),
class_literal_list_(nullptr),
code_literal_list_(nullptr),
first_fixup_(nullptr),
+ arena_(arena),
cu_(cu),
mir_graph_(mir_graph),
switch_tables_(arena->Adapter(kArenaAllocSwitchTable)),
@@ -990,7 +988,8 @@
last_lir_insn_(nullptr),
slow_paths_(arena->Adapter(kArenaAllocSlowPaths)),
mem_ref_type_(ResourceMask::kHeapRef),
- mask_cache_(arena) {
+ mask_cache_(arena),
+ in_to_reg_storage_mapping_(arena) {
switch_tables_.reserve(4);
fill_array_data_.reserve(4);
tempreg_info_.reserve(20);
@@ -1021,9 +1020,6 @@
/* Method is not empty */
if (first_lir_insn_) {
- // mark the targets of switch statement case labels
- ProcessSwitchTables();
-
/* Convert LIR into machine code. */
AssembleLIR();
@@ -1078,12 +1074,20 @@
});
std::unique_ptr<std::vector<uint8_t>> cfi_info(ReturnFrameDescriptionEntry());
- CompiledMethod* result =
- new CompiledMethod(cu_->compiler_driver, cu_->instruction_set, code_buffer_, frame_size_,
- core_spill_mask_, fp_spill_mask_, &src_mapping_table_, encoded_mapping_table_,
- vmap_encoder.GetData(), native_gc_map_, cfi_info.get(),
- ArrayRef<LinkerPatch>(patches_));
- return result;
+ ArrayRef<const uint8_t> cfi_ref;
+ if (cfi_info.get() != nullptr) {
+ cfi_ref = ArrayRef<const uint8_t>(*cfi_info);
+ }
+ return CompiledMethod::SwapAllocCompiledMethod(
+ cu_->compiler_driver, cu_->instruction_set,
+ ArrayRef<const uint8_t>(code_buffer_),
+ frame_size_, core_spill_mask_, fp_spill_mask_,
+ &src_mapping_table_,
+ ArrayRef<const uint8_t>(encoded_mapping_table_),
+ ArrayRef<const uint8_t>(vmap_encoder.GetData()),
+ ArrayRef<const uint8_t>(native_gc_map_),
+ cfi_ref,
+ ArrayRef<LinkerPatch>(patches_));
}
size_t Mir2Lir::GetMaxPossibleCompilerTemps() const {
@@ -1159,24 +1163,6 @@
new_lir->next->prev = new_lir;
}
-bool Mir2Lir::IsPowerOfTwo(uint64_t x) {
- return (x & (x - 1)) == 0;
-}
-
-// Returns the index of the lowest set bit in 'x'.
-int32_t Mir2Lir::LowestSetBit(uint64_t x) {
- int bit_posn = 0;
- while ((x & 0xf) == 0) {
- bit_posn += 4;
- x >>= 4;
- }
- while ((x & 1) == 0) {
- bit_posn++;
- x >>= 1;
- }
- return bit_posn;
-}
-
bool Mir2Lir::PartiallyIntersects(RegLocation rl_src, RegLocation rl_dest) {
DCHECK(rl_src.wide);
DCHECK(rl_dest.wide);
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index 3039852..7245853 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -18,18 +18,15 @@
#include <algorithm>
+#include "base/logging.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "base/mutex-inl.h"
-#include "dex/frontend.h"
-#include "thread.h"
+#include "dex/compiler_ir.h"
#include "thread-inl.h"
#include "dex/mir_graph.h"
#include "dex/quick/mir_to_lir.h"
-#include "dex_instruction.h"
#include "dex_instruction-inl.h"
#include "driver/dex_compilation_unit.h"
-#include "verifier/method_verifier.h"
#include "verifier/method_verifier-inl.h"
namespace art {
@@ -293,9 +290,9 @@
{ { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, kInlineIntrinsic, { d } } }
INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0),
- INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0),
+ INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, kIntrinsicFlagToFloatingPoint),
INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0),
- INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, 0),
+ INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, kIntrinsicFlagToFloatingPoint),
INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, k32),
INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, k64),
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index 774176e..9f53b89 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -13,18 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+#include "mir_to_lir-inl.h"
+
+#include <functional>
+
#include "arch/arm/instruction_set_features_arm.h"
+#include "base/macros.h"
#include "dex/compiler_ir.h"
-#include "dex/compiler_internals.h"
+#include "dex/mir_graph.h"
#include "dex/quick/arm/arm_lir.h"
-#include "dex/quick/mir_to_lir-inl.h"
+#include "driver/compiler_driver.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "mirror/array.h"
#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object_reference.h"
+#include "utils.h"
#include "verifier/method_verifier.h"
-#include <functional>
namespace art {
@@ -38,6 +44,18 @@
* and "op" calls may be used here.
*/
+ALWAYS_INLINE static inline bool ForceSlowFieldPath(CompilationUnit* cu) {
+ return (cu->enable_debug & (1 << kDebugSlowFieldPath)) != 0;
+}
+
+ALWAYS_INLINE static inline bool ForceSlowStringPath(CompilationUnit* cu) {
+ return (cu->enable_debug & (1 << kDebugSlowStringPath)) != 0;
+}
+
+ALWAYS_INLINE static inline bool ForceSlowTypePath(CompilationUnit* cu) {
+ return (cu->enable_debug & (1 << kDebugSlowTypePath)) != 0;
+}
+
/*
* Generate a kPseudoBarrier marker to indicate the boundary of special
* blocks.
@@ -371,19 +389,19 @@
// The fast path.
if (!use_direct_type_ptr) {
LoadClassType(*dex_file, type_idx, kArg0);
- CallRuntimeHelperRegMethodRegLocation(kQuickAllocArrayResolved, TargetReg(kArg0, kNotWide),
+ CallRuntimeHelperRegRegLocationMethod(kQuickAllocArrayResolved, TargetReg(kArg0, kNotWide),
rl_src, true);
} else {
// Use the direct pointer.
- CallRuntimeHelperImmMethodRegLocation(kQuickAllocArrayResolved, direct_type_ptr, rl_src,
+ CallRuntimeHelperImmRegLocationMethod(kQuickAllocArrayResolved, direct_type_ptr, rl_src,
true);
}
} else {
// The slow path.
- CallRuntimeHelperImmMethodRegLocation(kQuickAllocArray, type_idx, rl_src, true);
+ CallRuntimeHelperImmRegLocationMethod(kQuickAllocArray, type_idx, rl_src, true);
}
} else {
- CallRuntimeHelperImmMethodRegLocation(kQuickAllocArrayWithAccessCheck, type_idx, rl_src, true);
+ CallRuntimeHelperImmRegLocationMethod(kQuickAllocArrayWithAccessCheck, type_idx, rl_src, true);
}
StoreValue(rl_dest, GetReturn(kRefReg));
}
@@ -405,7 +423,7 @@
} else {
target = kQuickCheckAndAllocArrayWithAccessCheck;
}
- CallRuntimeHelperImmMethodImm(target, type_idx, elems, true);
+ CallRuntimeHelperImmImmMethod(target, type_idx, elems, true);
FreeTemp(TargetReg(kArg2, kNotWide));
FreeTemp(TargetReg(kArg1, kNotWide));
/*
@@ -591,7 +609,7 @@
const MirSFieldLoweringInfo& field_info = mir_graph_->GetSFieldLoweringInfo(mir);
DCHECK_EQ(SPutMemAccessType(mir->dalvikInsn.opcode), field_info.MemAccessType());
cu_->compiler_driver->ProcessedStaticField(field_info.FastPut(), field_info.IsReferrersClass());
- if (!SLOW_FIELD_PATH && field_info.FastPut()) {
+ if (!ForceSlowFieldPath(cu_) && field_info.FastPut()) {
DCHECK_GE(field_info.FieldOffset().Int32Value(), 0);
RegStorage r_base;
if (field_info.IsReferrersClass()) {
@@ -711,7 +729,7 @@
DCHECK_EQ(SGetMemAccessType(mir->dalvikInsn.opcode), field_info.MemAccessType());
cu_->compiler_driver->ProcessedStaticField(field_info.FastGet(), field_info.IsReferrersClass());
- if (!SLOW_FIELD_PATH && field_info.FastGet()) {
+ if (!ForceSlowFieldPath(cu_) && field_info.FastGet()) {
DCHECK_GE(field_info.FieldOffset().Int32Value(), 0);
RegStorage r_base;
if (field_info.IsReferrersClass()) {
@@ -849,7 +867,7 @@
const MirIFieldLoweringInfo& field_info = mir_graph_->GetIFieldLoweringInfo(mir);
DCHECK_EQ(IGetMemAccessType(mir->dalvikInsn.opcode), field_info.MemAccessType());
cu_->compiler_driver->ProcessedInstanceField(field_info.FastGet());
- if (!SLOW_FIELD_PATH && field_info.FastGet()) {
+ if (!ForceSlowFieldPath(cu_) && field_info.FastGet()) {
RegisterClass reg_class = RegClassForFieldLoadStore(size, field_info.IsVolatile());
// A load of the class will lead to an iget with offset 0.
DCHECK_GE(field_info.FieldOffset().Int32Value(), 0);
@@ -923,7 +941,7 @@
const MirIFieldLoweringInfo& field_info = mir_graph_->GetIFieldLoweringInfo(mir);
DCHECK_EQ(IPutMemAccessType(mir->dalvikInsn.opcode), field_info.MemAccessType());
cu_->compiler_driver->ProcessedInstanceField(field_info.FastPut());
- if (!SLOW_FIELD_PATH && field_info.FastPut()) {
+ if (!ForceSlowFieldPath(cu_) && field_info.FastPut()) {
RegisterClass reg_class = RegClassForFieldLoadStore(size, field_info.IsVolatile());
// Dex code never writes to the class field.
DCHECK_GE(static_cast<uint32_t>(field_info.FieldOffset().Int32Value()),
@@ -936,15 +954,15 @@
}
GenNullCheck(rl_obj.reg, opt_flags);
int field_offset = field_info.FieldOffset().Int32Value();
- LIR* store;
+ LIR* null_ck_insn;
if (IsRef(size)) {
- store = StoreRefDisp(rl_obj.reg, field_offset, rl_src.reg, field_info.IsVolatile() ?
+ null_ck_insn = StoreRefDisp(rl_obj.reg, field_offset, rl_src.reg, field_info.IsVolatile() ?
kVolatile : kNotVolatile);
} else {
- store = StoreBaseDisp(rl_obj.reg, field_offset, rl_src.reg, size,
- field_info.IsVolatile() ? kVolatile : kNotVolatile);
+ null_ck_insn = StoreBaseDisp(rl_obj.reg, field_offset, rl_src.reg, size,
+ field_info.IsVolatile() ? kVolatile : kNotVolatile);
}
- MarkPossibleNullPointerExceptionAfter(opt_flags, store);
+ MarkPossibleNullPointerExceptionAfter(opt_flags, null_ck_insn);
if (IsRef(size) && !mir_graph_->IsConstantNullRef(rl_src)) {
MarkGCCard(opt_flags, rl_src.reg, rl_obj.reg);
}
@@ -1013,7 +1031,7 @@
int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
LoadRefDisp(res_reg, offset_of_type, rl_result.reg, kNotVolatile);
if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file,
- type_idx) || SLOW_TYPE_PATH) {
+ type_idx) || ForceSlowTypePath(cu_)) {
// Slow path, at runtime test if type is null and if so initialize
FlushAllRegs();
LIR* branch = OpCmpImmBranch(kCondEq, rl_result.reg, 0, NULL);
@@ -1058,7 +1076,7 @@
int32_t offset_of_string = mirror::ObjectArray<mirror::String>::OffsetOfElement(string_idx).
Int32Value();
if (!cu_->compiler_driver->CanAssumeStringIsPresentInDexCache(
- *cu_->dex_file, string_idx) || SLOW_STRING_PATH) {
+ *cu_->dex_file, string_idx) || ForceSlowStringPath(cu_)) {
// slow path, resolve string if not in dex cache
FlushAllRegs();
LockCallTemps(); // Using explicit registers
@@ -1098,7 +1116,7 @@
void Compile() {
GenerateTargetLabel();
- m2l_->CallRuntimeHelperRegImm(kQuickResolveString, r_method_, string_idx_, true);
+ m2l_->CallRuntimeHelperImmReg(kQuickResolveString, string_idx_, r_method_, true);
m2l_->OpUnconditionalBranch(cont_);
}
@@ -1676,7 +1694,7 @@
rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, op == kOpDiv);
done = true;
} else if (cu_->instruction_set == kThumb2) {
- if (cu_->GetInstructionSetFeatures()->AsArmInstructionSetFeatures()->
+ if (cu_->compiler_driver->GetInstructionSetFeatures()->AsArmInstructionSetFeatures()->
HasDivideInstruction()) {
// Use ARM SDIV instruction for division. For remainder we also need to
// calculate using a MUL and subtract.
@@ -1724,16 +1742,12 @@
// Returns true if it added instructions to 'cu' to divide 'rl_src' by 'lit'
// and store the result in 'rl_dest'.
-bool Mir2Lir::HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div,
+bool Mir2Lir::HandleEasyDivRem(Instruction::Code dalvik_opcode ATTRIBUTE_UNUSED, bool is_div,
RegLocation rl_src, RegLocation rl_dest, int lit) {
- if ((lit < 2) || ((cu_->instruction_set != kThumb2) && !IsPowerOfTwo(lit))) {
+ if ((lit < 2) || (!IsPowerOfTwo(lit))) {
return false;
}
- // No divide instruction for Arm, so check for more special cases
- if ((cu_->instruction_set == kThumb2) && !IsPowerOfTwo(lit)) {
- return SmallLiteralDivRem(dalvik_opcode, is_div, rl_src, rl_dest, lit);
- }
- int k = LowestSetBit(lit);
+ int k = CTZ(lit);
if (k >= 30) {
// Avoid special cases.
return false;
@@ -1813,18 +1827,18 @@
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
if (power_of_two) {
// Shift.
- OpRegRegImm(kOpLsl, rl_result.reg, rl_src.reg, LowestSetBit(lit));
+ OpRegRegImm(kOpLsl, rl_result.reg, rl_src.reg, CTZ(lit));
} else if (pop_count_le2) {
// Shift and add and shift.
- int first_bit = LowestSetBit(lit);
- int second_bit = LowestSetBit(lit ^ (1 << first_bit));
+ int first_bit = CTZ(lit);
+ int second_bit = CTZ(lit ^ (1 << first_bit));
GenMultiplyByTwoBitMultiplier(rl_src, rl_result, lit, first_bit, second_bit);
} else {
// Reverse subtract: (src << (shift + 1)) - src.
DCHECK(power_of_two_minus_one);
- // TUNING: rsb dst, src, src lsl#LowestSetBit(lit + 1)
+ // TUNING: rsb dst, src, src lsl#CTZ(lit + 1)
RegStorage t_reg = AllocTemp();
- OpRegRegImm(kOpLsl, t_reg, rl_src.reg, LowestSetBit(lit + 1));
+ OpRegRegImm(kOpLsl, t_reg, rl_src.reg, CTZ(lit + 1));
OpRegRegReg(kOpSub, rl_result.reg, t_reg, rl_src.reg);
}
StoreValue(rl_dest, rl_result);
@@ -1974,7 +1988,7 @@
rl_result = GenDivRemLit(rl_dest, rl_src, lit, is_div);
done = true;
} else if (cu_->instruction_set == kThumb2) {
- if (cu_->GetInstructionSetFeatures()->AsArmInstructionSetFeatures()->
+ if (cu_->compiler_driver->GetInstructionSetFeatures()->AsArmInstructionSetFeatures()->
HasDivideInstruction()) {
// Use ARM SDIV instruction for division. For remainder we also need to
// calculate using a MUL and subtract.
@@ -2163,18 +2177,15 @@
/* Check if we need to check for pending suspend request */
void Mir2Lir::GenSuspendTest(int opt_flags) {
+ if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK) != 0) {
+ return;
+ }
if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitSuspendChecks()) {
- if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK)) {
- return;
- }
FlushAllRegs();
LIR* branch = OpTestSuspend(NULL);
LIR* cont = NewLIR0(kPseudoTargetLabel);
AddSlowPath(new (arena_) SuspendCheckSlowPath(this, branch, cont));
} else {
- if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK)) {
- return;
- }
FlushAllRegs(); // TODO: needed?
LIR* inst = CheckSuspendUsingLoad();
MarkSafepointPC(inst);
@@ -2183,11 +2194,11 @@
/* Check if we need to check for pending suspend request */
void Mir2Lir::GenSuspendTestAndBranch(int opt_flags, LIR* target) {
+ if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK) != 0) {
+ OpUnconditionalBranch(target);
+ return;
+ }
if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitSuspendChecks()) {
- if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK)) {
- OpUnconditionalBranch(target);
- return;
- }
OpTestSuspend(target);
FlushAllRegs();
LIR* branch = OpUnconditionalBranch(nullptr);
@@ -2195,10 +2206,6 @@
} else {
// For the implicit suspend check, just perform the trigger
// load and branch to the target.
- if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK)) {
- OpUnconditionalBranch(target);
- return;
- }
FlushAllRegs();
LIR* inst = CheckSuspendUsingLoad();
MarkSafepointPC(inst);
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 31b81bf..bb5b0cd 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -14,12 +14,16 @@
* limitations under the License.
*/
+#include "mir_to_lir-inl.h"
+
#include "arm/codegen_arm.h"
#include "dex/compiler_ir.h"
-#include "dex/frontend.h"
+#include "dex/dex_flags.h"
+#include "dex/mir_graph.h"
#include "dex/quick/dex_file_method_inliner.h"
#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "dex_file-inl.h"
+#include "driver/compiler_driver.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "invoke_type.h"
#include "mirror/array.h"
@@ -27,7 +31,6 @@
#include "mirror/dex_cache.h"
#include "mirror/object_array-inl.h"
#include "mirror/string.h"
-#include "mir_to_lir-inl.h"
#include "scoped_thread_state_change.h"
namespace art {
@@ -201,16 +204,16 @@
CallHelper(r_tgt, trampoline, safepoint_pc);
}
-void Mir2Lir::CallRuntimeHelperRegMethodRegLocation(QuickEntrypointEnum trampoline, RegStorage arg0,
- RegLocation arg2, bool safepoint_pc) {
+void Mir2Lir::CallRuntimeHelperRegRegLocationMethod(QuickEntrypointEnum trampoline, RegStorage arg0,
+ RegLocation arg1, bool safepoint_pc) {
RegStorage r_tgt = CallHelperSetup(trampoline);
- DCHECK(!IsSameReg(TargetReg(kArg1, arg0.GetWideKind()), arg0));
+ DCHECK(!IsSameReg(TargetReg(kArg2, arg0.GetWideKind()), arg0));
RegStorage r_tmp = TargetReg(kArg0, arg0.GetWideKind());
if (r_tmp.NotExactlyEquals(arg0)) {
OpRegCopy(r_tmp, arg0);
}
- LoadCurrMethodDirect(TargetReg(kArg1, kRef));
- LoadValueDirectFixed(arg2, TargetReg(kArg2, arg2));
+ LoadValueDirectFixed(arg1, TargetReg(kArg1, arg1));
+ LoadCurrMethodDirect(TargetReg(kArg2, kRef));
ClobberCallerSave();
CallHelper(r_tgt, trampoline, safepoint_pc);
}
@@ -306,21 +309,21 @@
CallHelper(r_tgt, trampoline, safepoint_pc);
}
-void Mir2Lir::CallRuntimeHelperImmMethodRegLocation(QuickEntrypointEnum trampoline, int arg0,
- RegLocation arg2, bool safepoint_pc) {
+void Mir2Lir::CallRuntimeHelperImmRegLocationMethod(QuickEntrypointEnum trampoline, int arg0,
+ RegLocation arg1, bool safepoint_pc) {
RegStorage r_tgt = CallHelperSetup(trampoline);
- LoadValueDirectFixed(arg2, TargetReg(kArg2, arg2));
- LoadCurrMethodDirect(TargetReg(kArg1, kRef));
+ LoadValueDirectFixed(arg1, TargetReg(kArg1, arg1));
+ LoadCurrMethodDirect(TargetReg(kArg2, kRef));
LoadConstant(TargetReg(kArg0, kNotWide), arg0);
ClobberCallerSave();
CallHelper(r_tgt, trampoline, safepoint_pc);
}
-void Mir2Lir::CallRuntimeHelperImmMethodImm(QuickEntrypointEnum trampoline, int arg0, int arg2,
+void Mir2Lir::CallRuntimeHelperImmImmMethod(QuickEntrypointEnum trampoline, int arg0, int arg1,
bool safepoint_pc) {
RegStorage r_tgt = CallHelperSetup(trampoline);
- LoadCurrMethodDirect(TargetReg(kArg1, kRef));
- LoadConstant(TargetReg(kArg2, kNotWide), arg2);
+ LoadCurrMethodDirect(TargetReg(kArg2, kRef));
+ LoadConstant(TargetReg(kArg1, kNotWide), arg1);
LoadConstant(TargetReg(kArg0, kNotWide), arg0);
ClobberCallerSave();
CallHelper(r_tgt, trampoline, safepoint_pc);
@@ -401,59 +404,50 @@
* half to memory as well.
*/
ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- for (uint32_t i = 0; i < mir_graph_->GetNumOfInVRs(); i++) {
- PromotionMap* v_map = &promotion_map_[start_vreg + i];
+ RegLocation* t_loc = nullptr;
+ for (uint32_t i = 0; i < mir_graph_->GetNumOfInVRs(); i += t_loc->wide ? 2 : 1) {
+ // get reg corresponding to input
RegStorage reg = GetArgMappingToPhysicalReg(i);
+ t_loc = &ArgLocs[i];
+
+ // If the wide input appeared as single, flush it and go
+ // as it comes from memory.
+ if (t_loc->wide && reg.Valid() && !reg.Is64Bit()) {
+ // The memory already holds the half. Don't do anything.
+ reg = RegStorage::InvalidReg();
+ }
if (reg.Valid()) {
- // If arriving in register
- bool need_flush = true;
- RegLocation* t_loc = &ArgLocs[i];
- if ((v_map->core_location == kLocPhysReg) && !t_loc->fp) {
- OpRegCopy(RegStorage::Solo32(v_map->core_reg), reg);
- need_flush = false;
- } else if ((v_map->fp_location == kLocPhysReg) && t_loc->fp) {
- OpRegCopy(RegStorage::Solo32(v_map->fp_reg), reg);
- need_flush = false;
- } else {
- need_flush = true;
- }
+ // If arriving in register.
- // For wide args, force flush if not fully promoted
- if (t_loc->wide) {
- PromotionMap* p_map = v_map + (t_loc->high_word ? -1 : +1);
- // Is only half promoted?
- need_flush |= (p_map->core_location != v_map->core_location) ||
- (p_map->fp_location != v_map->fp_location);
- if ((cu_->instruction_set == kThumb2) && t_loc->fp && !need_flush) {
- /*
- * In Arm, a double is represented as a pair of consecutive single float
- * registers starting at an even number. It's possible that both Dalvik vRegs
- * representing the incoming double were independently promoted as singles - but
- * not in a form usable as a double. If so, we need to flush - even though the
- * incoming arg appears fully in register. At this point in the code, both
- * halves of the double are promoted. Make sure they are in a usable form.
- */
- int lowreg_index = start_vreg + i + (t_loc->high_word ? -1 : 0);
- int low_reg = promotion_map_[lowreg_index].fp_reg;
- int high_reg = promotion_map_[lowreg_index + 1].fp_reg;
- if (((low_reg & 0x1) != 0) || (high_reg != (low_reg + 1))) {
- need_flush = true;
- }
+ // We have already updated the arg location with promoted info
+ // so we can be based on it.
+ if (t_loc->location == kLocPhysReg) {
+ // Just copy it.
+ if (t_loc->wide) {
+ OpRegCopyWide(t_loc->reg, reg);
+ } else {
+ OpRegCopy(t_loc->reg, reg);
+ }
+ } else {
+ // Needs flush.
+ int offset = SRegOffset(start_vreg + i);
+ if (t_loc->ref) {
+ StoreRefDisp(TargetPtrReg(kSp), offset, reg, kNotVolatile);
+ } else {
+ StoreBaseDisp(TargetPtrReg(kSp), offset, reg, t_loc->wide ? k64 : k32, kNotVolatile);
}
}
- if (need_flush) {
- Store32Disp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), reg);
- }
} else {
- // If arriving in frame & promoted
- if (v_map->core_location == kLocPhysReg) {
- Load32Disp(TargetPtrReg(kSp), SRegOffset(start_vreg + i),
- RegStorage::Solo32(v_map->core_reg));
- }
- if (v_map->fp_location == kLocPhysReg) {
- Load32Disp(TargetPtrReg(kSp), SRegOffset(start_vreg + i),
- RegStorage::Solo32(v_map->fp_reg));
+ // If arriving in frame & promoted.
+ if (t_loc->location == kLocPhysReg) {
+ int offset = SRegOffset(start_vreg + i);
+ if (t_loc->ref) {
+ LoadRefDisp(TargetPtrReg(kSp), offset, t_loc->reg, kNotVolatile);
+ } else {
+ LoadBaseDisp(TargetPtrReg(kSp), offset, t_loc->reg, t_loc->wide ? k64 : k32,
+ kNotVolatile);
+ }
}
}
}
@@ -488,87 +482,10 @@
/*
* Bit of a hack here - in the absence of a real scheduling pass,
- * emit the next instruction in static & direct invoke sequences.
- */
-static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
- int state, const MethodReference& target_method,
- uint32_t,
- uintptr_t direct_code, uintptr_t direct_method,
- InvokeType type) {
- UNUSED(info);
- DCHECK(cu->instruction_set != kX86 && cu->instruction_set != kX86_64 &&
- cu->instruction_set != kThumb2 && cu->instruction_set != kArm &&
- cu->instruction_set != kArm64);
- Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
- if (direct_code != 0 && direct_method != 0) {
- switch (state) {
- case 0: // Get the current Method* [sets kArg0]
- if (direct_code != static_cast<uintptr_t>(-1)) {
- cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
- } else {
- cg->LoadCodeAddress(target_method, type, kInvokeTgt);
- }
- if (direct_method != static_cast<uintptr_t>(-1)) {
- cg->LoadConstant(cg->TargetReg(kArg0, kRef), direct_method);
- } else {
- cg->LoadMethodAddress(target_method, type, kArg0);
- }
- break;
- default:
- return -1;
- }
- } else {
- RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
- switch (state) {
- case 0: // Get the current Method* [sets kArg0]
- // TUNING: we can save a reg copy if Method* has been promoted.
- cg->LoadCurrMethodDirect(arg0_ref);
- break;
- case 1: // Get method->dex_cache_resolved_methods_
- cg->LoadRefDisp(arg0_ref,
- mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
- arg0_ref,
- kNotVolatile);
- // Set up direct code if known.
- if (direct_code != 0) {
- if (direct_code != static_cast<uintptr_t>(-1)) {
- cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
- } else {
- CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds());
- cg->LoadCodeAddress(target_method, type, kInvokeTgt);
- }
- }
- break;
- case 2: // Grab target method*
- CHECK_EQ(cu->dex_file, target_method.dex_file);
- cg->LoadRefDisp(arg0_ref,
- ObjArray::OffsetOfElement(target_method.dex_method_index).Int32Value(),
- arg0_ref,
- kNotVolatile);
- break;
- case 3: // Grab the code from the method*
- if (direct_code == 0) {
- if (CommonCallCodeLoadCodePointerIntoInvokeTgt(&arg0_ref, cu, cg)) {
- break; // kInvokeTgt := arg0_ref->entrypoint
- }
- } else {
- break;
- }
- DCHECK(cu->instruction_set == kX86 || cu->instruction_set == kX86_64);
- FALLTHROUGH_INTENDED;
- default:
- return -1;
- }
- }
- return state + 1;
-}
-
-/*
- * Bit of a hack here - in the absence of a real scheduling pass,
* emit the next instruction in a virtual invoke sequence.
* We can use kLr as a temp prior to target address loading
* Note also that we'll load the first argument ("this") into
- * kArg1 here rather than the standard LoadArgRegs.
+ * kArg1 here rather than the standard GenDalvikArgs.
*/
static int NextVCallInsn(CompilationUnit* cu, CallInfo* info,
int state, const MethodReference& target_method,
@@ -612,7 +529,7 @@
* Emit the next instruction in an invoke interface sequence. This will do a lookup in the
* class's IMT, calling either the actual method or art_quick_imt_conflict_trampoline if
* more than one interface method map to the same index. Note also that we'll load the first
- * argument ("this") into kArg1 here rather than the standard LoadArgRegs.
+ * argument ("this") into kArg1 here rather than the standard GenDalvikArgs.
*/
static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state,
const MethodReference& target_method,
@@ -719,158 +636,6 @@
target_method, 0);
}
-int Mir2Lir::LoadArgRegs(CallInfo* info, int call_state,
- NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx, uintptr_t direct_code,
- uintptr_t direct_method, InvokeType type, bool skip_this) {
- int last_arg_reg = 3 - 1;
- int arg_regs[3] = {TargetReg(kArg1, kNotWide).GetReg(), TargetReg(kArg2, kNotWide).GetReg(),
- TargetReg(kArg3, kNotWide).GetReg()};
-
- int next_reg = 0;
- int next_arg = 0;
- if (skip_this) {
- next_reg++;
- next_arg++;
- }
- for (; (next_reg <= last_arg_reg) && (next_arg < info->num_arg_words); next_reg++) {
- RegLocation rl_arg = info->args[next_arg++];
- rl_arg = UpdateRawLoc(rl_arg);
- if (rl_arg.wide && (next_reg <= last_arg_reg - 1)) {
- RegStorage r_tmp(RegStorage::k64BitPair, arg_regs[next_reg], arg_regs[next_reg + 1]);
- LoadValueDirectWideFixed(rl_arg, r_tmp);
- next_reg++;
- next_arg++;
- } else {
- if (rl_arg.wide) {
- rl_arg = NarrowRegLoc(rl_arg);
- rl_arg.is_const = false;
- }
- LoadValueDirectFixed(rl_arg, RegStorage::Solo32(arg_regs[next_reg]));
- }
- call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
- direct_code, direct_method, type);
- }
- return call_state;
-}
-
-/*
- * Load up to 5 arguments, the first three of which will be in
- * kArg1 .. kArg3. On entry kArg0 contains the current method pointer,
- * and as part of the load sequence, it must be replaced with
- * the target method pointer. Note, this may also be called
- * for "range" variants if the number of arguments is 5 or fewer.
- */
-int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info,
- int call_state, LIR** pcrLabel, NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx, uintptr_t direct_code,
- uintptr_t direct_method, InvokeType type, bool skip_this) {
- RegLocation rl_arg;
-
- /* If no arguments, just return */
- if (info->num_arg_words == 0)
- return call_state;
-
- call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
- direct_code, direct_method, type);
-
- DCHECK_LE(info->num_arg_words, 5);
- if (info->num_arg_words > 3) {
- int32_t next_use = 3;
- // Detect special case of wide arg spanning arg3/arg4
- RegLocation rl_use0 = info->args[0];
- RegLocation rl_use1 = info->args[1];
- RegLocation rl_use2 = info->args[2];
- if (((!rl_use0.wide && !rl_use1.wide) || rl_use0.wide) && rl_use2.wide) {
- RegStorage reg;
- // Wide spans, we need the 2nd half of uses[2].
- rl_arg = UpdateLocWide(rl_use2);
- if (rl_arg.location == kLocPhysReg) {
- if (rl_arg.reg.IsPair()) {
- reg = rl_arg.reg.GetHigh();
- } else {
- RegisterInfo* reg_info = GetRegInfo(rl_arg.reg);
- reg_info = reg_info->FindMatchingView(RegisterInfo::kHighSingleStorageMask);
- if (reg_info == nullptr) {
- // NOTE: For hard float convention we won't split arguments across reg/mem.
- UNIMPLEMENTED(FATAL) << "Needs hard float api.";
- }
- reg = reg_info->GetReg();
- }
- } else {
- // kArg2 & rArg3 can safely be used here
- reg = TargetReg(kArg3, kNotWide);
- {
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- Load32Disp(TargetPtrReg(kSp), SRegOffset(rl_arg.s_reg_low) + 4, reg);
- }
- call_state = next_call_insn(cu_, info, call_state, target_method,
- vtable_idx, direct_code, direct_method, type);
- }
- {
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- Store32Disp(TargetPtrReg(kSp), (next_use + 1) * 4, reg);
- }
- call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
- direct_code, direct_method, type);
- next_use++;
- }
- // Loop through the rest
- while (next_use < info->num_arg_words) {
- RegStorage arg_reg;
- rl_arg = info->args[next_use];
- rl_arg = UpdateRawLoc(rl_arg);
- if (rl_arg.location == kLocPhysReg) {
- arg_reg = rl_arg.reg;
- } else {
- arg_reg = TargetReg(kArg2, rl_arg.wide ? kWide : kNotWide);
- if (rl_arg.wide) {
- LoadValueDirectWideFixed(rl_arg, arg_reg);
- } else {
- LoadValueDirectFixed(rl_arg, arg_reg);
- }
- call_state = next_call_insn(cu_, info, call_state, target_method,
- vtable_idx, direct_code, direct_method, type);
- }
- int outs_offset = (next_use + 1) * 4;
- {
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- if (rl_arg.wide) {
- StoreBaseDisp(TargetPtrReg(kSp), outs_offset, arg_reg, k64, kNotVolatile);
- next_use += 2;
- } else {
- Store32Disp(TargetPtrReg(kSp), outs_offset, arg_reg);
- next_use++;
- }
- }
- call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
- direct_code, direct_method, type);
- }
- }
-
- call_state = LoadArgRegs(info, call_state, next_call_insn,
- target_method, vtable_idx, direct_code, direct_method,
- type, skip_this);
-
- if (pcrLabel) {
- if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) {
- *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags);
- } else {
- *pcrLabel = nullptr;
- if (!(cu_->disable_opt & (1 << kNullCheckElimination)) &&
- (info->opt_flags & MIR_IGNORE_NULL_CHECK)) {
- return call_state;
- }
- // In lieu of generating a check for kArg1 being null, we need to
- // perform a load when doing implicit checks.
- GenImplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags);
- }
- }
- return call_state;
-}
-
// Default implementation of implicit null pointer check.
// Overridden by arch specific as necessary.
void Mir2Lir::GenImplicitNullCheck(RegStorage reg, int opt_flags) {
@@ -883,209 +648,196 @@
FreeTemp(tmp);
}
-
-/*
- * May have 0+ arguments (also used for jumbo). Note that
- * source virtual registers may be in physical registers, so may
- * need to be flushed to home location before copying. This
- * applies to arg3 and above (see below).
- *
- * Two general strategies:
- * If < 20 arguments
- * Pass args 3-18 using vldm/vstm block copy
- * Pass arg0, arg1 & arg2 in kArg1-kArg3
- * If 20+ arguments
- * Pass args arg19+ using memcpy block copy
- * Pass arg0, arg1 & arg2 in kArg1-kArg3
- *
+/**
+ * @brief Used to flush promoted registers if they are used as argument
+ * in an invocation.
+ * @param info the infromation about arguments for invocation.
+ * @param start the first argument we should start to look from.
*/
-int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state,
- LIR** pcrLabel, NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method,
- InvokeType type, bool skip_this) {
- // If we can treat it as non-range (Jumbo ops will use range form)
- if (info->num_arg_words <= 5)
- return GenDalvikArgsNoRange(info, call_state, pcrLabel,
- next_call_insn, target_method, vtable_idx,
- direct_code, direct_method, type, skip_this);
- /*
- * First load the non-register arguments. Both forms expect all
- * of the source arguments to be in their home frame location, so
- * scan the s_reg names and flush any that have been promoted to
- * frame backing storage.
- */
+void Mir2Lir::GenDalvikArgsFlushPromoted(CallInfo* info, int start) {
+ if (cu_->disable_opt & (1 << kPromoteRegs)) {
+ // This make sense only if promotion is enabled.
+ return;
+ }
+ ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
// Scan the rest of the args - if in phys_reg flush to memory
- for (int next_arg = 0; next_arg < info->num_arg_words;) {
+ for (int next_arg = start; next_arg < info->num_arg_words;) {
RegLocation loc = info->args[next_arg];
if (loc.wide) {
loc = UpdateLocWide(loc);
- if ((next_arg >= 2) && (loc.location == kLocPhysReg)) {
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+ if (loc.location == kLocPhysReg) {
StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k64, kNotVolatile);
}
next_arg += 2;
} else {
loc = UpdateLoc(loc);
- if ((next_arg >= 3) && (loc.location == kLocPhysReg)) {
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- Store32Disp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg);
+ if (loc.location == kLocPhysReg) {
+ if (loc.ref) {
+ StoreRefDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, kNotVolatile);
+ } else {
+ StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k32,
+ kNotVolatile);
+ }
}
next_arg++;
}
}
+}
- // The first 3 arguments are passed via registers.
- // TODO: For 64-bit, instead of hardcoding 4 for Method* size, we should either
- // get size of uintptr_t or size of object reference according to model being used.
- int outs_offset = 4 /* Method* */ + (3 * sizeof(uint32_t));
- int start_offset = SRegOffset(info->args[3].s_reg_low);
- int regs_left_to_pass_via_stack = info->num_arg_words - 3;
- DCHECK_GT(regs_left_to_pass_via_stack, 0);
+/**
+ * @brief Used to optimize the copying of VRs which are arguments of invocation.
+ * Please note that you should flush promoted registers first if you copy.
+ * If implementation does copying it may skip several of the first VRs but must copy
+ * till the end. Implementation must return the number of skipped VRs
+ * (it might be all VRs).
+ * @see GenDalvikArgsFlushPromoted
+ * @param info the information about arguments for invocation.
+ * @param first the first argument we should start to look from.
+ * @param count the number of remaining arguments we can handle.
+ * @return the number of arguments which we did not handle. Unhandled arguments
+ * must be attached to the first one.
+ */
+int Mir2Lir::GenDalvikArgsBulkCopy(CallInfo* info, int first, int count) {
+ // call is pretty expensive, let's use it if count is big.
+ if (count > 16) {
+ GenDalvikArgsFlushPromoted(info, first);
+ int start_offset = SRegOffset(info->args[first].s_reg_low);
+ int outs_offset = StackVisitor::GetOutVROffset(first, cu_->instruction_set);
- if (cu_->instruction_set == kThumb2 && regs_left_to_pass_via_stack <= 16) {
- // Use vldm/vstm pair using kArg3 as a temp
- call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
- direct_code, direct_method, type);
- OpRegRegImm(kOpAdd, TargetReg(kArg3, kRef), TargetPtrReg(kSp), start_offset);
- LIR* ld = nullptr;
- {
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- ld = OpVldm(TargetReg(kArg3, kRef), regs_left_to_pass_via_stack);
- }
- // TUNING: loosen barrier
- ld->u.m.def_mask = &kEncodeAll;
- call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
- direct_code, direct_method, type);
- OpRegRegImm(kOpAdd, TargetReg(kArg3, kRef), TargetPtrReg(kSp), 4 /* Method* */ + (3 * 4));
- call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
- direct_code, direct_method, type);
- LIR* st = nullptr;
- {
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- st = OpVstm(TargetReg(kArg3, kRef), regs_left_to_pass_via_stack);
- }
- st->u.m.def_mask = &kEncodeAll;
- call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
- direct_code, direct_method, type);
- } else if (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) {
- int current_src_offset = start_offset;
- int current_dest_offset = outs_offset;
-
- // Only davik regs are accessed in this loop; no next_call_insn() calls.
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- while (regs_left_to_pass_via_stack > 0) {
- // This is based on the knowledge that the stack itself is 16-byte aligned.
- bool src_is_16b_aligned = (current_src_offset & 0xF) == 0;
- bool dest_is_16b_aligned = (current_dest_offset & 0xF) == 0;
- size_t bytes_to_move;
-
- /*
- * The amount to move defaults to 32-bit. If there are 4 registers left to move, then do a
- * a 128-bit move because we won't get the chance to try to aligned. If there are more than
- * 4 registers left to move, consider doing a 128-bit only if either src or dest are aligned.
- * We do this because we could potentially do a smaller move to align.
- */
- if (regs_left_to_pass_via_stack == 4 ||
- (regs_left_to_pass_via_stack > 4 && (src_is_16b_aligned || dest_is_16b_aligned))) {
- // Moving 128-bits via xmm register.
- bytes_to_move = sizeof(uint32_t) * 4;
-
- // Allocate a free xmm temp. Since we are working through the calling sequence,
- // we expect to have an xmm temporary available. AllocTempDouble will abort if
- // there are no free registers.
- RegStorage temp = AllocTempDouble();
-
- LIR* ld1 = nullptr;
- LIR* ld2 = nullptr;
- LIR* st1 = nullptr;
- LIR* st2 = nullptr;
-
- /*
- * The logic is similar for both loads and stores. If we have 16-byte alignment,
- * do an aligned move. If we have 8-byte alignment, then do the move in two
- * parts. This approach prevents possible cache line splits. Finally, fall back
- * to doing an unaligned move. In most cases we likely won't split the cache
- * line but we cannot prove it and thus take a conservative approach.
- */
- bool src_is_8b_aligned = (current_src_offset & 0x7) == 0;
- bool dest_is_8b_aligned = (current_dest_offset & 0x7) == 0;
-
- if (src_is_16b_aligned) {
- ld1 = OpMovRegMem(temp, TargetPtrReg(kSp), current_src_offset, kMovA128FP);
- } else if (src_is_8b_aligned) {
- ld1 = OpMovRegMem(temp, TargetPtrReg(kSp), current_src_offset, kMovLo128FP);
- ld2 = OpMovRegMem(temp, TargetPtrReg(kSp), current_src_offset + (bytes_to_move >> 1),
- kMovHi128FP);
- } else {
- ld1 = OpMovRegMem(temp, TargetPtrReg(kSp), current_src_offset, kMovU128FP);
- }
-
- if (dest_is_16b_aligned) {
- st1 = OpMovMemReg(TargetPtrReg(kSp), current_dest_offset, temp, kMovA128FP);
- } else if (dest_is_8b_aligned) {
- st1 = OpMovMemReg(TargetPtrReg(kSp), current_dest_offset, temp, kMovLo128FP);
- st2 = OpMovMemReg(TargetPtrReg(kSp), current_dest_offset + (bytes_to_move >> 1),
- temp, kMovHi128FP);
- } else {
- st1 = OpMovMemReg(TargetPtrReg(kSp), current_dest_offset, temp, kMovU128FP);
- }
-
- // TODO If we could keep track of aliasing information for memory accesses that are wider
- // than 64-bit, we wouldn't need to set up a barrier.
- if (ld1 != nullptr) {
- if (ld2 != nullptr) {
- // For 64-bit load we can actually set up the aliasing information.
- AnnotateDalvikRegAccess(ld1, current_src_offset >> 2, true, true);
- AnnotateDalvikRegAccess(ld2, (current_src_offset + (bytes_to_move >> 1)) >> 2, true,
- true);
- } else {
- // Set barrier for 128-bit load.
- ld1->u.m.def_mask = &kEncodeAll;
- }
- }
- if (st1 != nullptr) {
- if (st2 != nullptr) {
- // For 64-bit store we can actually set up the aliasing information.
- AnnotateDalvikRegAccess(st1, current_dest_offset >> 2, false, true);
- AnnotateDalvikRegAccess(st2, (current_dest_offset + (bytes_to_move >> 1)) >> 2, false,
- true);
- } else {
- // Set barrier for 128-bit store.
- st1->u.m.def_mask = &kEncodeAll;
- }
- }
-
- // Free the temporary used for the data movement.
- FreeTemp(temp);
- } else {
- // Moving 32-bits via general purpose register.
- bytes_to_move = sizeof(uint32_t);
-
- // Instead of allocating a new temp, simply reuse one of the registers being used
- // for argument passing.
- RegStorage temp = TargetReg(kArg3, kNotWide);
-
- // Now load the argument VR and store to the outs.
- Load32Disp(TargetPtrReg(kSp), current_src_offset, temp);
- Store32Disp(TargetPtrReg(kSp), current_dest_offset, temp);
- }
-
- current_src_offset += bytes_to_move;
- current_dest_offset += bytes_to_move;
- regs_left_to_pass_via_stack -= (bytes_to_move >> 2);
- }
- } else {
- // Generate memcpy
OpRegRegImm(kOpAdd, TargetReg(kArg0, kRef), TargetPtrReg(kSp), outs_offset);
OpRegRegImm(kOpAdd, TargetReg(kArg1, kRef), TargetPtrReg(kSp), start_offset);
CallRuntimeHelperRegRegImm(kQuickMemcpy, TargetReg(kArg0, kRef), TargetReg(kArg1, kRef),
- (info->num_arg_words - 3) * 4, false);
+ count * 4, false);
+ count = 0;
+ }
+ return count;
+}
+
+int Mir2Lir::GenDalvikArgs(CallInfo* info, int call_state,
+ LIR** pcrLabel, NextCallInsn next_call_insn,
+ const MethodReference& target_method,
+ uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method,
+ InvokeType type, bool skip_this) {
+ // If no arguments, just return.
+ if (info->num_arg_words == 0)
+ return call_state;
+
+ const int start_index = skip_this ? 1 : 0;
+
+ // Get architecture dependent mapping between output VRs and physical registers
+ // basing on shorty of method to call.
+ InToRegStorageMapping in_to_reg_storage_mapping(arena_);
+ {
+ const char* target_shorty = mir_graph_->GetShortyFromMethodReference(target_method);
+ ShortyIterator shorty_iterator(target_shorty, type == kStatic);
+ in_to_reg_storage_mapping.Initialize(&shorty_iterator, GetResetedInToRegStorageMapper());
}
- call_state = LoadArgRegs(info, call_state, next_call_insn,
- target_method, vtable_idx, direct_code, direct_method,
- type, skip_this);
+ int stack_map_start = std::max(in_to_reg_storage_mapping.GetMaxMappedIn() + 1, start_index);
+ if ((stack_map_start < info->num_arg_words) && info->args[stack_map_start].high_word) {
+ // It is possible that the last mapped reg is 32 bit while arg is 64-bit.
+ // It will be handled together with low part mapped to register.
+ stack_map_start++;
+ }
+ int regs_left_to_pass_via_stack = info->num_arg_words - stack_map_start;
+
+ // If it is a range case we can try to copy remaining VRs (not mapped to physical registers)
+ // using more optimal algorithm.
+ if (info->is_range && regs_left_to_pass_via_stack > 1) {
+ regs_left_to_pass_via_stack = GenDalvikArgsBulkCopy(info, stack_map_start,
+ regs_left_to_pass_via_stack);
+ }
+
+ // Now handle any remaining VRs mapped to stack.
+ if (in_to_reg_storage_mapping.HasArgumentsOnStack()) {
+ // Two temps but do not use kArg1, it might be this which we can skip.
+ // Separate single and wide - it can give some advantage.
+ RegStorage regRef = TargetReg(kArg3, kRef);
+ RegStorage regSingle = TargetReg(kArg3, kNotWide);
+ RegStorage regWide = TargetReg(kArg2, kWide);
+ for (int i = start_index;
+ i < stack_map_start + regs_left_to_pass_via_stack; i++) {
+ RegLocation rl_arg = info->args[i];
+ rl_arg = UpdateRawLoc(rl_arg);
+ RegStorage reg = in_to_reg_storage_mapping.Get(i);
+ if (!reg.Valid()) {
+ int out_offset = StackVisitor::GetOutVROffset(i, cu_->instruction_set);
+ {
+ ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+ if (rl_arg.wide) {
+ if (rl_arg.location == kLocPhysReg) {
+ StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k64, kNotVolatile);
+ } else {
+ LoadValueDirectWideFixed(rl_arg, regWide);
+ StoreBaseDisp(TargetPtrReg(kSp), out_offset, regWide, k64, kNotVolatile);
+ }
+ } else {
+ if (rl_arg.location == kLocPhysReg) {
+ if (rl_arg.ref) {
+ StoreRefDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, kNotVolatile);
+ } else {
+ StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k32, kNotVolatile);
+ }
+ } else {
+ if (rl_arg.ref) {
+ LoadValueDirectFixed(rl_arg, regRef);
+ StoreRefDisp(TargetPtrReg(kSp), out_offset, regRef, kNotVolatile);
+ } else {
+ LoadValueDirectFixed(rl_arg, regSingle);
+ StoreBaseDisp(TargetPtrReg(kSp), out_offset, regSingle, k32, kNotVolatile);
+ }
+ }
+ }
+ }
+ call_state = next_call_insn(cu_, info, call_state, target_method,
+ vtable_idx, direct_code, direct_method, type);
+ }
+ if (rl_arg.wide) {
+ i++;
+ }
+ }
+ }
+
+ // Finish with VRs mapped to physical registers.
+ for (int i = start_index; i < stack_map_start; i++) {
+ RegLocation rl_arg = info->args[i];
+ rl_arg = UpdateRawLoc(rl_arg);
+ RegStorage reg = in_to_reg_storage_mapping.Get(i);
+ if (reg.Valid()) {
+ if (rl_arg.wide) {
+ // if reg is not 64-bit (it is half of 64-bit) then handle it separately.
+ if (!reg.Is64Bit()) {
+ ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+ if (rl_arg.location == kLocPhysReg) {
+ int out_offset = StackVisitor::GetOutVROffset(i, cu_->instruction_set);
+ // Dump it to memory.
+ StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k64, kNotVolatile);
+ LoadBaseDisp(TargetPtrReg(kSp), out_offset, reg, k32, kNotVolatile);
+ } else {
+ int high_offset = StackVisitor::GetOutVROffset(i + 1, cu_->instruction_set);
+ // First, use target reg for high part.
+ LoadBaseDisp(TargetPtrReg(kSp), SRegOffset(rl_arg.s_reg_low + 1), reg, k32,
+ kNotVolatile);
+ StoreBaseDisp(TargetPtrReg(kSp), high_offset, reg, k32, kNotVolatile);
+ // Now, use target reg for low part.
+ LoadBaseDisp(TargetPtrReg(kSp), SRegOffset(rl_arg.s_reg_low), reg, k32, kNotVolatile);
+ int low_offset = StackVisitor::GetOutVROffset(i, cu_->instruction_set);
+ // And store it to the expected memory location.
+ StoreBaseDisp(TargetPtrReg(kSp), low_offset, reg, k32, kNotVolatile);
+ }
+ } else {
+ LoadValueDirectWideFixed(rl_arg, reg);
+ }
+ } else {
+ LoadValueDirectFixed(rl_arg, reg);
+ }
+ call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
+ direct_code, direct_method, type);
+ }
+ if (rl_arg.wide) {
+ i++;
+ }
+ }
call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
direct_code, direct_method, type);
@@ -1094,18 +846,20 @@
*pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags);
} else {
*pcrLabel = nullptr;
- if (!(cu_->disable_opt & (1 << kNullCheckElimination)) &&
- (info->opt_flags & MIR_IGNORE_NULL_CHECK)) {
- return call_state;
- }
- // In lieu of generating a check for kArg1 being null, we need to
- // perform a load when doing implicit checks.
GenImplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags);
}
}
return call_state;
}
+RegStorage Mir2Lir::GetArgMappingToPhysicalReg(int arg_num) {
+ if (!in_to_reg_storage_mapping_.IsInitialized()) {
+ ShortyIterator shorty_iterator(cu_->shorty, cu_->invoke_type == kStatic);
+ in_to_reg_storage_mapping_.Initialize(&shorty_iterator, GetResetedInToRegStorageMapper());
+ }
+ return in_to_reg_storage_mapping_.Get(arg_num);
+}
+
RegLocation Mir2Lir::InlineTarget(CallInfo* info) {
RegLocation res;
if (info->result.location == kLocInvalid) {
@@ -1167,8 +921,8 @@
RegStorage reg_slow_path = AllocTemp();
RegStorage reg_disabled = AllocTemp();
- Load8Disp(reg_class, slow_path_flag_offset, reg_slow_path);
- Load8Disp(reg_class, disable_flag_offset, reg_disabled);
+ LoadBaseDisp(reg_class, slow_path_flag_offset, reg_slow_path, kSignedByte, kNotVolatile);
+ LoadBaseDisp(reg_class, disable_flag_offset, reg_disabled, kSignedByte, kNotVolatile);
FreeTemp(reg_class);
LIR* or_inst = OpRegRegReg(kOpOr, reg_slow_path, reg_slow_path, reg_disabled);
FreeTemp(reg_disabled);
@@ -1200,10 +954,6 @@
}
bool Mir2Lir::GenInlinedCharAt(CallInfo* info) {
- if (cu_->instruction_set == kMips) {
- // TODO - add Mips implementation
- return false;
- }
// Location of reference to data array
int value_offset = mirror::String::ValueOffset().Int32Value();
// Location of count
@@ -1301,9 +1051,13 @@
// TODO - add Mips implementation.
return false;
}
+ RegLocation rl_dest = IsWide(size) ? InlineTargetWide(info) : InlineTarget(info); // result reg
+ if (rl_dest.s_reg_low == INVALID_SREG) {
+ // Result is unused, the code is dead. Inlining successful, no code generated.
+ return true;
+ }
RegLocation rl_src_i = info->args[0];
RegLocation rl_i = IsWide(size) ? LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
- RegLocation rl_dest = IsWide(size) ? InlineTargetWide(info) : InlineTarget(info); // result reg
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
if (IsWide(size)) {
if (cu_->instruction_set == kArm64 || cu_->instruction_set == kX86_64) {
@@ -1333,13 +1087,13 @@
}
bool Mir2Lir::GenInlinedAbsInt(CallInfo* info) {
- if (cu_->instruction_set == kMips) {
- // TODO - add Mips implementation
- return false;
+ RegLocation rl_dest = InlineTarget(info);
+ if (rl_dest.s_reg_low == INVALID_SREG) {
+ // Result is unused, the code is dead. Inlining successful, no code generated.
+ return true;
}
RegLocation rl_src = info->args[0];
rl_src = LoadValue(rl_src, kCoreReg);
- RegLocation rl_dest = InlineTarget(info);
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
RegStorage sign_reg = AllocTemp();
// abs(x) = y<=x>>31, (x+y)^y.
@@ -1351,13 +1105,13 @@
}
bool Mir2Lir::GenInlinedAbsLong(CallInfo* info) {
- if (cu_->instruction_set == kMips) {
- // TODO - add Mips implementation
- return false;
+ RegLocation rl_dest = InlineTargetWide(info);
+ if (rl_dest.s_reg_low == INVALID_SREG) {
+ // Result is unused, the code is dead. Inlining successful, no code generated.
+ return true;
}
RegLocation rl_src = info->args[0];
rl_src = LoadValueWide(rl_src, kCoreReg);
- RegLocation rl_dest = InlineTargetWide(info);
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
// If on x86 or if we would clobber a register needed later, just copy the source first.
@@ -1432,8 +1186,12 @@
// TODO - add Mips implementation
return false;
}
- RegLocation rl_src = info->args[0];
RegLocation rl_dest = InlineTarget(info);
+ if (rl_dest.s_reg_low == INVALID_SREG) {
+ // Result is unused, the code is dead. Inlining successful, no code generated.
+ return true;
+ }
+ RegLocation rl_src = info->args[0];
StoreValue(rl_dest, rl_src);
return true;
}
@@ -1443,8 +1201,12 @@
// TODO - add Mips implementation
return false;
}
- RegLocation rl_src = info->args[0];
RegLocation rl_dest = InlineTargetWide(info);
+ if (rl_dest.s_reg_low == INVALID_SREG) {
+ // Result is unused, the code is dead. Inlining successful, no code generated.
+ return true;
+ }
+ RegLocation rl_src = info->args[0];
StoreValueWide(rl_dest, rl_src);
return true;
}
@@ -1460,14 +1222,6 @@
* otherwise bails to standard library code.
*/
bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
- if (cu_->instruction_set == kMips) {
- // TODO - add Mips implementation
- return false;
- }
- if (cu_->instruction_set == kX86_64) {
- // TODO - add kX86_64 implementation
- return false;
- }
RegLocation rl_obj = info->args[0];
RegLocation rl_char = info->args[1];
if (rl_char.is_const && (mir_graph_->ConstantValue(rl_char) & ~0xFFFF) != 0) {
@@ -1556,23 +1310,13 @@
RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true);
- switch (cu_->instruction_set) {
- case kArm:
- // Fall-through.
- case kThumb2:
- // Fall-through.
- case kMips:
- Load32Disp(TargetPtrReg(kSelf), Thread::PeerOffset<4>().Int32Value(), rl_result.reg);
- break;
-
- case kArm64:
- LoadRefDisp(TargetPtrReg(kSelf), Thread::PeerOffset<8>().Int32Value(), rl_result.reg,
- kNotVolatile);
- break;
-
- default:
- LOG(FATAL) << "Unexpected isa " << cu_->instruction_set;
+ if (Is64BitInstructionSet(cu_->instruction_set)) {
+ LoadRefDisp(TargetPtrReg(kSelf), Thread::PeerOffset<8>().Int32Value(), rl_result.reg,
+ kNotVolatile);
+ } else {
+ Load32Disp(TargetPtrReg(kSelf), Thread::PeerOffset<4>().Int32Value(), rl_result.reg);
}
+
StoreValue(rl_dest, rl_result);
return true;
}
@@ -1719,17 +1463,10 @@
skip_this = fast_path;
}
MethodReference target_method = method_info.GetTargetMethod();
- if (!info->is_range) {
- call_state = GenDalvikArgsNoRange(info, call_state, p_null_ck,
- next_call_insn, target_method, method_info.VTableIndex(),
- method_info.DirectCode(), method_info.DirectMethod(),
- original_type, skip_this);
- } else {
- call_state = GenDalvikArgsRange(info, call_state, p_null_ck,
- next_call_insn, target_method, method_info.VTableIndex(),
- method_info.DirectCode(), method_info.DirectMethod(),
- original_type, skip_this);
- }
+ call_state = GenDalvikArgs(info, call_state, p_null_ck,
+ next_call_insn, target_method, method_info.VTableIndex(),
+ method_info.DirectCode(), method_info.DirectMethod(),
+ original_type, skip_this);
// Finish up any of the call sequence not interleaved in arg loading
while (call_state >= 0) {
call_state = next_call_insn(cu_, info, call_state, target_method, method_info.VTableIndex(),
@@ -1738,7 +1475,7 @@
LIR* call_insn = GenCallInsn(method_info);
MarkSafepointPC(call_insn);
- ClobberCallerSave();
+ FreeCallTemps();
if (info->result.location != kLocInvalid) {
// We have a following MOVE_RESULT - do it now.
if (info->result.wide) {
@@ -1751,16 +1488,4 @@
}
}
-NextCallInsn Mir2Lir::GetNextSDCallInsn() {
- return NextSDCallInsn;
-}
-
-LIR* Mir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info) {
- UNUSED(method_info);
- DCHECK(cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64 &&
- cu_->instruction_set != kThumb2 && cu_->instruction_set != kArm &&
- cu_->instruction_set != kArm64);
- return OpReg(kOpBlx, TargetPtrReg(kInvokeTgt));
-}
-
} // namespace art
diff --git a/compiler/dex/quick/gen_loadstore.cc b/compiler/dex/quick/gen_loadstore.cc
index d314601..9f36e35 100644
--- a/compiler/dex/quick/gen_loadstore.cc
+++ b/compiler/dex/quick/gen_loadstore.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
+#include "mir_to_lir-inl.h"
+
#include "dex/compiler_ir.h"
-#include "dex/compiler_internals.h"
-#include "dex/quick/mir_to_lir-inl.h"
+#include "dex/mir_graph.h"
#include "invoke_type.h"
namespace art {
diff --git a/compiler/dex/quick/local_optimizations.cc b/compiler/dex/quick/local_optimizations.cc
index e0f4691..e573899 100644
--- a/compiler/dex/quick/local_optimizations.cc
+++ b/compiler/dex/quick/local_optimizations.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#include "dex/compiler_internals.h"
#include "dex/quick/mir_to_lir-inl.h"
+#include "base/logging.h"
+
namespace art {
#define DEBUG_OPT(X)
diff --git a/compiler/dex/quick/mips/assemble_mips.cc b/compiler/dex/quick/mips/assemble_mips.cc
index 0d1d9bf..5c98b10 100644
--- a/compiler/dex/quick/mips/assemble_mips.cc
+++ b/compiler/dex/quick/mips/assemble_mips.cc
@@ -16,6 +16,8 @@
#include "codegen_mips.h"
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "mips_lir.h"
@@ -434,7 +436,7 @@
* anchor:
* ori rAT, rAT, ((target-anchor) & 0xffff)
* addu rAT, rAT, rRA
- * jr rAT
+ * jalr rZERO, rAT
* hop:
*
* Orig unconditional branch
@@ -448,7 +450,7 @@
* anchor:
* ori rAT, rAT, ((target-anchor) & 0xffff)
* addu rAT, rAT, rRA
- * jr rAT
+ * jalr rZERO, rAT
*
*
* NOTE: An out-of-range bal isn't supported because it should
@@ -482,7 +484,7 @@
if (!unconditional) {
hop_target = RawLIR(dalvik_offset, kPseudoTargetLabel);
LIR* hop_branch = RawLIR(dalvik_offset, opcode, lir->operands[0],
- lir->operands[1], 0, 0, 0, hop_target);
+ lir->operands[1], 0, 0, 0, hop_target);
InsertLIRBefore(lir, hop_branch);
}
LIR* curr_pc = RawLIR(dalvik_offset, kMipsCurrPC);
@@ -497,8 +499,8 @@
InsertLIRBefore(lir, delta_lo);
LIR* addu = RawLIR(dalvik_offset, kMipsAddu, rAT, rAT, rRA);
InsertLIRBefore(lir, addu);
- LIR* jr = RawLIR(dalvik_offset, kMipsJr, rAT);
- InsertLIRBefore(lir, jr);
+ LIR* jalr = RawLIR(dalvik_offset, kMipsJalr, rZERO, rAT);
+ InsertLIRBefore(lir, jalr);
if (!unconditional) {
InsertLIRBefore(lir, hop_target);
}
diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc
index 3bb81bf..0719b52 100644
--- a/compiler/dex/quick/mips/call_mips.cc
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -18,10 +18,14 @@
#include "codegen_mips.h"
+#include "base/logging.h"
+#include "dex/mir_graph.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "gc/accounting/card_table.h"
#include "mips_lir.h"
+#include "mirror/art_method.h"
+#include "mirror/object_array-inl.h"
namespace art {
@@ -58,23 +62,19 @@
* bne r_val, r_key, loop
* lw r_disp, -4(r_base)
* addu rRA, r_disp
- * jr rRA
+ * jalr rZERO, rRA
* done:
*
*/
void MipsMir2Lir::GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
- if (cu_->verbose) {
- DumpSparseSwitchTable(table);
- }
// Add the table to the list - we'll process it later
SwitchTable* tab_rec =
static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
+ tab_rec->switch_mir = mir;
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
int elements = table[1];
- tab_rec->targets =
- static_cast<LIR**>(arena_->Alloc(elements * sizeof(LIR*), kArenaAllocLIR));
switch_tables_.push_back(tab_rec);
// The table is composed of 8-byte key/disp pairs
@@ -136,22 +136,18 @@
* bound check -> done
* lw r_disp, [rRA, r_val]
* addu rRA, r_disp
- * jr rRA
+ * jalr rZERO, rRA
* done:
*/
void MipsMir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
- if (cu_->verbose) {
- DumpPackedSwitchTable(table);
- }
// Add the table to the list - we'll process it later
SwitchTable* tab_rec =
static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
+ tab_rec->switch_mir = mir;
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
int size = table[1];
- tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*),
- kArenaAllocLIR));
switch_tables_.push_back(tab_rec);
// Get the switch value
@@ -319,4 +315,84 @@
OpReg(kOpBx, rs_rRA);
}
+/*
+ * Bit of a hack here - in the absence of a real scheduling pass,
+ * emit the next instruction in static & direct invoke sequences.
+ */
+static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSED,
+ int state, const MethodReference& target_method,
+ uint32_t,
+ uintptr_t direct_code, uintptr_t direct_method,
+ InvokeType type) {
+ Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
+ if (direct_code != 0 && direct_method != 0) {
+ switch (state) {
+ case 0: // Get the current Method* [sets kArg0]
+ if (direct_code != static_cast<uintptr_t>(-1)) {
+ cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
+ } else {
+ cg->LoadCodeAddress(target_method, type, kInvokeTgt);
+ }
+ if (direct_method != static_cast<uintptr_t>(-1)) {
+ cg->LoadConstant(cg->TargetReg(kArg0, kRef), direct_method);
+ } else {
+ cg->LoadMethodAddress(target_method, type, kArg0);
+ }
+ break;
+ default:
+ return -1;
+ }
+ } else {
+ RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
+ switch (state) {
+ case 0: // Get the current Method* [sets kArg0]
+ // TUNING: we can save a reg copy if Method* has been promoted.
+ cg->LoadCurrMethodDirect(arg0_ref);
+ break;
+ case 1: // Get method->dex_cache_resolved_methods_
+ cg->LoadRefDisp(arg0_ref,
+ mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
+ arg0_ref,
+ kNotVolatile);
+ // Set up direct code if known.
+ if (direct_code != 0) {
+ if (direct_code != static_cast<uintptr_t>(-1)) {
+ cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
+ } else {
+ CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds());
+ cg->LoadCodeAddress(target_method, type, kInvokeTgt);
+ }
+ }
+ break;
+ case 2: // Grab target method*
+ CHECK_EQ(cu->dex_file, target_method.dex_file);
+ cg->LoadRefDisp(arg0_ref,
+ mirror::ObjectArray<mirror::Object>::
+ OffsetOfElement(target_method.dex_method_index).Int32Value(),
+ arg0_ref,
+ kNotVolatile);
+ break;
+ case 3: // Grab the code from the method*
+ if (direct_code == 0) {
+ int32_t offset = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ InstructionSetPointerSize(cu->instruction_set)).Int32Value();
+ // Get the compiled code address [use *alt_from or kArg0, set kInvokeTgt]
+ cg->LoadWordDisp(arg0_ref, offset, cg->TargetPtrReg(kInvokeTgt));
+ }
+ break;
+ default:
+ return -1;
+ }
+ }
+ return state + 1;
+}
+
+NextCallInsn MipsMir2Lir::GetNextSDCallInsn() {
+ return NextSDCallInsn;
+}
+
+LIR* MipsMir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info ATTRIBUTE_UNUSED) {
+ return OpReg(kOpBlx, TargetPtrReg(kInvokeTgt));
+}
+
} // namespace art
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index e08846c..a37fe40 100644
--- a/compiler/dex/quick/mips/codegen_mips.h
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -17,13 +17,34 @@
#ifndef ART_COMPILER_DEX_QUICK_MIPS_CODEGEN_MIPS_H_
#define ART_COMPILER_DEX_QUICK_MIPS_CODEGEN_MIPS_H_
-#include "dex/compiler_internals.h"
#include "dex/quick/mir_to_lir.h"
#include "mips_lir.h"
namespace art {
+struct CompilationUnit;
+
class MipsMir2Lir FINAL : public Mir2Lir {
+ protected:
+ class InToRegStorageMipsMapper : public InToRegStorageMapper {
+ public:
+ explicit InToRegStorageMipsMapper(Mir2Lir* m2l) : m2l_(m2l), cur_core_reg_(0) {}
+ virtual RegStorage GetNextReg(ShortyArg arg);
+ virtual void Reset() OVERRIDE {
+ cur_core_reg_ = 0;
+ }
+ protected:
+ Mir2Lir* m2l_;
+ private:
+ size_t cur_core_reg_;
+ };
+
+ InToRegStorageMipsMapper in_to_reg_storage_mips_mapper_;
+ InToRegStorageMapper* GetResetedInToRegStorageMapper() OVERRIDE {
+ in_to_reg_storage_mips_mapper_.Reset();
+ return &in_to_reg_storage_mips_mapper_;
+ }
+
public:
MipsMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
@@ -56,7 +77,6 @@
// Required for target - register utilities.
RegStorage Solo64ToPair64(RegStorage reg);
RegStorage TargetReg(SpecialTargetRegister reg);
- RegStorage GetArgMappingToPhysicalReg(int arg_num);
RegLocation GetReturnAlt();
RegLocation GetReturnWideAlt();
RegLocation LocCReturn();
@@ -187,6 +207,29 @@
LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) OVERRIDE;
+ RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2, bool is_div, int flags) OVERRIDE;
+ RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div)
+ OVERRIDE;
+
+ NextCallInsn GetNextSDCallInsn() OVERRIDE;
+ LIR* GenCallInsn(const MirMethodLoweringInfo& method_info) OVERRIDE;
+
+ // Unimplemented intrinsics.
+ bool GenInlinedCharAt(CallInfo* info ATTRIBUTE_UNUSED) OVERRIDE {
+ return false;
+ }
+ bool GenInlinedAbsInt(CallInfo* info ATTRIBUTE_UNUSED) OVERRIDE {
+ return false;
+ }
+ bool GenInlinedAbsLong(CallInfo* info ATTRIBUTE_UNUSED) OVERRIDE {
+ return false;
+ }
+ bool GenInlinedIndexOf(CallInfo* info ATTRIBUTE_UNUSED, bool zero_based ATTRIBUTE_UNUSED)
+ OVERRIDE {
+ return false;
+ }
+
private:
void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
void GenAddLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
@@ -195,9 +238,6 @@
RegLocation rl_src2);
void ConvertShortToLongBranch(LIR* lir);
- RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
- RegLocation rl_src2, bool is_div, int flags) OVERRIDE;
- RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) OVERRIDE;
};
} // namespace art
diff --git a/compiler/dex/quick/mips/fp_mips.cc b/compiler/dex/quick/mips/fp_mips.cc
index 495d85e..d7ed7ac 100644
--- a/compiler/dex/quick/mips/fp_mips.cc
+++ b/compiler/dex/quick/mips/fp_mips.cc
@@ -16,6 +16,7 @@
#include "codegen_mips.h"
+#include "base/logging.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "mips_lir.h"
diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc
index 0778c3b..17ac629 100644
--- a/compiler/dex/quick/mips/int_mips.cc
+++ b/compiler/dex/quick/mips/int_mips.cc
@@ -18,6 +18,8 @@
#include "codegen_mips.h"
+#include "base/logging.h"
+#include "dex/mir_graph.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "dex/reg_storage_eq.h"
#include "entrypoints/quick/quick_entrypoints.h"
@@ -172,7 +174,7 @@
if (r_dest.IsFloat() || r_src.IsFloat())
return OpFpRegCopy(r_dest, r_src);
LIR* res = RawLIR(current_dalvik_offset_, kMipsMove,
- r_dest.GetReg(), r_src.GetReg());
+ r_dest.GetReg(), r_src.GetReg());
if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
res->flags.is_nop = true;
}
@@ -194,7 +196,7 @@
if (src_fp) {
OpRegCopy(r_dest, r_src);
} else {
- /* note the operands are swapped for the mtc1 instr */
+ /* note the operands are swapped for the mtc1 instr */
NewLIR2(kMipsMtc1, r_src.GetLowReg(), r_dest.GetLowReg());
NewLIR2(kMipsMtc1, r_src.GetHighReg(), r_dest.GetHighReg());
}
@@ -240,7 +242,7 @@
}
RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegStorage reg1, RegStorage reg2,
- bool is_div) {
+ bool is_div) {
NewLIR2(kMipsDiv, reg1.GetReg(), reg2.GetReg());
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
if (is_div) {
@@ -252,7 +254,7 @@
}
RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit,
- bool is_div) {
+ bool is_div) {
RegStorage t_reg = AllocTemp();
NewLIR3(kMipsAddiu, t_reg.GetReg(), rZERO, lit);
NewLIR2(kMipsDiv, reg1.GetReg(), t_reg.GetReg());
@@ -501,7 +503,7 @@
* Generate array load
*/
void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_dest, int scale) {
+ RegLocation rl_index, RegLocation rl_dest, int scale) {
RegisterClass reg_class = RegClassBySize(size);
int len_offset = mirror::Array::LengthOffset().Int32Value();
int data_offset;
@@ -570,7 +572,7 @@
*
*/
void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
+ RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
RegisterClass reg_class = RegClassBySize(size);
int len_offset = mirror::Array::LengthOffset().Int32Value();
int data_offset;
@@ -632,7 +634,7 @@
} else {
rl_src = LoadValue(rl_src, reg_class);
if (needs_range_check) {
- GenArrayBoundsCheck(rl_index.reg, reg_len);
+ GenArrayBoundsCheck(rl_index.reg, reg_len);
FreeTemp(reg_len);
}
StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size);
diff --git a/compiler/dex/quick/mips/mips_lir.h b/compiler/dex/quick/mips/mips_lir.h
index 3df8f2e..66e3894 100644
--- a/compiler/dex/quick/mips/mips_lir.h
+++ b/compiler/dex/quick/mips/mips_lir.h
@@ -17,7 +17,8 @@
#ifndef ART_COMPILER_DEX_QUICK_MIPS_MIPS_LIR_H_
#define ART_COMPILER_DEX_QUICK_MIPS_MIPS_LIR_H_
-#include "dex/compiler_internals.h"
+#include "dex/reg_location.h"
+#include "dex/reg_storage.h"
namespace art {
diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc
index 185112d..8574ffd 100644
--- a/compiler/dex/quick/mips/target_mips.cc
+++ b/compiler/dex/quick/mips/target_mips.cc
@@ -22,8 +22,10 @@
#include "arch/mips/instruction_set_features_mips.h"
#include "backend_mips.h"
-#include "dex/compiler_internals.h"
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
#include "dex/quick/mir_to_lir-inl.h"
+#include "driver/compiler_driver.h"
#include "mips_lir.h"
namespace art {
@@ -89,9 +91,9 @@
// Convert k64BitSolo into k64BitPair
RegStorage MipsMir2Lir::Solo64ToPair64(RegStorage reg) {
- DCHECK(reg.IsDouble());
- int reg_num = (reg.GetRegNum() & ~1) | RegStorage::kFloatingPoint;
- return RegStorage(RegStorage::k64BitPair, reg_num, reg_num + 1);
+ DCHECK(reg.IsDouble());
+ int reg_num = (reg.GetRegNum() & ~1) | RegStorage::kFloatingPoint;
+ return RegStorage(RegStorage::k64BitPair, reg_num, reg_num + 1);
}
// Return a target-dependent special register.
@@ -122,18 +124,20 @@
return res_reg;
}
-RegStorage MipsMir2Lir::GetArgMappingToPhysicalReg(int arg_num) {
- // For the 32-bit internal ABI, the first 3 arguments are passed in registers.
- switch (arg_num) {
- case 0:
- return rs_rMIPS_ARG1;
- case 1:
- return rs_rMIPS_ARG2;
- case 2:
- return rs_rMIPS_ARG3;
- default:
- return RegStorage::InvalidReg();
+RegStorage MipsMir2Lir::InToRegStorageMipsMapper::GetNextReg(ShortyArg arg) {
+ const SpecialTargetRegister coreArgMappingToPhysicalReg[] = {kArg1, kArg2, kArg3};
+ const size_t coreArgMappingToPhysicalRegSize = arraysize(coreArgMappingToPhysicalReg);
+
+ RegStorage result = RegStorage::InvalidReg();
+ if (cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
+ result = m2l_->TargetReg(coreArgMappingToPhysicalReg[cur_core_reg_++],
+ arg.IsRef() ? kRef : kNotWide);
+ if (arg.IsWide() && cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
+ result = RegStorage::MakeRegPair(
+ result, m2l_->TargetReg(coreArgMappingToPhysicalReg[cur_core_reg_++], kNotWide));
+ }
}
+ return result;
}
/*
@@ -141,7 +145,8 @@
*/
ResourceMask MipsMir2Lir::GetRegMaskCommon(const RegStorage& reg) const {
if (reg.IsDouble()) {
- if (cu_->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()->Is32BitFloatingPoint()) {
+ if (cu_->compiler_driver->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()
+ ->Is32BitFloatingPoint()) {
return ResourceMask::TwoBits((reg.GetRegNum() & ~1) + kMipsFPReg0);
} else {
return ResourceMask::TwoBits(reg.GetRegNum() * 2 + kMipsFPReg0);
@@ -221,78 +226,78 @@
if (nc == '!') {
strcpy(tbuf, "!");
} else {
- DCHECK_LT(fmt, fmt_end);
- DCHECK_LT(static_cast<unsigned>(nc-'0'), 4u);
- operand = lir->operands[nc-'0'];
- switch (*fmt++) {
- case 'b':
- strcpy(tbuf, "0000");
- for (i = 3; i >= 0; i--) {
- tbuf[i] += operand & 1;
- operand >>= 1;
- }
- break;
- case 's':
- snprintf(tbuf, arraysize(tbuf), "$f%d", RegStorage::RegNum(operand));
- break;
- case 'S':
- DCHECK_EQ(RegStorage::RegNum(operand) & 1, 0);
- snprintf(tbuf, arraysize(tbuf), "$f%d", RegStorage::RegNum(operand));
- break;
- case 'h':
- snprintf(tbuf, arraysize(tbuf), "%04x", operand);
- break;
- case 'M':
- case 'd':
- snprintf(tbuf, arraysize(tbuf), "%d", operand);
- break;
- case 'D':
- snprintf(tbuf, arraysize(tbuf), "%d", operand+1);
- break;
- case 'E':
- snprintf(tbuf, arraysize(tbuf), "%d", operand*4);
- break;
- case 'F':
- snprintf(tbuf, arraysize(tbuf), "%d", operand*2);
- break;
- case 't':
- snprintf(tbuf, arraysize(tbuf), "0x%08" PRIxPTR " (L%p)",
- reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 + (operand << 1),
- lir->target);
- break;
- case 'T':
- snprintf(tbuf, arraysize(tbuf), "0x%08x", operand << 2);
- break;
- case 'u': {
- int offset_1 = lir->operands[0];
- int offset_2 = NEXT_LIR(lir)->operands[0];
- uintptr_t target =
- (((reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4) & ~3) +
- (offset_1 << 21 >> 9) + (offset_2 << 1)) & 0xfffffffc;
- snprintf(tbuf, arraysize(tbuf), "%p", reinterpret_cast<void*>(target));
- break;
+ DCHECK_LT(fmt, fmt_end);
+ DCHECK_LT(static_cast<unsigned>(nc-'0'), 4u);
+ operand = lir->operands[nc-'0'];
+ switch (*fmt++) {
+ case 'b':
+ strcpy(tbuf, "0000");
+ for (i = 3; i >= 0; i--) {
+ tbuf[i] += operand & 1;
+ operand >>= 1;
+ }
+ break;
+ case 's':
+ snprintf(tbuf, arraysize(tbuf), "$f%d", RegStorage::RegNum(operand));
+ break;
+ case 'S':
+ DCHECK_EQ(RegStorage::RegNum(operand) & 1, 0);
+ snprintf(tbuf, arraysize(tbuf), "$f%d", RegStorage::RegNum(operand));
+ break;
+ case 'h':
+ snprintf(tbuf, arraysize(tbuf), "%04x", operand);
+ break;
+ case 'M':
+ case 'd':
+ snprintf(tbuf, arraysize(tbuf), "%d", operand);
+ break;
+ case 'D':
+ snprintf(tbuf, arraysize(tbuf), "%d", operand+1);
+ break;
+ case 'E':
+ snprintf(tbuf, arraysize(tbuf), "%d", operand*4);
+ break;
+ case 'F':
+ snprintf(tbuf, arraysize(tbuf), "%d", operand*2);
+ break;
+ case 't':
+ snprintf(tbuf, arraysize(tbuf), "0x%08" PRIxPTR " (L%p)",
+ reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 + (operand << 1),
+ lir->target);
+ break;
+ case 'T':
+ snprintf(tbuf, arraysize(tbuf), "0x%08x", operand << 2);
+ break;
+ case 'u': {
+ int offset_1 = lir->operands[0];
+ int offset_2 = NEXT_LIR(lir)->operands[0];
+ uintptr_t target =
+ (((reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4) & ~3) +
+ (offset_1 << 21 >> 9) + (offset_2 << 1)) & 0xfffffffc;
+ snprintf(tbuf, arraysize(tbuf), "%p", reinterpret_cast<void*>(target));
+ break;
}
- /* Nothing to print for BLX_2 */
- case 'v':
- strcpy(tbuf, "see above");
- break;
- case 'r':
- DCHECK(operand >= 0 && operand < MIPS_REG_COUNT);
- strcpy(tbuf, mips_reg_name[operand]);
- break;
- case 'N':
- // Placeholder for delay slot handling
- strcpy(tbuf, "; nop");
- break;
- default:
- strcpy(tbuf, "DecodeError");
- break;
- }
- buf += tbuf;
+ /* Nothing to print for BLX_2 */
+ case 'v':
+ strcpy(tbuf, "see above");
+ break;
+ case 'r':
+ DCHECK(operand >= 0 && operand < MIPS_REG_COUNT);
+ strcpy(tbuf, mips_reg_name[operand]);
+ break;
+ case 'N':
+ // Placeholder for delay slot handling
+ strcpy(tbuf, "; nop");
+ break;
+ default:
+ strcpy(tbuf, "DecodeError");
+ break;
+ }
+ buf += tbuf;
}
} else {
- buf += *fmt++;
+ buf += *fmt++;
}
}
return buf;
@@ -396,7 +401,8 @@
Clobber(rs_rF13);
Clobber(rs_rF14);
Clobber(rs_rF15);
- if (cu_->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()->Is32BitFloatingPoint()) {
+ if (cu_->compiler_driver->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()
+ ->Is32BitFloatingPoint()) {
Clobber(rs_rD0_fr0);
Clobber(rs_rD1_fr0);
Clobber(rs_rD2_fr0);
@@ -443,10 +449,11 @@
FreeTemp(rs_rMIPS_ARG1);
FreeTemp(rs_rMIPS_ARG2);
FreeTemp(rs_rMIPS_ARG3);
+ FreeTemp(TargetReg(kHiddenArg));
}
bool MipsMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind ATTRIBUTE_UNUSED) {
- if (cu_->GetInstructionSetFeatures()->IsSmp()) {
+ if (cu_->compiler_driver->GetInstructionSetFeatures()->IsSmp()) {
NewLIR1(kMipsSync, 0 /* Only stype currently supported */);
return true;
} else {
@@ -456,7 +463,8 @@
void MipsMir2Lir::CompilerInitializeRegAlloc() {
const bool fpu_is_32bit =
- cu_->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()->Is32BitFloatingPoint();
+ cu_->compiler_driver->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()
+ ->Is32BitFloatingPoint();
reg_pool_.reset(new (arena_) RegisterPool(this, arena_, core_regs, empty_pool /* core64 */,
sp_regs,
fpu_is_32bit ? dp_fr0_regs : dp_fr1_regs,
@@ -602,7 +610,7 @@
}
MipsMir2Lir::MipsMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
- : Mir2Lir(cu, mir_graph, arena) {
+ : Mir2Lir(cu, mir_graph, arena), in_to_reg_storage_mips_mapper_(this) {
for (int i = 0; i < kMipsLast; i++) {
DCHECK_EQ(MipsMir2Lir::EncodingMap[i].opcode, i)
<< "Encoding order for " << MipsMir2Lir::EncodingMap[i].name
diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc
index 18f1cde..6f6bf68 100644
--- a/compiler/dex/quick/mips/utility_mips.cc
+++ b/compiler/dex/quick/mips/utility_mips.cc
@@ -17,8 +17,10 @@
#include "codegen_mips.h"
#include "arch/mips/instruction_set_features_mips.h"
+#include "base/logging.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "dex/reg_storage_eq.h"
+#include "driver/compiler_driver.h"
#include "mips_lir.h"
namespace art {
@@ -125,7 +127,7 @@
opcode = kMipsJalr;
break;
case kOpBx:
- return NewLIR1(kMipsJr, r_dest_src.GetReg());
+ return NewLIR2(kMipsJalr, rZERO, r_dest_src.GetReg());
break;
default:
LOG(FATAL) << "Bad case in OpReg";
@@ -228,17 +230,17 @@
}
break;
case kOpLsl:
- DCHECK(value >= 0 && value <= 31);
- opcode = kMipsSll;
- break;
+ DCHECK(value >= 0 && value <= 31);
+ opcode = kMipsSll;
+ break;
case kOpLsr:
- DCHECK(value >= 0 && value <= 31);
- opcode = kMipsSrl;
- break;
+ DCHECK(value >= 0 && value <= 31);
+ opcode = kMipsSrl;
+ break;
case kOpAsr:
- DCHECK(value >= 0 && value <= 31);
- opcode = kMipsSra;
- break;
+ DCHECK(value >= 0 && value <= 31);
+ opcode = kMipsSra;
+ break;
case kOpAnd:
if (IS_UIMM16((value))) {
opcode = kMipsAndi;
@@ -306,7 +308,7 @@
case kOpXor:
return OpRegRegReg(op, r_dest_src1, r_dest_src1, r_src2);
case kOp2Byte:
- if (cu_->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()
+ if (cu_->compiler_driver->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()
->IsMipsIsaRevGreaterThanEqual2()) {
res = NewLIR2(kMipsSeb, r_dest_src1.GetReg(), r_src2.GetReg());
} else {
@@ -315,7 +317,7 @@
}
return res;
case kOp2Short:
- if (cu_->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()
+ if (cu_->compiler_driver->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()
->IsMipsIsaRevGreaterThanEqual2()) {
res = NewLIR2(kMipsSeh, r_dest_src1.GetReg(), r_src2.GetReg());
} else {
@@ -324,7 +326,7 @@
}
return res;
case kOp2Char:
- return NewLIR3(kMipsAndi, r_dest_src1.GetReg(), r_src2.GetReg(), 0xFFFF);
+ return NewLIR3(kMipsAndi, r_dest_src1.GetReg(), r_src2.GetReg(), 0xFFFF);
default:
LOG(FATAL) << "Bad case in OpRegReg";
UNREACHABLE();
diff --git a/compiler/dex/quick/mir_to_lir-inl.h b/compiler/dex/quick/mir_to_lir-inl.h
index 0aefc2d..280dbbe 100644
--- a/compiler/dex/quick/mir_to_lir-inl.h
+++ b/compiler/dex/quick/mir_to_lir-inl.h
@@ -19,7 +19,8 @@
#include "mir_to_lir.h"
-#include "dex/compiler_internals.h"
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
namespace art {
@@ -276,6 +277,24 @@
}
}
+inline Mir2Lir::ShortyIterator::ShortyIterator(const char* shorty, bool is_static)
+ : cur_(shorty + 1), pending_this_(!is_static), initialized_(false) {
+ DCHECK(shorty != nullptr);
+ DCHECK_NE(*shorty, 0);
+}
+
+inline bool Mir2Lir::ShortyIterator::Next() {
+ if (!initialized_) {
+ initialized_ = true;
+ } else if (pending_this_) {
+ pending_this_ = false;
+ } else if (*cur_ != 0) {
+ cur_++;
+ }
+
+ return *cur_ != 0 || pending_this_;
+}
+
} // namespace art
#endif // ART_COMPILER_DEX_QUICK_MIR_TO_LIR_INL_H_
diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc
index 320c0f4..274e078 100644
--- a/compiler/dex/quick/mir_to_lir.cc
+++ b/compiler/dex/quick/mir_to_lir.cc
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#include "dex/compiler_internals.h"
+#include "mir_to_lir-inl.h"
+
#include "dex/dataflow_iterator-inl.h"
#include "dex/quick/dex_file_method_inliner.h"
-#include "mir_to_lir-inl.h"
+#include "driver/compiler_driver.h"
#include "primitive.h"
#include "thread-inl.h"
@@ -53,20 +54,14 @@
return res;
}
-void Mir2Lir::LockArg(int in_position, bool wide) {
- RegStorage reg_arg_low = GetArgMappingToPhysicalReg(in_position);
- RegStorage reg_arg_high = wide ? GetArgMappingToPhysicalReg(in_position + 1) :
- RegStorage::InvalidReg();
+void Mir2Lir::LockArg(int in_position, bool) {
+ RegStorage reg_arg = GetArgMappingToPhysicalReg(in_position);
- if (reg_arg_low.Valid()) {
- LockTemp(reg_arg_low);
- }
- if (reg_arg_high.Valid() && reg_arg_low.NotExactlyEquals(reg_arg_high)) {
- LockTemp(reg_arg_high);
+ if (reg_arg.Valid()) {
+ LockTemp(reg_arg);
}
}
-// TODO: simplify when 32-bit targets go hard-float.
RegStorage Mir2Lir::LoadArg(int in_position, RegisterClass reg_class, bool wide) {
ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
int offset = StackVisitor::GetOutVROffset(in_position, cu_->instruction_set);
@@ -87,81 +82,38 @@
offset += sizeof(uint64_t);
}
- if (cu_->target64) {
- RegStorage reg_arg = GetArgMappingToPhysicalReg(in_position);
- if (!reg_arg.Valid()) {
- RegStorage new_reg =
- wide ? AllocTypedTempWide(false, reg_class) : AllocTypedTemp(false, reg_class);
- LoadBaseDisp(TargetPtrReg(kSp), offset, new_reg, wide ? k64 : k32, kNotVolatile);
- return new_reg;
- } else {
- // Check if we need to copy the arg to a different reg_class.
- if (!RegClassMatches(reg_class, reg_arg)) {
- if (wide) {
- RegStorage new_reg = AllocTypedTempWide(false, reg_class);
- OpRegCopyWide(new_reg, reg_arg);
- reg_arg = new_reg;
- } else {
- RegStorage new_reg = AllocTypedTemp(false, reg_class);
- OpRegCopy(new_reg, reg_arg);
- reg_arg = new_reg;
- }
+ RegStorage reg_arg = GetArgMappingToPhysicalReg(in_position);
+
+ // TODO: REVISIT: This adds a spill of low part while we could just copy it.
+ if (reg_arg.Valid() && wide && (reg_arg.GetWideKind() == kNotWide)) {
+ // For wide register we've got only half of it.
+ // Flush it to memory then.
+ StoreBaseDisp(TargetPtrReg(kSp), offset, reg_arg, k32, kNotVolatile);
+ reg_arg = RegStorage::InvalidReg();
+ }
+
+ if (!reg_arg.Valid()) {
+ reg_arg = wide ? AllocTypedTempWide(false, reg_class) : AllocTypedTemp(false, reg_class);
+ LoadBaseDisp(TargetPtrReg(kSp), offset, reg_arg, wide ? k64 : k32, kNotVolatile);
+ } else {
+ // Check if we need to copy the arg to a different reg_class.
+ if (!RegClassMatches(reg_class, reg_arg)) {
+ if (wide) {
+ RegStorage new_reg = AllocTypedTempWide(false, reg_class);
+ OpRegCopyWide(new_reg, reg_arg);
+ reg_arg = new_reg;
+ } else {
+ RegStorage new_reg = AllocTypedTemp(false, reg_class);
+ OpRegCopy(new_reg, reg_arg);
+ reg_arg = new_reg;
}
}
- return reg_arg;
- }
-
- RegStorage reg_arg_low = GetArgMappingToPhysicalReg(in_position);
- RegStorage reg_arg_high = wide ? GetArgMappingToPhysicalReg(in_position + 1) :
- RegStorage::InvalidReg();
-
- // If the VR is wide and there is no register for high part, we need to load it.
- if (wide && !reg_arg_high.Valid()) {
- // If the low part is not in a reg, we allocate a pair. Otherwise, we just load to high reg.
- if (!reg_arg_low.Valid()) {
- RegStorage new_regs = AllocTypedTempWide(false, reg_class);
- LoadBaseDisp(TargetPtrReg(kSp), offset, new_regs, k64, kNotVolatile);
- return new_regs; // The reg_class is OK, we can return.
- } else {
- // Assume that no ABI allows splitting a wide fp reg between a narrow fp reg and memory,
- // i.e. the low part is in a core reg. Load the second part in a core reg as well for now.
- DCHECK(!reg_arg_low.IsFloat());
- reg_arg_high = AllocTemp();
- int offset_high = offset + sizeof(uint32_t);
- Load32Disp(TargetPtrReg(kSp), offset_high, reg_arg_high);
- // Continue below to check the reg_class.
- }
- }
-
- // If the low part is not in a register yet, we need to load it.
- if (!reg_arg_low.Valid()) {
- // Assume that if the low part of a wide arg is passed in memory, so is the high part,
- // thus we don't get here for wide args as it's handled above. Big-endian ABIs could
- // conceivably break this assumption but Android supports only little-endian architectures.
- DCHECK(!wide);
- reg_arg_low = AllocTypedTemp(false, reg_class);
- Load32Disp(TargetPtrReg(kSp), offset, reg_arg_low);
- return reg_arg_low; // The reg_class is OK, we can return.
- }
-
- RegStorage reg_arg = wide ? RegStorage::MakeRegPair(reg_arg_low, reg_arg_high) : reg_arg_low;
- // Check if we need to copy the arg to a different reg_class.
- if (!RegClassMatches(reg_class, reg_arg)) {
- if (wide) {
- RegStorage new_regs = AllocTypedTempWide(false, reg_class);
- OpRegCopyWide(new_regs, reg_arg);
- reg_arg = new_regs;
- } else {
- RegStorage new_reg = AllocTypedTemp(false, reg_class);
- OpRegCopy(new_reg, reg_arg);
- reg_arg = new_reg;
- }
}
return reg_arg;
}
-// TODO: simpilfy when 32-bit targets go hard float.
void Mir2Lir::LoadArgDirect(int in_position, RegLocation rl_dest) {
+ DCHECK_EQ(rl_dest.location, kLocPhysReg);
ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
int offset = StackVisitor::GetOutVROffset(in_position, cu_->instruction_set);
if (cu_->instruction_set == kX86) {
@@ -180,48 +132,23 @@
offset += sizeof(uint64_t);
}
- if (!rl_dest.wide) {
- RegStorage reg = GetArgMappingToPhysicalReg(in_position);
- if (reg.Valid()) {
- OpRegCopy(rl_dest.reg, reg);
- } else {
- Load32Disp(TargetPtrReg(kSp), offset, rl_dest.reg);
- }
+ RegStorage reg_arg = GetArgMappingToPhysicalReg(in_position);
+
+ // TODO: REVISIT: This adds a spill of low part while we could just copy it.
+ if (reg_arg.Valid() && rl_dest.wide && (reg_arg.GetWideKind() == kNotWide)) {
+ // For wide register we've got only half of it.
+ // Flush it to memory then.
+ StoreBaseDisp(TargetPtrReg(kSp), offset, reg_arg, k32, kNotVolatile);
+ reg_arg = RegStorage::InvalidReg();
+ }
+
+ if (!reg_arg.Valid()) {
+ LoadBaseDisp(TargetPtrReg(kSp), offset, rl_dest.reg, rl_dest.wide ? k64 : k32, kNotVolatile);
} else {
- if (cu_->target64) {
- RegStorage reg = GetArgMappingToPhysicalReg(in_position);
- if (reg.Valid()) {
- OpRegCopy(rl_dest.reg, reg);
- } else {
- LoadBaseDisp(TargetPtrReg(kSp), offset, rl_dest.reg, k64, kNotVolatile);
- }
- return;
- }
-
- RegStorage reg_arg_low = GetArgMappingToPhysicalReg(in_position);
- RegStorage reg_arg_high = GetArgMappingToPhysicalReg(in_position + 1);
-
- if (cu_->instruction_set == kX86) {
- // Can't handle double split between reg & memory. Flush reg half to memory.
- if (rl_dest.reg.IsDouble() && (reg_arg_low.Valid() != reg_arg_high.Valid())) {
- DCHECK(reg_arg_low.Valid());
- DCHECK(!reg_arg_high.Valid());
- Store32Disp(TargetPtrReg(kSp), offset, reg_arg_low);
- reg_arg_low = RegStorage::InvalidReg();
- }
- }
-
- if (reg_arg_low.Valid() && reg_arg_high.Valid()) {
- OpRegCopyWide(rl_dest.reg, RegStorage::MakeRegPair(reg_arg_low, reg_arg_high));
- } else if (reg_arg_low.Valid() && !reg_arg_high.Valid()) {
- OpRegCopy(rl_dest.reg, reg_arg_low);
- int offset_high = offset + sizeof(uint32_t);
- Load32Disp(TargetPtrReg(kSp), offset_high, rl_dest.reg.GetHigh());
- } else if (!reg_arg_low.Valid() && reg_arg_high.Valid()) {
- OpRegCopy(rl_dest.reg.GetHigh(), reg_arg_high);
- Load32Disp(TargetPtrReg(kSp), offset, rl_dest.reg.GetLow());
+ if (rl_dest.wide) {
+ OpRegCopyWide(rl_dest.reg, reg_arg);
} else {
- LoadBaseDisp(TargetPtrReg(kSp), offset, rl_dest.reg, k64, kNotVolatile);
+ OpRegCopy(rl_dest.reg, reg_arg);
}
}
}
@@ -623,8 +550,7 @@
case Instruction::GOTO:
case Instruction::GOTO_16:
case Instruction::GOTO_32:
- if (mir_graph_->IsBackedge(bb, bb->taken) &&
- (kLeafOptimization || !mir_graph_->HasSuspendTestBetween(bb, bb->taken))) {
+ if (mir_graph_->IsBackEdge(bb, bb->taken)) {
GenSuspendTestAndBranch(opt_flags, &label_list[bb->taken]);
} else {
OpUnconditionalBranch(&label_list[bb->taken]);
@@ -656,12 +582,10 @@
case Instruction::IF_GE:
case Instruction::IF_GT:
case Instruction::IF_LE: {
- LIR* taken = &label_list[bb->taken];
- if (mir_graph_->IsBackwardsBranch(bb) &&
- (kLeafOptimization || !mir_graph_->HasSuspendTestBetween(bb, bb->taken) ||
- !mir_graph_->HasSuspendTestBetween(bb, bb->fall_through))) {
+ if (mir_graph_->IsBackEdge(bb, bb->taken) || mir_graph_->IsBackEdge(bb, bb->fall_through)) {
GenSuspendTest(opt_flags);
}
+ LIR* taken = &label_list[bb->taken];
GenCompareAndBranch(opcode, rl_src[0], rl_src[1], taken);
break;
}
@@ -671,24 +595,22 @@
case Instruction::IF_GEZ:
case Instruction::IF_GTZ:
case Instruction::IF_LEZ: {
- LIR* taken = &label_list[bb->taken];
- if (mir_graph_->IsBackwardsBranch(bb) &&
- (kLeafOptimization || !mir_graph_->HasSuspendTestBetween(bb, bb->taken) ||
- !mir_graph_->HasSuspendTestBetween(bb, bb->fall_through))) {
+ if (mir_graph_->IsBackEdge(bb, bb->taken) || mir_graph_->IsBackEdge(bb, bb->fall_through)) {
GenSuspendTest(opt_flags);
}
+ LIR* taken = &label_list[bb->taken];
GenCompareZeroAndBranch(opcode, rl_src[0], taken);
break;
}
case Instruction::AGET_WIDE:
- GenArrayGet(opt_flags, k64, rl_src[0], rl_src[1], rl_dest, 3);
+ GenArrayGet(opt_flags, rl_dest.fp ? kDouble : k64, rl_src[0], rl_src[1], rl_dest, 3);
break;
case Instruction::AGET_OBJECT:
GenArrayGet(opt_flags, kReference, rl_src[0], rl_src[1], rl_dest, 2);
break;
case Instruction::AGET:
- GenArrayGet(opt_flags, k32, rl_src[0], rl_src[1], rl_dest, 2);
+ GenArrayGet(opt_flags, rl_dest.fp ? kSingle : k32, rl_src[0], rl_src[1], rl_dest, 2);
break;
case Instruction::AGET_BOOLEAN:
GenArrayGet(opt_flags, kUnsignedByte, rl_src[0], rl_src[1], rl_dest, 0);
@@ -703,10 +625,10 @@
GenArrayGet(opt_flags, kSignedHalf, rl_src[0], rl_src[1], rl_dest, 1);
break;
case Instruction::APUT_WIDE:
- GenArrayPut(opt_flags, k64, rl_src[1], rl_src[2], rl_src[0], 3, false);
+ GenArrayPut(opt_flags, rl_src[0].fp ? kDouble : k64, rl_src[1], rl_src[2], rl_src[0], 3, false);
break;
case Instruction::APUT:
- GenArrayPut(opt_flags, k32, rl_src[1], rl_src[2], rl_src[0], 2, false);
+ GenArrayPut(opt_flags, rl_src[0].fp ? kSingle : k32, rl_src[1], rl_src[2], rl_src[0], 2, false);
break;
case Instruction::APUT_OBJECT: {
bool is_null = mir_graph_->IsConstantNullRef(rl_src[0]);
@@ -740,11 +662,19 @@
case Instruction::IGET_WIDE:
// kPrimLong and kPrimDouble share the same entrypoints.
- GenIGet(mir, opt_flags, k64, Primitive::kPrimLong, rl_dest, rl_src[0]);
+ if (rl_dest.fp) {
+ GenIGet(mir, opt_flags, kDouble, Primitive::kPrimDouble, rl_dest, rl_src[0]);
+ } else {
+ GenIGet(mir, opt_flags, k64, Primitive::kPrimLong, rl_dest, rl_src[0]);
+ }
break;
case Instruction::IGET:
- GenIGet(mir, opt_flags, k32, Primitive::kPrimInt, rl_dest, rl_src[0]);
+ if (rl_dest.fp) {
+ GenIGet(mir, opt_flags, kSingle, Primitive::kPrimFloat, rl_dest, rl_src[0]);
+ } else {
+ GenIGet(mir, opt_flags, k32, Primitive::kPrimInt, rl_dest, rl_src[0]);
+ }
break;
case Instruction::IGET_CHAR:
@@ -764,7 +694,7 @@
break;
case Instruction::IPUT_WIDE:
- GenIPut(mir, opt_flags, k64, rl_src[0], rl_src[1]);
+ GenIPut(mir, opt_flags, rl_src[0].fp ? kDouble : k64, rl_src[0], rl_src[1]);
break;
case Instruction::IPUT_OBJECT:
@@ -772,7 +702,7 @@
break;
case Instruction::IPUT:
- GenIPut(mir, opt_flags, k32, rl_src[0], rl_src[1]);
+ GenIPut(mir, opt_flags, rl_src[0].fp ? kSingle : k32, rl_src[0], rl_src[1]);
break;
case Instruction::IPUT_BYTE:
@@ -793,7 +723,7 @@
break;
case Instruction::SGET:
- GenSget(mir, rl_dest, k32, Primitive::kPrimInt);
+ GenSget(mir, rl_dest, rl_dest.fp ? kSingle : k32, Primitive::kPrimInt);
break;
case Instruction::SGET_CHAR:
@@ -814,7 +744,7 @@
case Instruction::SGET_WIDE:
// kPrimLong and kPrimDouble share the same entrypoints.
- GenSget(mir, rl_dest, k64, Primitive::kPrimLong);
+ GenSget(mir, rl_dest, rl_dest.fp ? kDouble : k64, Primitive::kPrimDouble);
break;
case Instruction::SPUT_OBJECT:
@@ -822,7 +752,7 @@
break;
case Instruction::SPUT:
- GenSput(mir, rl_src[0], k32);
+ GenSput(mir, rl_src[0], rl_src[0].fp ? kSingle : k32);
break;
case Instruction::SPUT_BYTE:
@@ -840,74 +770,42 @@
case Instruction::SPUT_WIDE:
- GenSput(mir, rl_src[0], k64);
+ GenSput(mir, rl_src[0], rl_src[0].fp ? kDouble : k64);
break;
case Instruction::INVOKE_STATIC_RANGE:
GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kStatic, true));
- if (!kLeafOptimization) {
- // If the invocation is not inlined, we can assume there is already a
- // suspend check at the return site
- mir_graph_->AppendGenSuspendTestList(bb);
- }
break;
case Instruction::INVOKE_STATIC:
GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kStatic, false));
- if (!kLeafOptimization) {
- mir_graph_->AppendGenSuspendTestList(bb);
- }
break;
case Instruction::INVOKE_DIRECT:
GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kDirect, false));
- if (!kLeafOptimization) {
- mir_graph_->AppendGenSuspendTestList(bb);
- }
break;
case Instruction::INVOKE_DIRECT_RANGE:
GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kDirect, true));
- if (!kLeafOptimization) {
- mir_graph_->AppendGenSuspendTestList(bb);
- }
break;
case Instruction::INVOKE_VIRTUAL:
GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kVirtual, false));
- if (!kLeafOptimization) {
- mir_graph_->AppendGenSuspendTestList(bb);
- }
break;
case Instruction::INVOKE_VIRTUAL_RANGE:
GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kVirtual, true));
- if (!kLeafOptimization) {
- mir_graph_->AppendGenSuspendTestList(bb);
- }
break;
case Instruction::INVOKE_SUPER:
GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kSuper, false));
- if (!kLeafOptimization) {
- mir_graph_->AppendGenSuspendTestList(bb);
- }
break;
case Instruction::INVOKE_SUPER_RANGE:
GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kSuper, true));
- if (!kLeafOptimization) {
- mir_graph_->AppendGenSuspendTestList(bb);
- }
break;
case Instruction::INVOKE_INTERFACE:
GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kInterface, false));
- if (!kLeafOptimization) {
- mir_graph_->AppendGenSuspendTestList(bb);
- }
break;
case Instruction::INVOKE_INTERFACE_RANGE:
GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kInterface, true));
- if (!kLeafOptimization) {
- mir_graph_->AppendGenSuspendTestList(bb);
- }
break;
case Instruction::NEG_INT:
@@ -1108,18 +1006,33 @@
break;
}
case kMirOpFusedCmplFloat:
+ if (mir_graph_->IsBackEdge(bb, bb->taken) || mir_graph_->IsBackEdge(bb, bb->fall_through)) {
+ GenSuspendTest(mir->optimization_flags);
+ }
GenFusedFPCmpBranch(bb, mir, false /*gt bias*/, false /*double*/);
break;
case kMirOpFusedCmpgFloat:
+ if (mir_graph_->IsBackEdge(bb, bb->taken) || mir_graph_->IsBackEdge(bb, bb->fall_through)) {
+ GenSuspendTest(mir->optimization_flags);
+ }
GenFusedFPCmpBranch(bb, mir, true /*gt bias*/, false /*double*/);
break;
case kMirOpFusedCmplDouble:
+ if (mir_graph_->IsBackEdge(bb, bb->taken) || mir_graph_->IsBackEdge(bb, bb->fall_through)) {
+ GenSuspendTest(mir->optimization_flags);
+ }
GenFusedFPCmpBranch(bb, mir, false /*gt bias*/, true /*double*/);
break;
case kMirOpFusedCmpgDouble:
+ if (mir_graph_->IsBackEdge(bb, bb->taken) || mir_graph_->IsBackEdge(bb, bb->fall_through)) {
+ GenSuspendTest(mir->optimization_flags);
+ }
GenFusedFPCmpBranch(bb, mir, true /*gt bias*/, true /*double*/);
break;
case kMirOpFusedCmpLong:
+ if (mir_graph_->IsBackEdge(bb, bb->taken) || mir_graph_->IsBackEdge(bb, bb->fall_through)) {
+ GenSuspendTest(mir->optimization_flags);
+ }
GenFusedLongCmpBranch(bb, mir);
break;
case kMirOpSelect:
@@ -1372,4 +1285,35 @@
UNREACHABLE();
}
+void Mir2Lir::InToRegStorageMapping::Initialize(ShortyIterator* shorty,
+ InToRegStorageMapper* mapper) {
+ DCHECK(mapper != nullptr);
+ DCHECK(shorty != nullptr);
+ max_mapped_in_ = -1;
+ has_arguments_on_stack_ = false;
+ while (shorty->Next()) {
+ ShortyArg arg = shorty->GetArg();
+ RegStorage reg = mapper->GetNextReg(arg);
+ if (reg.Valid()) {
+ mapping_.Put(count_, reg);
+ max_mapped_in_ = count_;
+ // If the VR is wide and was mapped as wide then account for it.
+ if (arg.IsWide() && reg.Is64Bit()) {
+ max_mapped_in_++;
+ }
+ } else {
+ has_arguments_on_stack_ = true;
+ }
+ count_ += arg.IsWide() ? 2 : 1;
+ }
+ initialized_ = true;
+}
+
+RegStorage Mir2Lir::InToRegStorageMapping::Get(int in_position) {
+ DCHECK(IsInitialized());
+ DCHECK_LT(in_position, count_);
+ auto res = mapping_.find(in_position);
+ return res != mapping_.end() ? res->second : RegStorage::InvalidReg();
+}
+
} // namespace art
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 5d78a6e..9f1a497 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -17,15 +17,13 @@
#ifndef ART_COMPILER_DEX_QUICK_MIR_TO_LIR_H_
#define ART_COMPILER_DEX_QUICK_MIR_TO_LIR_H_
-#include "arch/instruction_set.h"
#include "compiled_method.h"
#include "dex/compiler_enums.h"
-#include "dex/compiler_ir.h"
+#include "dex/dex_flags.h"
+#include "dex/dex_types.h"
#include "dex/reg_location.h"
#include "dex/reg_storage.h"
-#include "dex/backend.h"
#include "dex/quick/resource_mask.h"
-#include "driver/compiler_driver.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "invoke_type.h"
#include "leb128.h"
@@ -125,10 +123,12 @@
#define REG_USE23 (REG_USE2 | REG_USE3)
#define REG_USE123 (REG_USE1 | REG_USE2 | REG_USE3)
-// TODO: #includes need a cleanup
-#ifndef INVALID_SREG
-#define INVALID_SREG (-1)
-#endif
+/*
+ * Assembly is an iterative process, and usually terminates within
+ * two or three passes. This should be high enough to handle bizarre
+ * cases, but detect an infinite loop bug.
+ */
+#define MAX_ASSEMBLER_RETRIES 50
class BasicBlock;
struct CallInfo;
@@ -140,7 +140,6 @@
class DexFileMethodInliner;
class MIRGraph;
class MirMethodLoweringInfo;
-class Mir2Lir;
typedef int (*NextCallInsn)(CompilationUnit*, CallInfo*, int,
const MethodReference& target_method,
@@ -148,6 +147,7 @@
uintptr_t direct_method, InvokeType type);
typedef std::vector<uint8_t> CodeBuffer;
+typedef uint32_t CodeOffset; // Native code offset in bytes.
struct UseDefMasks {
const ResourceMask* use_mask; // Resource mask for use.
@@ -200,14 +200,7 @@
// Mask to denote sreg as the start of a 64-bit item. Must not interfere with low 16 bits.
#define STARTING_WIDE_SREG 0x10000
-// TODO: replace these macros
-#define SLOW_FIELD_PATH (cu_->enable_debug & (1 << kDebugSlowFieldPath))
-#define SLOW_INVOKE_PATH (cu_->enable_debug & (1 << kDebugSlowInvokePath))
-#define SLOW_STRING_PATH (cu_->enable_debug & (1 << kDebugSlowStringPath))
-#define SLOW_TYPE_PATH (cu_->enable_debug & (1 << kDebugSlowTypePath))
-#define EXERCISE_SLOWEST_STRING_PATH (cu_->enable_debug & (1 << kDebugSlowestStringPath))
-
-class Mir2Lir : public Backend {
+class Mir2Lir {
public:
static constexpr bool kFailOnSizeError = true && kIsDebugBuild;
static constexpr bool kReportSizeError = true && kIsDebugBuild;
@@ -231,7 +224,7 @@
struct SwitchTable : EmbeddedData {
LIR* anchor; // Reference instruction for relative offsets.
- LIR** targets; // Array of case targets.
+ MIR* switch_mir; // The switch mir.
};
/* Static register use counts */
@@ -660,7 +653,6 @@
LIR* ScanLiteralPoolClass(LIR* data_target, const DexFile& dex_file, uint32_t type_idx);
LIR* AddWordData(LIR* *constant_list_p, int value);
LIR* AddWideData(LIR* *constant_list_p, int val_lo, int val_hi);
- void ProcessSwitchTables();
void DumpSparseSwitchTable(const uint16_t* table);
void DumpPackedSwitchTable(const uint16_t* table);
void MarkBoundary(DexOffset offset, const char* inst_str);
@@ -678,9 +670,7 @@
int AssignLiteralOffset(CodeOffset offset);
int AssignSwitchTablesOffset(CodeOffset offset);
int AssignFillArrayDataOffset(CodeOffset offset);
- virtual LIR* InsertCaseLabel(DexOffset vaddr, int keyVal);
- virtual void MarkPackedCaseLabels(Mir2Lir::SwitchTable* tab_rec);
- void MarkSparseCaseLabels(Mir2Lir::SwitchTable* tab_rec);
+ LIR* InsertCaseLabel(uint32_t bbid, int keyVal);
// Handle bookkeeping to convert a wide RegLocation to a narrow RegLocation. No code generated.
virtual RegLocation NarrowRegLoc(RegLocation loc);
@@ -843,8 +833,8 @@
virtual void GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest,
RegLocation rl_src1, RegLocation rl_src2, int flags);
void GenConversionCall(QuickEntrypointEnum trampoline, RegLocation rl_dest, RegLocation rl_src);
- virtual void GenSuspendTest(int opt_flags);
- virtual void GenSuspendTestAndBranch(int opt_flags, LIR* target);
+ void GenSuspendTest(int opt_flags);
+ void GenSuspendTestAndBranch(int opt_flags, LIR* target);
// This will be overridden by x86 implementation.
virtual void GenConstWide(RegLocation rl_dest, int64_t value);
@@ -874,17 +864,17 @@
void CallRuntimeHelperImmMethod(QuickEntrypointEnum trampoline, int arg0, bool safepoint_pc);
void CallRuntimeHelperRegMethod(QuickEntrypointEnum trampoline, RegStorage arg0,
bool safepoint_pc);
- void CallRuntimeHelperRegMethodRegLocation(QuickEntrypointEnum trampoline, RegStorage arg0,
- RegLocation arg2, bool safepoint_pc);
+ void CallRuntimeHelperRegRegLocationMethod(QuickEntrypointEnum trampoline, RegStorage arg0,
+ RegLocation arg1, bool safepoint_pc);
void CallRuntimeHelperRegLocationRegLocation(QuickEntrypointEnum trampoline, RegLocation arg0,
RegLocation arg1, bool safepoint_pc);
void CallRuntimeHelperRegReg(QuickEntrypointEnum trampoline, RegStorage arg0, RegStorage arg1,
bool safepoint_pc);
void CallRuntimeHelperRegRegImm(QuickEntrypointEnum trampoline, RegStorage arg0,
RegStorage arg1, int arg2, bool safepoint_pc);
- void CallRuntimeHelperImmMethodRegLocation(QuickEntrypointEnum trampoline, int arg0,
- RegLocation arg2, bool safepoint_pc);
- void CallRuntimeHelperImmMethodImm(QuickEntrypointEnum trampoline, int arg0, int arg2,
+ void CallRuntimeHelperImmRegLocationMethod(QuickEntrypointEnum trampoline, int arg0,
+ RegLocation arg1, bool safepoint_pc);
+ void CallRuntimeHelperImmImmMethod(QuickEntrypointEnum trampoline, int arg0, int arg1,
bool safepoint_pc);
void CallRuntimeHelperImmRegLocationRegLocation(QuickEntrypointEnum trampoline, int arg0,
RegLocation arg1, RegLocation arg2,
@@ -895,29 +885,24 @@
bool safepoint_pc);
void GenInvoke(CallInfo* info);
void GenInvokeNoInline(CallInfo* info);
- virtual NextCallInsn GetNextSDCallInsn();
+ virtual NextCallInsn GetNextSDCallInsn() = 0;
/*
* @brief Generate the actual call insn based on the method info.
* @param method_info the lowering info for the method call.
* @returns Call instruction
*/
- virtual LIR* GenCallInsn(const MirMethodLoweringInfo& method_info);
+ virtual LIR* GenCallInsn(const MirMethodLoweringInfo& method_info) = 0;
virtual void FlushIns(RegLocation* ArgLocs, RegLocation rl_method);
- virtual int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel,
- NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx,
- uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
- bool skip_this);
- virtual int GenDalvikArgsRange(CallInfo* info, int call_state, LIR** pcrLabel,
- NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx,
- uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
- bool skip_this);
-
+ virtual int GenDalvikArgs(CallInfo* info, int call_state, LIR** pcrLabel,
+ NextCallInsn next_call_insn,
+ const MethodReference& target_method,
+ uint32_t vtable_idx,
+ uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
+ bool skip_this);
+ virtual int GenDalvikArgsBulkCopy(CallInfo* info, int first, int count);
+ virtual void GenDalvikArgsFlushPromoted(CallInfo* info, int start);
/**
* @brief Used to determine the register location of destination.
* @details This is needed during generation of inline intrinsics because it finds destination
@@ -958,36 +943,26 @@
bool GenInlinedUnsafeGet(CallInfo* info, bool is_long, bool is_volatile);
bool GenInlinedUnsafePut(CallInfo* info, bool is_long, bool is_object,
bool is_volatile, bool is_ordered);
- virtual int LoadArgRegs(CallInfo* info, int call_state,
- NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx,
- uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
- bool skip_this);
// Shared by all targets - implemented in gen_loadstore.cc.
RegLocation LoadCurrMethod();
void LoadCurrMethodDirect(RegStorage r_tgt);
virtual LIR* LoadConstant(RegStorage r_dest, int value);
// Natural word size.
- virtual LIR* LoadWordDisp(RegStorage r_base, int displacement, RegStorage r_dest) {
+ LIR* LoadWordDisp(RegStorage r_base, int displacement, RegStorage r_dest) {
return LoadBaseDisp(r_base, displacement, r_dest, kWord, kNotVolatile);
}
- // Load 8 bits, regardless of target.
- virtual LIR* Load8Disp(RegStorage r_base, int displacement, RegStorage r_dest) {
- return LoadBaseDisp(r_base, displacement, r_dest, kSignedByte, kNotVolatile);
- }
// Load 32 bits, regardless of target.
- virtual LIR* Load32Disp(RegStorage r_base, int displacement, RegStorage r_dest) {
+ LIR* Load32Disp(RegStorage r_base, int displacement, RegStorage r_dest) {
return LoadBaseDisp(r_base, displacement, r_dest, k32, kNotVolatile);
}
// Load a reference at base + displacement and decompress into register.
- virtual LIR* LoadRefDisp(RegStorage r_base, int displacement, RegStorage r_dest,
+ LIR* LoadRefDisp(RegStorage r_base, int displacement, RegStorage r_dest,
VolatileKind is_volatile) {
return LoadBaseDisp(r_base, displacement, r_dest, kReference, is_volatile);
}
// Load a reference at base + index and decompress into register.
- virtual LIR* LoadRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest,
+ LIR* LoadRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest,
int scale) {
return LoadBaseIndexed(r_base, r_index, r_dest, scale, kReference);
}
@@ -1004,21 +979,21 @@
// Load Dalvik value with 64-bit memory storage.
virtual void LoadValueDirectWideFixed(RegLocation rl_src, RegStorage r_dest);
// Store an item of natural word size.
- virtual LIR* StoreWordDisp(RegStorage r_base, int displacement, RegStorage r_src) {
+ LIR* StoreWordDisp(RegStorage r_base, int displacement, RegStorage r_src) {
return StoreBaseDisp(r_base, displacement, r_src, kWord, kNotVolatile);
}
// Store an uncompressed reference into a compressed 32-bit container.
- virtual LIR* StoreRefDisp(RegStorage r_base, int displacement, RegStorage r_src,
+ LIR* StoreRefDisp(RegStorage r_base, int displacement, RegStorage r_src,
VolatileKind is_volatile) {
return StoreBaseDisp(r_base, displacement, r_src, kReference, is_volatile);
}
// Store an uncompressed reference into a compressed 32-bit container by index.
- virtual LIR* StoreRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src,
+ LIR* StoreRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src,
int scale) {
return StoreBaseIndexed(r_base, r_index, r_src, scale, kReference);
}
// Store 32 bits, regardless of target.
- virtual LIR* Store32Disp(RegStorage r_base, int displacement, RegStorage r_src) {
+ LIR* Store32Disp(RegStorage r_base, int displacement, RegStorage r_src) {
return StoreBaseDisp(r_base, displacement, r_src, k32, kNotVolatile);
}
@@ -1228,7 +1203,7 @@
}
}
- virtual RegStorage GetArgMappingToPhysicalReg(int arg_num) = 0;
+ RegStorage GetArgMappingToPhysicalReg(int arg_num);
virtual RegLocation GetReturnAlt() = 0;
virtual RegLocation GetReturnWideAlt() = 0;
virtual RegLocation LocCReturn() = 0;
@@ -1486,6 +1461,30 @@
virtual LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) = 0;
+ // Queries for backend support for vectors
+ /*
+ * Return the number of bits in a vector register.
+ * @return 0 if vector registers are not supported, or the
+ * number of bits in the vector register if supported.
+ */
+ virtual int VectorRegisterSize() {
+ return 0;
+ }
+
+ /*
+ * Return the number of reservable vector registers supported
+ * @param long_or_fp, true if floating point computations will be
+ * executed or the operations will be long type while vector
+ * registers are reserved.
+ * @return the number of vector registers that are available
+ * @note The backend should ensure that sufficient vector registers
+ * are held back to generate scalar code without exhausting vector
+ * registers, if scalar code also uses the vector registers.
+ */
+ virtual int NumReservableVectorRegisters(bool long_or_fp ATTRIBUTE_UNUSED) {
+ return 0;
+ }
+
protected:
Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
@@ -1493,18 +1492,6 @@
return cu_;
}
/*
- * @brief Returns the index of the lowest set bit in 'x'.
- * @param x Value to be examined.
- * @returns The bit number of the lowest bit set in the value.
- */
- int32_t LowestSetBit(uint64_t x);
- /*
- * @brief Is this value a power of two?
- * @param x Value to be examined.
- * @returns 'true' if only 1 bit is set in the value.
- */
- bool IsPowerOfTwo(uint64_t x);
- /*
* @brief Do these SRs overlap?
* @param rl_op1 One RegLocation
* @param rl_op2 The other RegLocation
@@ -1720,6 +1707,7 @@
LIR* first_fixup_; // Doubly-linked list of LIR nodes requiring fixups.
protected:
+ ArenaAllocator* const arena_;
CompilationUnit* const cu_;
MIRGraph* const mir_graph_;
ArenaVector<SwitchTable*> switch_tables_;
@@ -1752,7 +1740,7 @@
int live_sreg_;
CodeBuffer code_buffer_;
// The source mapping table data (pc -> dex). More entries than in encoded_mapping_table_
- SrcMap src_mapping_table_;
+ DefaultSrcMap src_mapping_table_;
// The encoding mapping table data (dex -> pc offset and pc offset -> dex) with a size prefix.
std::vector<uint8_t> encoded_mapping_table_;
ArenaVector<uint32_t> core_vmap_table_;
@@ -1780,6 +1768,63 @@
// to deduplicate the masks.
ResourceMaskCache mask_cache_;
+ protected:
+ // ABI support
+ class ShortyArg {
+ public:
+ explicit ShortyArg(char type) : type_(type) { }
+ bool IsFP() { return type_ == 'F' || type_ == 'D'; }
+ bool IsWide() { return type_ == 'J' || type_ == 'D'; }
+ bool IsRef() { return type_ == 'L'; }
+ char GetType() { return type_; }
+ private:
+ char type_;
+ };
+
+ class ShortyIterator {
+ public:
+ ShortyIterator(const char* shorty, bool is_static);
+ bool Next();
+ ShortyArg GetArg() { return ShortyArg(pending_this_ ? 'L' : *cur_); }
+ private:
+ const char* cur_;
+ bool pending_this_;
+ bool initialized_;
+ };
+
+ class InToRegStorageMapper {
+ public:
+ virtual RegStorage GetNextReg(ShortyArg arg) = 0;
+ virtual ~InToRegStorageMapper() {}
+ virtual void Reset() = 0;
+ };
+
+ class InToRegStorageMapping {
+ public:
+ explicit InToRegStorageMapping(ArenaAllocator* arena)
+ : mapping_(std::less<int>(), arena->Adapter()), count_(0),
+ max_mapped_in_(0), has_arguments_on_stack_(false), initialized_(false) {}
+ void Initialize(ShortyIterator* shorty, InToRegStorageMapper* mapper);
+ /**
+ * @return the index of last VR mapped to physical register. In other words
+ * any VR starting from (return value + 1) index is mapped to memory.
+ */
+ int GetMaxMappedIn() { return max_mapped_in_; }
+ bool HasArgumentsOnStack() { return has_arguments_on_stack_; }
+ RegStorage Get(int in_position);
+ bool IsInitialized() { return initialized_; }
+ private:
+ ArenaSafeMap<int, RegStorage> mapping_;
+ int count_;
+ int max_mapped_in_;
+ bool has_arguments_on_stack_;
+ bool initialized_;
+ };
+
+ // Cached mapping of method input to reg storage according to ABI.
+ InToRegStorageMapping in_to_reg_storage_mapping_;
+ virtual InToRegStorageMapper* GetResetedInToRegStorageMapper() = 0;
+
private:
static bool SizeMatchesTypeForEntrypoint(OpSize size, Primitive::Type type);
}; // Class Mir2Lir
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
index 8d4cb3c..909077e 100644
--- a/compiler/dex/quick/quick_compiler.cc
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -18,15 +18,28 @@
#include <cstdint>
+#include "base/dumpable.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/timing_logger.h"
#include "compiler.h"
-#include "dex/frontend.h"
+#include "dex_file-inl.h"
+#include "dex_file_to_method_inliner_map.h"
+#include "dex/compiler_ir.h"
+#include "dex/dex_flags.h"
#include "dex/mir_graph.h"
+#include "dex/pass_driver_me_opts.h"
+#include "dex/pass_driver_me_post_opt.h"
+#include "dex/pass_manager.h"
#include "dex/quick/mir_to_lir.h"
#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
#include "elf_writer_quick.h"
#include "jni/quick/jni_compiler.h"
+#include "mir_to_lir.h"
#include "mirror/art_method-inl.h"
-#include "base/logging.h"
+#include "mirror/object.h"
+#include "runtime.h"
// Specific compiler backends.
#include "dex/quick/arm/backend_arm.h"
@@ -36,48 +49,6 @@
namespace art {
-class QuickCompiler : public Compiler {
- public:
- explicit QuickCompiler(CompilerDriver* driver) : Compiler(driver, 100) {}
-
- void Init() const OVERRIDE;
-
- void UnInit() const OVERRIDE;
-
- bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const
- OVERRIDE;
-
- CompiledMethod* Compile(const DexFile::CodeItem* code_item,
- uint32_t access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const DexFile& dex_file) const OVERRIDE;
-
- CompiledMethod* JniCompile(uint32_t access_flags,
- uint32_t method_idx,
- const DexFile& dex_file) const OVERRIDE;
-
- uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const OVERRIDE
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- bool WriteElf(art::File* file,
- OatWriter* oat_writer,
- const std::vector<const art::DexFile*>& dex_files,
- const std::string& android_root,
- bool is_host) const
- OVERRIDE
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const OVERRIDE;
-
- void InitCompilationUnit(CompilationUnit& cu) const OVERRIDE;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(QuickCompiler);
-};
-
static_assert(0U == static_cast<size_t>(kNone), "kNone not 0");
static_assert(1U == static_cast<size_t>(kArm), "kArm not 1");
static_assert(2U == static_cast<size_t>(kArm64), "kArm64 not 2");
@@ -392,10 +363,10 @@
Instruction::IPUT_BYTE_QUICK,
Instruction::IPUT_CHAR_QUICK,
Instruction::IPUT_SHORT_QUICK,
- Instruction::UNUSED_EF,
- Instruction::UNUSED_F0,
- Instruction::UNUSED_F1,
- Instruction::UNUSED_F2,
+ Instruction::IGET_BOOLEAN_QUICK,
+ Instruction::IGET_BYTE_QUICK,
+ Instruction::IGET_CHAR_QUICK,
+ Instruction::IGET_SHORT_QUICK,
Instruction::UNUSED_F3,
Instruction::UNUSED_F4,
Instruction::UNUSED_F5,
@@ -573,7 +544,7 @@
cu.disable_opt |= kDisabledOptimizationsPerISA[cu.instruction_set];
}
-void QuickCompiler::Init() const {
+void QuickCompiler::Init() {
CHECK(GetCompilerDriver()->GetCompilerContext() == nullptr);
}
@@ -581,6 +552,47 @@
CHECK(GetCompilerDriver()->GetCompilerContext() == nullptr);
}
+/* Default optimizer/debug setting for the compiler. */
+static uint32_t kCompilerOptimizerDisableFlags = 0 | // Disable specific optimizations
+ // (1 << kLoadStoreElimination) |
+ // (1 << kLoadHoisting) |
+ // (1 << kSuppressLoads) |
+ // (1 << kNullCheckElimination) |
+ // (1 << kClassInitCheckElimination) |
+ // (1 << kGlobalValueNumbering) |
+ // (1 << kLocalValueNumbering) |
+ // (1 << kPromoteRegs) |
+ // (1 << kTrackLiveTemps) |
+ // (1 << kSafeOptimizations) |
+ // (1 << kBBOpt) |
+ // (1 << kSuspendCheckElimination) |
+ // (1 << kMatch) |
+ // (1 << kPromoteCompilerTemps) |
+ // (1 << kSuppressExceptionEdges) |
+ // (1 << kSuppressMethodInlining) |
+ 0;
+
+static uint32_t kCompilerDebugFlags = 0 | // Enable debug/testing modes
+ // (1 << kDebugDisplayMissingTargets) |
+ // (1 << kDebugVerbose) |
+ // (1 << kDebugDumpCFG) |
+ // (1 << kDebugSlowFieldPath) |
+ // (1 << kDebugSlowInvokePath) |
+ // (1 << kDebugSlowStringPath) |
+ // (1 << kDebugSlowestFieldPath) |
+ // (1 << kDebugSlowestStringPath) |
+ // (1 << kDebugExerciseResolveMethod) |
+ // (1 << kDebugVerifyDataflow) |
+ // (1 << kDebugShowMemoryUsage) |
+ // (1 << kDebugShowNops) |
+ // (1 << kDebugCountOpcodes) |
+ // (1 << kDebugDumpCheckStats) |
+ // (1 << kDebugShowSummaryMemoryUsage) |
+ // (1 << kDebugShowFilterStats) |
+ // (1 << kDebugTimings) |
+ // (1 << kDebugCodegenDump) |
+ 0;
+
CompiledMethod* QuickCompiler::Compile(const DexFile::CodeItem* code_item,
uint32_t access_flags,
InvokeType invoke_type,
@@ -588,22 +600,163 @@
uint32_t method_idx,
jobject class_loader,
const DexFile& dex_file) const {
- CompiledMethod* method = TryCompileWithSeaIR(code_item,
- access_flags,
- invoke_type,
- class_def_idx,
- method_idx,
- class_loader,
- dex_file);
- if (method != nullptr) {
- return method;
- }
-
// TODO: check method fingerprint here to determine appropriate backend type. Until then, use
// build default.
CompilerDriver* driver = GetCompilerDriver();
- return CompileOneMethod(driver, this, code_item, access_flags, invoke_type, class_def_idx,
- method_idx, class_loader, dex_file, nullptr /* use thread llvm_info */);
+
+ VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "...";
+ if (Compiler::IsPathologicalCase(*code_item, method_idx, dex_file)) {
+ return nullptr;
+ }
+
+ DCHECK(driver->GetCompilerOptions().IsCompilationEnabled());
+
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ InstructionSet instruction_set = driver->GetInstructionSet();
+ if (instruction_set == kArm) {
+ instruction_set = kThumb2;
+ }
+ CompilationUnit cu(driver->GetArenaPool(), instruction_set, driver, class_linker);
+
+ // TODO: Mips64 is not yet implemented.
+ CHECK((cu.instruction_set == kThumb2) ||
+ (cu.instruction_set == kArm64) ||
+ (cu.instruction_set == kX86) ||
+ (cu.instruction_set == kX86_64) ||
+ (cu.instruction_set == kMips));
+
+ // TODO: set this from command line
+ constexpr bool compiler_flip_match = false;
+ const std::string compiler_method_match = "";
+
+ bool use_match = !compiler_method_match.empty();
+ bool match = use_match && (compiler_flip_match ^
+ (PrettyMethod(method_idx, dex_file).find(compiler_method_match) != std::string::npos));
+ if (!use_match || match) {
+ cu.disable_opt = kCompilerOptimizerDisableFlags;
+ cu.enable_debug = kCompilerDebugFlags;
+ cu.verbose = VLOG_IS_ON(compiler) ||
+ (cu.enable_debug & (1 << kDebugVerbose));
+ }
+
+ if (driver->GetCompilerOptions().HasVerboseMethods()) {
+ cu.verbose = driver->GetCompilerOptions().IsVerboseMethod(PrettyMethod(method_idx, dex_file));
+ }
+
+ if (cu.verbose) {
+ cu.enable_debug |= (1 << kDebugCodegenDump);
+ }
+
+ /*
+ * TODO: rework handling of optimization and debug flags. Should we split out
+ * MIR and backend flags? Need command-line setting as well.
+ */
+
+ InitCompilationUnit(cu);
+
+ cu.StartTimingSplit("BuildMIRGraph");
+ cu.mir_graph.reset(new MIRGraph(&cu, &cu.arena));
+
+ /*
+ * After creation of the MIR graph, also create the code generator.
+ * The reason we do this is that optimizations on the MIR graph may need to get information
+ * that is only available if a CG exists.
+ */
+ cu.cg.reset(GetCodeGenerator(&cu, nullptr));
+
+ /* Gathering opcode stats? */
+ if (kCompilerDebugFlags & (1 << kDebugCountOpcodes)) {
+ cu.mir_graph->EnableOpcodeCounting();
+ }
+
+ /* Build the raw MIR graph */
+ cu.mir_graph->InlineMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx,
+ class_loader, dex_file);
+
+ if (!CanCompileMethod(method_idx, dex_file, &cu)) {
+ VLOG(compiler) << cu.instruction_set << ": Cannot compile method : "
+ << PrettyMethod(method_idx, dex_file);
+ cu.EndTiming();
+ return nullptr;
+ }
+
+ cu.NewTimingSplit("MIROpt:CheckFilters");
+ std::string skip_message;
+ if (cu.mir_graph->SkipCompilation(&skip_message)) {
+ VLOG(compiler) << cu.instruction_set << ": Skipping method : "
+ << PrettyMethod(method_idx, dex_file) << " Reason = " << skip_message;
+ cu.EndTiming();
+ return nullptr;
+ }
+
+ /* Create the pass driver and launch it */
+ PassDriverMEOpts pass_driver(GetPreOptPassManager(), &cu);
+ pass_driver.Launch();
+
+ /* For non-leaf methods check if we should skip compilation when the profiler is enabled. */
+ if (cu.compiler_driver->ProfilePresent()
+ && !cu.mir_graph->MethodIsLeaf()
+ && cu.mir_graph->SkipCompilationByName(PrettyMethod(method_idx, dex_file))) {
+ cu.EndTiming();
+ return nullptr;
+ }
+
+ if (cu.enable_debug & (1 << kDebugDumpCheckStats)) {
+ cu.mir_graph->DumpCheckStats();
+ }
+
+ if (kCompilerDebugFlags & (1 << kDebugCountOpcodes)) {
+ cu.mir_graph->ShowOpcodeStats();
+ }
+
+ /* Reassociate sreg names with original Dalvik vreg names. */
+ cu.mir_graph->RemapRegLocations();
+
+ /* Free Arenas from the cu.arena_stack for reuse by the cu.arena in the codegen. */
+ if (cu.enable_debug & (1 << kDebugShowMemoryUsage)) {
+ if (cu.arena_stack.PeakBytesAllocated() > 1 * 1024 * 1024) {
+ MemStats stack_stats(cu.arena_stack.GetPeakStats());
+ LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(stack_stats);
+ }
+ }
+ cu.arena_stack.Reset();
+
+ CompiledMethod* result = nullptr;
+
+ if (cu.mir_graph->PuntToInterpreter()) {
+ VLOG(compiler) << cu.instruction_set << ": Punted method to interpreter: "
+ << PrettyMethod(method_idx, dex_file);
+ cu.EndTiming();
+ return nullptr;
+ }
+
+ cu.cg->Materialize();
+
+ cu.NewTimingSplit("Dedupe"); /* deduping takes up the vast majority of time in GetCompiledMethod(). */
+ result = cu.cg->GetCompiledMethod();
+ cu.NewTimingSplit("Cleanup");
+
+ if (result) {
+ VLOG(compiler) << cu.instruction_set << ": Compiled " << PrettyMethod(method_idx, dex_file);
+ } else {
+ VLOG(compiler) << cu.instruction_set << ": Deferred " << PrettyMethod(method_idx, dex_file);
+ }
+
+ if (cu.enable_debug & (1 << kDebugShowMemoryUsage)) {
+ if (cu.arena.BytesAllocated() > (1 * 1024 *1024)) {
+ MemStats mem_stats(cu.arena.GetMemStats());
+ LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(mem_stats);
+ }
+ }
+
+ if (cu.enable_debug & (1 << kDebugShowSummaryMemoryUsage)) {
+ LOG(INFO) << "MEMINFO " << cu.arena.BytesAllocated() << " " << cu.mir_graph->GetNumBlocks()
+ << " " << PrettyMethod(method_idx, dex_file);
+ }
+
+ cu.EndTiming();
+ driver->GetTimingsLogger()->AddLogger(cu.timings);
+ return result;
}
CompiledMethod* QuickCompiler::JniCompile(uint32_t access_flags,
@@ -626,7 +779,7 @@
*GetCompilerDriver());
}
-Backend* QuickCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
+Mir2Lir* QuickCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
UNUSED(compilation_unit);
Mir2Lir* mir_to_lir = nullptr;
switch (cu->instruction_set) {
@@ -657,8 +810,34 @@
return mir_to_lir;
}
+QuickCompiler::QuickCompiler(CompilerDriver* driver) : Compiler(driver, 100) {
+ const auto& compiler_options = driver->GetCompilerOptions();
+ auto* pass_manager_options = compiler_options.GetPassManagerOptions();
+ pre_opt_pass_manager_.reset(new PassManager(*pass_manager_options));
+ CHECK(pre_opt_pass_manager_.get() != nullptr);
+ PassDriverMEOpts::SetupPasses(pre_opt_pass_manager_.get());
+ pre_opt_pass_manager_->CreateDefaultPassList();
+ if (pass_manager_options->GetPrintPassOptions()) {
+ PassDriverMEOpts::PrintPassOptions(pre_opt_pass_manager_.get());
+ }
+ // TODO: Different options for pre vs post opts?
+ post_opt_pass_manager_.reset(new PassManager(PassManagerOptions()));
+ CHECK(post_opt_pass_manager_.get() != nullptr);
+ PassDriverMEPostOpt::SetupPasses(post_opt_pass_manager_.get());
+ post_opt_pass_manager_->CreateDefaultPassList();
+ if (pass_manager_options->GetPrintPassOptions()) {
+ PassDriverMEPostOpt::PrintPassOptions(post_opt_pass_manager_.get());
+ }
+}
+
+QuickCompiler::~QuickCompiler() {
+}
Compiler* CreateQuickCompiler(CompilerDriver* driver) {
+ return QuickCompiler::Create(driver);
+}
+
+Compiler* QuickCompiler::Create(CompilerDriver* driver) {
return new QuickCompiler(driver);
}
diff --git a/compiler/dex/quick/quick_compiler.h b/compiler/dex/quick/quick_compiler.h
index 10de5fb..5153a9e 100644
--- a/compiler/dex/quick/quick_compiler.h
+++ b/compiler/dex/quick/quick_compiler.h
@@ -17,12 +17,70 @@
#ifndef ART_COMPILER_DEX_QUICK_QUICK_COMPILER_H_
#define ART_COMPILER_DEX_QUICK_QUICK_COMPILER_H_
+#include "compiler.h"
+
namespace art {
class Compiler;
class CompilerDriver;
+class Mir2Lir;
+class PassManager;
-Compiler* CreateQuickCompiler(CompilerDriver* driver);
+class QuickCompiler : public Compiler {
+ public:
+ virtual ~QuickCompiler();
+
+ void Init() OVERRIDE;
+
+ void UnInit() const OVERRIDE;
+
+ bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const
+ OVERRIDE;
+
+ CompiledMethod* Compile(const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint16_t class_def_idx,
+ uint32_t method_idx,
+ jobject class_loader,
+ const DexFile& dex_file) const OVERRIDE;
+
+ CompiledMethod* JniCompile(uint32_t access_flags,
+ uint32_t method_idx,
+ const DexFile& dex_file) const OVERRIDE;
+
+ uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const OVERRIDE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ bool WriteElf(art::File* file,
+ OatWriter* oat_writer,
+ const std::vector<const art::DexFile*>& dex_files,
+ const std::string& android_root,
+ bool is_host) const
+ OVERRIDE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ Mir2Lir* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const;
+
+ void InitCompilationUnit(CompilationUnit& cu) const OVERRIDE;
+
+ static Compiler* Create(CompilerDriver* driver);
+
+ const PassManager* GetPreOptPassManager() const {
+ return pre_opt_pass_manager_.get();
+ }
+ const PassManager* GetPostOptPassManager() const {
+ return post_opt_pass_manager_.get();
+ }
+
+ protected:
+ explicit QuickCompiler(CompilerDriver* driver);
+
+ private:
+ std::unique_ptr<PassManager> pre_opt_pass_manager_;
+ std::unique_ptr<PassManager> post_opt_pass_manager_;
+ DISALLOW_COPY_AND_ASSIGN(QuickCompiler);
+};
} // namespace art
diff --git a/compiler/llvm/llvm_compiler.h b/compiler/dex/quick/quick_compiler_factory.h
similarity index 66%
rename from compiler/llvm/llvm_compiler.h
rename to compiler/dex/quick/quick_compiler_factory.h
index da6d0e9..31ee1cf 100644
--- a/compiler/llvm/llvm_compiler.h
+++ b/compiler/dex/quick/quick_compiler_factory.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_LLVM_LLVM_COMPILER_H_
-#define ART_COMPILER_LLVM_LLVM_COMPILER_H_
+#ifndef ART_COMPILER_DEX_QUICK_QUICK_COMPILER_FACTORY_H_
+#define ART_COMPILER_DEX_QUICK_QUICK_COMPILER_FACTORY_H_
namespace art {
class Compiler;
class CompilerDriver;
-Compiler* CreateLLVMCompiler(CompilerDriver* driver);
+Compiler* CreateQuickCompiler(CompilerDriver* driver);
-}
+} // namespace art
-#endif // ART_COMPILER_LLVM_LLVM_COMPILER_H_
+#endif // ART_COMPILER_DEX_QUICK_QUICK_COMPILER_FACTORY_H_
diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc
index 0a98c80..8efafb2 100644
--- a/compiler/dex/quick/ralloc_util.cc
+++ b/compiler/dex/quick/ralloc_util.cc
@@ -16,10 +16,13 @@
/* This file contains register alloction support. */
-#include "dex/compiler_ir.h"
-#include "dex/compiler_internals.h"
#include "mir_to_lir-inl.h"
+#include "dex/compiler_ir.h"
+#include "dex/mir_graph.h"
+#include "driver/compiler_driver.h"
+#include "driver/dex_compilation_unit.h"
+
namespace art {
/*
diff --git a/compiler/dex/quick/resource_mask.cc b/compiler/dex/quick/resource_mask.cc
index 088bec8..8a27ecb 100644
--- a/compiler/dex/quick/resource_mask.cc
+++ b/compiler/dex/quick/resource_mask.cc
@@ -18,7 +18,9 @@
#include "resource_mask.h"
+#include "base/logging.h"
#include "utils/arena_allocator.h"
+#include "utils.h"
namespace art {
diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc
index 84d68d2..6f26b78 100644
--- a/compiler/dex/quick/x86/assemble_x86.cc
+++ b/compiler/dex/quick/x86/assemble_x86.cc
@@ -15,7 +15,10 @@
*/
#include "codegen_x86.h"
-#include "dex/quick/mir_to_lir-inl.h"
+
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
+#include "dex/quick/mir_to_lir.h"
#include "oat.h"
#include "x86_lir.h"
@@ -553,7 +556,7 @@
}
static bool NeedsRex(int32_t raw_reg) {
- return RegStorage::RegNum(raw_reg) > 7;
+ return raw_reg != kRIPReg && RegStorage::RegNum(raw_reg) > 7;
}
static uint8_t LowRegisterBits(int32_t raw_reg) {
@@ -689,7 +692,13 @@
entry->opcode != kX86Lea32RM && entry->opcode != kX86Lea64RM) {
DCHECK_NE(entry->flags & (IS_LOAD | IS_STORE), UINT64_C(0)) << entry->name;
}
- size += IS_SIMM8(displacement) ? 1 : 4;
+ if (raw_base == kRIPReg) {
+ DCHECK(cu_->target64) <<
+ "Attempt to use a 64-bit RIP adressing with instruction " << entry->name;
+ size += 4;
+ } else {
+ size += IS_SIMM8(displacement) ? 1 : 4;
+ }
}
}
size += entry->skeleton.immediate_bytes;
@@ -1022,14 +1031,24 @@
void X86Mir2Lir::EmitModrmDisp(uint8_t reg_or_opcode, uint8_t base, int32_t disp) {
DCHECK_LT(reg_or_opcode, 8);
- DCHECK_LT(base, 8);
- uint8_t modrm = (ModrmForDisp(base, disp) << 6) | (reg_or_opcode << 3) | base;
- code_buffer_.push_back(modrm);
- if (base == rs_rX86_SP_32.GetRegNum()) {
- // Special SIB for SP base
- code_buffer_.push_back(0 << 6 | rs_rX86_SP_32.GetRegNum() << 3 | rs_rX86_SP_32.GetRegNum());
+ if (base == kRIPReg) {
+ // x86_64 RIP handling: always 32 bit displacement.
+ uint8_t modrm = (0x0 << 6) | (reg_or_opcode << 3) | 0x5;
+ code_buffer_.push_back(modrm);
+ code_buffer_.push_back(disp & 0xFF);
+ code_buffer_.push_back((disp >> 8) & 0xFF);
+ code_buffer_.push_back((disp >> 16) & 0xFF);
+ code_buffer_.push_back((disp >> 24) & 0xFF);
+ } else {
+ DCHECK_LT(base, 8);
+ uint8_t modrm = (ModrmForDisp(base, disp) << 6) | (reg_or_opcode << 3) | base;
+ code_buffer_.push_back(modrm);
+ if (base == rs_rX86_SP_32.GetRegNum()) {
+ // Special SIB for SP base
+ code_buffer_.push_back(0 << 6 | rs_rX86_SP_32.GetRegNum() << 3 | rs_rX86_SP_32.GetRegNum());
+ }
+ EmitDisp(base, disp);
}
- EmitDisp(base, disp);
}
void X86Mir2Lir::EmitModrmSibDisp(uint8_t reg_or_opcode, uint8_t base, uint8_t index,
@@ -1141,7 +1160,7 @@
CheckValidByteRegister(entry, raw_reg);
EmitPrefixAndOpcode(entry, raw_reg, NO_REG, raw_base);
uint8_t low_reg = LowRegisterBits(raw_reg);
- uint8_t low_base = LowRegisterBits(raw_base);
+ uint8_t low_base = (raw_base == kRIPReg) ? raw_base : LowRegisterBits(raw_base);
EmitModrmDisp(low_reg, low_base, disp);
DCHECK_EQ(0, entry->skeleton.modrm_opcode);
DCHECK_EQ(0, entry->skeleton.ax_opcode);
@@ -1758,12 +1777,29 @@
LIR *target_lir = lir->target;
DCHECK(target_lir != NULL);
CodeOffset target = target_lir->offset;
- lir->operands[2] = target;
- int newSize = GetInsnSize(lir);
- if (newSize != lir->flags.size) {
- lir->flags.size = newSize;
- res = kRetryAll;
+ // Handle 64 bit RIP addressing.
+ if (lir->operands[1] == kRIPReg) {
+ // Offset is relative to next instruction.
+ lir->operands[2] = target - (lir->offset + lir->flags.size);
+ } else {
+ lir->operands[2] = target;
+ int newSize = GetInsnSize(lir);
+ if (newSize != lir->flags.size) {
+ lir->flags.size = newSize;
+ res = kRetryAll;
+ }
}
+ } else if (lir->flags.fixup == kFixupSwitchTable) {
+ DCHECK(cu_->target64);
+ DCHECK_EQ(lir->opcode, kX86Lea64RM) << "Unknown instruction: " << X86Mir2Lir::EncodingMap[lir->opcode].name;
+ DCHECK_EQ(lir->operands[1], static_cast<int>(kRIPReg));
+ // Grab the target offset from the saved data.
+ Mir2Lir::EmbeddedData* tab_rec =
+ reinterpret_cast<Mir2Lir::EmbeddedData*>(UnwrapPointer(lir->operands[4]));
+ CodeOffset target = tab_rec->offset;
+ // Handle 64 bit RIP addressing.
+ // Offset is relative to next instruction.
+ lir->operands[2] = target - (lir->offset + lir->flags.size);
}
break;
}
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index be10d93..284e8f6 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -17,7 +17,10 @@
/* This file contains codegen for the X86 ISA */
#include "codegen_x86.h"
+
+#include "base/logging.h"
#include "dex/quick/mir_to_lir-inl.h"
+#include "driver/compiler_driver.h"
#include "gc/accounting/card_table.h"
#include "mirror/art_method.h"
#include "mirror/object_array-inl.h"
@@ -34,84 +37,6 @@
}
/*
- * We override InsertCaseLabel, because the first parameter represents
- * a basic block id, instead of a dex offset.
- */
-LIR* X86Mir2Lir::InsertCaseLabel(DexOffset bbid, int keyVal) {
- LIR* boundary_lir = &block_label_list_[bbid];
- LIR* res = boundary_lir;
- if (cu_->verbose) {
- // Only pay the expense if we're pretty-printing.
- LIR* new_label = static_cast<LIR*>(arena_->Alloc(sizeof(LIR), kArenaAllocLIR));
- BasicBlock* bb = mir_graph_->GetBasicBlock(bbid);
- DCHECK(bb != nullptr);
- new_label->dalvik_offset = bb->start_offset;;
- new_label->opcode = kPseudoCaseLabel;
- new_label->operands[0] = keyVal;
- new_label->flags.fixup = kFixupLabel;
- DCHECK(!new_label->flags.use_def_invalid);
- new_label->u.m.def_mask = &kEncodeAll;
- InsertLIRAfter(boundary_lir, new_label);
- res = new_label;
- }
- return res;
-}
-
-void X86Mir2Lir::MarkPackedCaseLabels(Mir2Lir::SwitchTable* tab_rec) {
- const uint16_t* table = tab_rec->table;
- const int32_t *targets = reinterpret_cast<const int32_t*>(&table[4]);
- int entries = table[1];
- int low_key = s4FromSwitchData(&table[2]);
- for (int i = 0; i < entries; i++) {
- // The value at targets[i] is a basic block id, instead of a dex offset.
- tab_rec->targets[i] = InsertCaseLabel(targets[i], i + low_key);
- }
-}
-
-/*
- * We convert and create a new packed switch table that stores
- * basic block ids to targets[] by examining successor blocks.
- * Note that the original packed switch table stores dex offsets to targets[].
- */
-const uint16_t* X86Mir2Lir::ConvertPackedSwitchTable(MIR* mir, const uint16_t* table) {
- /*
- * The original packed switch data format:
- * ushort ident = 0x0100 magic value
- * ushort size number of entries in the table
- * int first_key first (and lowest) switch case value
- * int targets[size] branch targets, relative to switch opcode
- *
- * Total size is (4+size*2) 16-bit code units.
- *
- * Note that the new packed switch data format is the same as the original
- * format, except that targets[] are basic block ids.
- *
- */
- BasicBlock* bb = mir_graph_->GetBasicBlock(mir->bb);
- DCHECK(bb != nullptr);
- // Get the number of entries.
- int entries = table[1];
- const int32_t* as_int32 = reinterpret_cast<const int32_t*>(&table[2]);
- int32_t starting_key = as_int32[0];
- // Create a new table.
- int size = sizeof(uint16_t) * (4 + entries * 2);
- uint16_t* new_table = reinterpret_cast<uint16_t*>(arena_->Alloc(size, kArenaAllocMisc));
- // Copy ident, size, and first_key to the new table.
- memcpy(new_table, table, sizeof(uint16_t) * 4);
- // Get the new targets.
- int32_t* new_targets = reinterpret_cast<int32_t*>(&new_table[4]);
- // Find out targets for each entry.
- int i = 0;
- for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) {
- DCHECK_EQ(starting_key + i, successor_block_info->key);
- // Save target basic block id.
- new_targets[i++] = successor_block_info->block;
- }
- DCHECK_EQ(i, entries);
- return new_table;
-}
-
-/*
* Code pattern will look something like:
*
* mov r_val, ..
@@ -128,39 +53,19 @@
* done:
*/
void X86Mir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
- const uint16_t* old_table = mir_graph_->GetTable(mir, table_offset);
- const uint16_t* table = ConvertPackedSwitchTable(mir, old_table);
+ const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
// Add the table to the list - we'll process it later
SwitchTable* tab_rec =
static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
+ tab_rec->switch_mir = mir;
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
int size = table[1];
- tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*),
- kArenaAllocLIR));
switch_tables_.push_back(tab_rec);
// Get the switch value
rl_src = LoadValue(rl_src, kCoreReg);
- // NewLIR0(kX86Bkpt);
- // Materialize a pointer to the switch table
- RegStorage start_of_method_reg;
- if (base_of_code_ != nullptr) {
- // We can use the saved value.
- RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
- if (rl_method.wide) {
- rl_method = LoadValueWide(rl_method, kCoreReg);
- } else {
- rl_method = LoadValue(rl_method, kCoreReg);
- }
- start_of_method_reg = rl_method.reg;
- store_method_addr_used_ = true;
- } else {
- start_of_method_reg = AllocTempRef();
- NewLIR1(kX86StartOfMethod, start_of_method_reg.GetReg());
- }
- DCHECK_EQ(start_of_method_reg.Is64Bit(), cu_->target64);
int low_key = s4FromSwitchData(&table[2]);
RegStorage keyReg;
// Remove the bias, if necessary
@@ -170,19 +75,49 @@
keyReg = AllocTemp();
OpRegRegImm(kOpSub, keyReg, rl_src.reg, low_key);
}
+
// Bounds check - if < 0 or >= size continue following switch
OpRegImm(kOpCmp, keyReg, size - 1);
LIR* branch_over = OpCondBranch(kCondHi, NULL);
- // Load the displacement from the switch table
- RegStorage disp_reg = AllocTemp();
- NewLIR5(kX86PcRelLoadRA, disp_reg.GetReg(), start_of_method_reg.GetReg(), keyReg.GetReg(),
- 2, WrapPointer(tab_rec));
- // Add displacement to start of method
- OpRegReg(kOpAdd, start_of_method_reg, cu_->target64 ? As64BitReg(disp_reg) : disp_reg);
+ RegStorage addr_for_jump;
+ if (cu_->target64) {
+ RegStorage table_base = AllocTempWide();
+ // Load the address of the table into table_base.
+ LIR* lea = RawLIR(current_dalvik_offset_, kX86Lea64RM, table_base.GetReg(), kRIPReg,
+ 256, 0, WrapPointer(tab_rec));
+ lea->flags.fixup = kFixupSwitchTable;
+ AppendLIR(lea);
+
+ // Load the offset from the table out of the table.
+ addr_for_jump = AllocTempWide();
+ NewLIR5(kX86MovsxdRA, addr_for_jump.GetReg(), table_base.GetReg(), keyReg.GetReg(), 2, 0);
+
+ // Add the offset from the table to the table base.
+ OpRegReg(kOpAdd, addr_for_jump, table_base);
+ } else {
+ // Materialize a pointer to the switch table.
+ RegStorage start_of_method_reg;
+ if (base_of_code_ != nullptr) {
+ // We can use the saved value.
+ RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
+ rl_method = LoadValue(rl_method, kCoreReg);
+ start_of_method_reg = rl_method.reg;
+ store_method_addr_used_ = true;
+ } else {
+ start_of_method_reg = AllocTempRef();
+ NewLIR1(kX86StartOfMethod, start_of_method_reg.GetReg());
+ }
+ // Load the displacement from the switch table.
+ addr_for_jump = AllocTemp();
+ NewLIR5(kX86PcRelLoadRA, addr_for_jump.GetReg(), start_of_method_reg.GetReg(), keyReg.GetReg(),
+ 2, WrapPointer(tab_rec));
+ // Add displacement to start of method.
+ OpRegReg(kOpAdd, addr_for_jump, start_of_method_reg);
+ }
+
// ..and go!
- LIR* switch_branch = NewLIR1(kX86JmpR, start_of_method_reg.GetReg());
- tab_rec->anchor = switch_branch;
+ tab_rec->anchor = NewLIR1(kX86JmpR, addr_for_jump.GetReg());
/* branch_over target here */
LIR* target = NewLIR0(kPseudoTargetLabel);
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index 9cb0bf5..ca60400 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -17,7 +17,9 @@
#ifndef ART_COMPILER_DEX_QUICK_X86_CODEGEN_X86_H_
#define ART_COMPILER_DEX_QUICK_X86_CODEGEN_X86_H_
-#include "dex/compiler_internals.h"
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
+#include "dex/mir_graph.h"
#include "dex/quick/mir_to_lir.h"
#include "x86_lir.h"
@@ -28,40 +30,41 @@
class X86Mir2Lir : public Mir2Lir {
protected:
- class InToRegStorageMapper {
- public:
- virtual RegStorage GetNextReg(bool is_double_or_float, bool is_wide, bool is_ref) = 0;
- virtual ~InToRegStorageMapper() {}
- };
-
class InToRegStorageX86_64Mapper : public InToRegStorageMapper {
public:
- explicit InToRegStorageX86_64Mapper(Mir2Lir* ml) : ml_(ml), cur_core_reg_(0), cur_fp_reg_(0) {}
- virtual ~InToRegStorageX86_64Mapper() {}
- virtual RegStorage GetNextReg(bool is_double_or_float, bool is_wide, bool is_ref);
+ explicit InToRegStorageX86_64Mapper(Mir2Lir* m2l)
+ : m2l_(m2l), cur_core_reg_(0), cur_fp_reg_(0) {}
+ virtual RegStorage GetNextReg(ShortyArg arg);
+ virtual void Reset() OVERRIDE {
+ cur_core_reg_ = 0;
+ cur_fp_reg_ = 0;
+ }
protected:
- Mir2Lir* ml_;
- private:
- int cur_core_reg_;
- int cur_fp_reg_;
+ Mir2Lir* m2l_;
+ size_t cur_core_reg_;
+ size_t cur_fp_reg_;
};
- class InToRegStorageMapping {
+ class InToRegStorageX86Mapper : public InToRegStorageX86_64Mapper {
public:
- InToRegStorageMapping() : max_mapped_in_(0), is_there_stack_mapped_(false),
- initialized_(false) {}
- void Initialize(RegLocation* arg_locs, int count, InToRegStorageMapper* mapper);
- int GetMaxMappedIn() { return max_mapped_in_; }
- bool IsThereStackMapped() { return is_there_stack_mapped_; }
- RegStorage Get(int in_position);
- bool IsInitialized() { return initialized_; }
- private:
- std::map<int, RegStorage> mapping_;
- int max_mapped_in_;
- bool is_there_stack_mapped_;
- bool initialized_;
+ explicit InToRegStorageX86Mapper(Mir2Lir* m2l)
+ : InToRegStorageX86_64Mapper(m2l) { }
+ virtual RegStorage GetNextReg(ShortyArg arg);
};
+ InToRegStorageX86_64Mapper in_to_reg_storage_x86_64_mapper_;
+ InToRegStorageX86Mapper in_to_reg_storage_x86_mapper_;
+ InToRegStorageMapper* GetResetedInToRegStorageMapper() OVERRIDE {
+ InToRegStorageMapper* res;
+ if (cu_->target64) {
+ res = &in_to_reg_storage_x86_64_mapper_;
+ } else {
+ res = &in_to_reg_storage_x86_mapper_;
+ }
+ res->Reset();
+ return res;
+ }
+
class ExplicitTempRegisterLock {
public:
ExplicitTempRegisterLock(X86Mir2Lir* mir_to_lir, int n_regs, ...);
@@ -71,6 +74,8 @@
X86Mir2Lir* const mir_to_lir_;
};
+ virtual int GenDalvikArgsBulkCopy(CallInfo* info, int first, int count) OVERRIDE;
+
public:
X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
@@ -108,9 +113,12 @@
if (cu_->target64) {
return As64BitReg(TargetReg32(symbolic_reg));
} else {
+ if (symbolic_reg >= kFArg0 && symbolic_reg <= kFArg3) {
+ // We want an XMM, not a pair.
+ return As64BitReg(TargetReg32(symbolic_reg));
+ }
// x86: construct a pair.
DCHECK((kArg0 <= symbolic_reg && symbolic_reg < kArg3) ||
- (kFArg0 <= symbolic_reg && symbolic_reg < kFArg3) ||
(kRet0 == symbolic_reg));
return RegStorage::MakeRegPair(TargetReg32(symbolic_reg),
TargetReg32(static_cast<SpecialTargetRegister>(symbolic_reg + 1)));
@@ -125,8 +133,6 @@
return TargetReg(symbolic_reg, cu_->target64 ? kWide : kNotWide);
}
- RegStorage GetArgMappingToPhysicalReg(int arg_num) OVERRIDE;
-
RegLocation GetReturnAlt() OVERRIDE;
RegLocation GetReturnWideAlt() OVERRIDE;
RegLocation LocCReturn() OVERRIDE;
@@ -265,11 +271,8 @@
int first_bit, int second_bit) OVERRIDE;
void GenNegDouble(RegLocation rl_dest, RegLocation rl_src) OVERRIDE;
void GenNegFloat(RegLocation rl_dest, RegLocation rl_src) OVERRIDE;
- const uint16_t* ConvertPackedSwitchTable(MIR* mir, const uint16_t* table);
void GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) OVERRIDE;
void GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) OVERRIDE;
- LIR* InsertCaseLabel(DexOffset vaddr, int keyVal) OVERRIDE;
- void MarkPackedCaseLabels(Mir2Lir::SwitchTable* tab_rec) OVERRIDE;
/**
* @brief Implement instanceof a final class with x86 specific code.
@@ -350,22 +353,7 @@
void LoadClassType(const DexFile& dex_file, uint32_t type_idx,
SpecialTargetRegister symbolic_reg) OVERRIDE;
- void FlushIns(RegLocation* ArgLocs, RegLocation rl_method) OVERRIDE;
-
NextCallInsn GetNextSDCallInsn() OVERRIDE;
- int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel,
- NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx,
- uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
- bool skip_this) OVERRIDE;
-
- int GenDalvikArgsRange(CallInfo* info, int call_state, LIR** pcrLabel,
- NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx,
- uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
- bool skip_this) OVERRIDE;
/*
* @brief Generate a relative call to the method that will be patched at link time.
@@ -439,8 +427,6 @@
LIR* StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement,
RegStorage r_src, OpSize size, int opt_flags = 0);
- RegStorage GetCoreArgMappingToPhysicalReg(int core_arg_num) const;
-
int AssignInsnOffsets();
void AssignOffsets();
AssemblerStatus AssembleInstructions(CodeOffset start_addr);
@@ -1000,8 +986,6 @@
*/
static void DumpRegLocation(RegLocation loc);
- InToRegStorageMapping in_to_reg_storage_mapping_;
-
private:
void SwapBits(RegStorage result_reg, int shift, int32_t value);
void SwapBits64(RegStorage result_reg, int shift, int64_t value);
diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc
index 4825db6..d8616a7 100755
--- a/compiler/dex/quick/x86/fp_x86.cc
+++ b/compiler/dex/quick/x86/fp_x86.cc
@@ -15,6 +15,8 @@
*/
#include "codegen_x86.h"
+
+#include "base/logging.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "dex/reg_storage_eq.h"
#include "x86_lir.h"
@@ -599,8 +601,12 @@
}
bool X86Mir2Lir::GenInlinedSqrt(CallInfo* info) {
- RegLocation rl_src = info->args[0];
RegLocation rl_dest = InlineTargetWide(info); // double place for result
+ if (rl_dest.s_reg_low == INVALID_SREG) {
+ // Result is unused, the code is dead. Inlining successful, no code generated.
+ return true;
+ }
+ RegLocation rl_src = info->args[0];
rl_src = LoadValueWide(rl_src, kFPReg);
RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
NewLIR2(kX86SqrtsdRR, rl_result.reg.GetReg(), rl_src.reg.GetReg());
@@ -722,9 +728,13 @@
bool X86Mir2Lir::GenInlinedMinMaxFP(CallInfo* info, bool is_min, bool is_double) {
if (is_double) {
+ RegLocation rl_dest = InlineTargetWide(info);
+ if (rl_dest.s_reg_low == INVALID_SREG) {
+ // Result is unused, the code is dead. Inlining successful, no code generated.
+ return true;
+ }
RegLocation rl_src1 = LoadValueWide(info->args[0], kFPReg);
RegLocation rl_src2 = LoadValueWide(info->args[2], kFPReg);
- RegLocation rl_dest = InlineTargetWide(info);
RegLocation rl_result = EvalLocWide(rl_dest, kFPReg, true);
// Avoid src2 corruption by OpRegCopyWide.
@@ -775,9 +785,13 @@
branch_exit_equal->target = NewLIR0(kPseudoTargetLabel);
StoreValueWide(rl_dest, rl_result);
} else {
+ RegLocation rl_dest = InlineTarget(info);
+ if (rl_dest.s_reg_low == INVALID_SREG) {
+ // Result is unused, the code is dead. Inlining successful, no code generated.
+ return true;
+ }
RegLocation rl_src1 = LoadValue(info->args[0], kFPReg);
RegLocation rl_src2 = LoadValue(info->args[1], kFPReg);
- RegLocation rl_dest = InlineTarget(info);
RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
// Avoid src2 corruption by OpRegCopyWide.
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 80cdc83..4fe7a43 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -17,10 +17,13 @@
/* This file contains codegen for the X86 ISA */
#include "codegen_x86.h"
+
+#include "base/logging.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "dex/reg_storage_eq.h"
#include "mirror/art_method.h"
#include "mirror/array-inl.h"
+#include "utils.h"
#include "x86_lir.h"
namespace art {
@@ -656,7 +659,7 @@
NewLIR3(kX86Lea32RM, rl_result.reg.GetReg(), rl_src.reg.GetReg(), std::abs(imm) - 1);
NewLIR2(kX86Test32RR, rl_src.reg.GetReg(), rl_src.reg.GetReg());
OpCondRegReg(kOpCmov, kCondPl, rl_result.reg, rl_src.reg);
- int shift_amount = LowestSetBit(imm);
+ int shift_amount = CTZ(imm);
OpRegImm(kOpAsr, rl_result.reg, shift_amount);
if (imm < 0) {
OpReg(kOpNeg, rl_result.reg);
@@ -947,12 +950,16 @@
}
// Get the two arguments to the invoke and place them in GP registers.
+ RegLocation rl_dest = (is_long) ? InlineTargetWide(info) : InlineTarget(info);
+ if (rl_dest.s_reg_low == INVALID_SREG) {
+ // Result is unused, the code is dead. Inlining successful, no code generated.
+ return true;
+ }
RegLocation rl_src1 = info->args[0];
RegLocation rl_src2 = (is_long) ? info->args[2] : info->args[1];
rl_src1 = (is_long) ? LoadValueWide(rl_src1, kCoreReg) : LoadValue(rl_src1, kCoreReg);
rl_src2 = (is_long) ? LoadValueWide(rl_src2, kCoreReg) : LoadValue(rl_src2, kCoreReg);
- RegLocation rl_dest = (is_long) ? InlineTargetWide(info) : InlineTarget(info);
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
/*
@@ -987,6 +994,11 @@
}
bool X86Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
+ RegLocation rl_dest = size == k64 ? InlineTargetWide(info) : InlineTarget(info);
+ if (rl_dest.s_reg_low == INVALID_SREG) {
+ // Result is unused, the code is dead. Inlining successful, no code generated.
+ return true;
+ }
RegLocation rl_src_address = info->args[0]; // long address
RegLocation rl_address;
if (!cu_->target64) {
@@ -995,7 +1007,6 @@
} else {
rl_address = LoadValueWide(rl_src_address, kCoreReg);
}
- RegLocation rl_dest = size == k64 ? InlineTargetWide(info) : InlineTarget(info);
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
// Unaligned access is allowed on x86.
LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile);
@@ -1237,10 +1248,14 @@
}
bool X86Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) {
+ RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info);
+ if (rl_dest.s_reg_low == INVALID_SREG) {
+ // Result is unused, the code is dead. Inlining successful, no code generated.
+ return true;
+ }
RegLocation rl_src_i = info->args[0];
RegLocation rl_i = (size == k64) ? LoadValueWide(rl_src_i, kCoreReg)
: LoadValue(rl_src_i, kCoreReg);
- RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info);
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
if (size == k64) {
if (cu_->instruction_set == kX86_64) {
@@ -1289,6 +1304,18 @@
}
LIR* X86Mir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) {
+ if (cu_->target64) {
+ // We can do this directly using RIP addressing.
+ // We don't know the proper offset for the value, so pick one that will force
+ // 4 byte offset. We will fix this up in the assembler later to have the right
+ // value.
+ ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
+ LIR* res = NewLIR3(kX86Mov32RM, reg.GetReg(), kRIPReg, 256);
+ res->target = target;
+ res->flags.fixup = kFixupLoad;
+ return res;
+ }
+
CHECK(base_of_code_ != nullptr);
// Address the start of the method
@@ -1309,7 +1336,6 @@
0, 0, target);
res->target = target;
res->flags.fixup = kFixupLoad;
- store_method_addr_used_ = true;
return res;
}
@@ -1616,7 +1642,7 @@
GenArithOpLong(Instruction::ADD_LONG, rl_dest, rl_src1, rl_src1, flags);
return true;
} else if (IsPowerOfTwo(val)) {
- int shift_amount = LowestSetBit(val);
+ int shift_amount = CTZ(val);
if (!PartiallyIntersects(rl_src1, rl_dest)) {
rl_src1 = LoadValueWide(rl_src1, kCoreReg);
RegLocation rl_result = GenShiftImmOpLong(Instruction::SHL_LONG, rl_dest, rl_src1,
@@ -2059,7 +2085,7 @@
OpRegReg(kOpAdd, rl_result.reg, rl_src.reg);
NewLIR2(kX86Test64RR, rl_src.reg.GetReg(), rl_src.reg.GetReg());
OpCondRegReg(kOpCmov, kCondPl, rl_result.reg, rl_src.reg);
- int shift_amount = LowestSetBit(imm);
+ int shift_amount = CTZ(imm);
OpRegImm(kOpAsr, rl_result.reg, shift_amount);
if (imm < 0) {
OpReg(kOpNeg, rl_result.reg);
@@ -2308,7 +2334,7 @@
*/
void X86Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
RegLocation rl_index, RegLocation rl_dest, int scale) {
- RegisterClass reg_class = RegClassBySize(size);
+ RegisterClass reg_class = RegClassForFieldLoadStore(size, false);
int len_offset = mirror::Array::LengthOffset().Int32Value();
RegLocation rl_result;
rl_array = LoadValue(rl_array, kRefReg);
@@ -2357,7 +2383,7 @@
*/
void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
- RegisterClass reg_class = RegClassBySize(size);
+ RegisterClass reg_class = RegClassForFieldLoadStore(size, false);
int len_offset = mirror::Array::LengthOffset().Int32Value();
int data_offset;
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index 998aeff..c4adb09 100755
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -14,16 +14,19 @@
* limitations under the License.
*/
+#include "codegen_x86.h"
+
#include <cstdarg>
#include <inttypes.h>
#include <string>
#include "arch/instruction_set_features.h"
#include "backend_x86.h"
-#include "codegen_x86.h"
-#include "dex/compiler_internals.h"
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "dex/reg_storage_eq.h"
+#include "driver/compiler_driver.h"
#include "mirror/array-inl.h"
#include "mirror/art_method.h"
#include "mirror/string.h"
@@ -177,10 +180,10 @@
RegStorage::InvalidReg(), // kArg5
RegStorage::InvalidReg(), // kArg6
RegStorage::InvalidReg(), // kArg7
- rs_rAX, // kFArg0
- rs_rCX, // kFArg1
- rs_rDX, // kFArg2
- rs_rBX, // kFArg3
+ rs_fr0, // kFArg0
+ rs_fr1, // kFArg1
+ rs_fr2, // kFArg2
+ rs_fr3, // kFArg3
RegStorage::InvalidReg(), // kFArg4
RegStorage::InvalidReg(), // kFArg5
RegStorage::InvalidReg(), // kFArg6
@@ -197,7 +200,7 @@
rs_rDX, // kRet1
rs_rAX, // kInvokeTgt
rs_rAX, // kHiddenArg - used to hold the method index before copying to fr0.
- rs_fr0, // kHiddenFpArg
+ rs_fr7, // kHiddenFpArg
rs_rCX, // kCount
};
@@ -206,7 +209,7 @@
RegStorage::InvalidReg(), // kSelf - Thread pointer.
RegStorage::InvalidReg(), // kSuspend - Used to reduce suspend checks for some targets.
RegStorage::InvalidReg(), // kLr - no register as the return address is pushed on entry.
- RegStorage::InvalidReg(), // kPc - TODO: RIP based addressing.
+ RegStorage(kRIPReg), // kPc
rs_rX86_SP_32, // kSp
rs_rDI, // kArg0
rs_rSI, // kArg1
@@ -542,13 +545,13 @@
LockTemp(TargetReg32(kArg1));
LockTemp(TargetReg32(kArg2));
LockTemp(TargetReg32(kArg3));
+ LockTemp(TargetReg32(kFArg0));
+ LockTemp(TargetReg32(kFArg1));
+ LockTemp(TargetReg32(kFArg2));
+ LockTemp(TargetReg32(kFArg3));
if (cu_->target64) {
LockTemp(TargetReg32(kArg4));
LockTemp(TargetReg32(kArg5));
- LockTemp(TargetReg32(kFArg0));
- LockTemp(TargetReg32(kFArg1));
- LockTemp(TargetReg32(kFArg2));
- LockTemp(TargetReg32(kFArg3));
LockTemp(TargetReg32(kFArg4));
LockTemp(TargetReg32(kFArg5));
LockTemp(TargetReg32(kFArg6));
@@ -562,13 +565,14 @@
FreeTemp(TargetReg32(kArg1));
FreeTemp(TargetReg32(kArg2));
FreeTemp(TargetReg32(kArg3));
+ FreeTemp(TargetReg32(kHiddenArg));
+ FreeTemp(TargetReg32(kFArg0));
+ FreeTemp(TargetReg32(kFArg1));
+ FreeTemp(TargetReg32(kFArg2));
+ FreeTemp(TargetReg32(kFArg3));
if (cu_->target64) {
FreeTemp(TargetReg32(kArg4));
FreeTemp(TargetReg32(kArg5));
- FreeTemp(TargetReg32(kFArg0));
- FreeTemp(TargetReg32(kFArg1));
- FreeTemp(TargetReg32(kFArg2));
- FreeTemp(TargetReg32(kFArg3));
FreeTemp(TargetReg32(kFArg4));
FreeTemp(TargetReg32(kFArg5));
FreeTemp(TargetReg32(kFArg6));
@@ -595,7 +599,7 @@
}
bool X86Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
- if (!cu_->GetInstructionSetFeatures()->IsSmp()) {
+ if (!cu_->compiler_driver->GetInstructionSetFeatures()->IsSmp()) {
return false;
}
// Start off with using the last LIR as the barrier. If it is not enough, then we will update it.
@@ -662,6 +666,12 @@
xp_reg_info->SetIsTemp(true);
}
+ // Special Handling for x86_64 RIP addressing.
+ if (cu_->target64) {
+ RegisterInfo* info = new (arena_) RegisterInfo(RegStorage(kRIPReg), kEncodeNone);
+ reginfo_map_[kRIPReg] = info;
+ }
+
// Alias single precision xmm to double xmms.
// TODO: as needed, add larger vector sizes - alias all to the largest.
for (RegisterInfo* info : reg_pool_->sp_regs_) {
@@ -791,6 +801,12 @@
}
RegisterClass X86Mir2Lir::RegClassForFieldLoadStore(OpSize size, bool is_volatile) {
+ // Prefer XMM registers. Fixes a problem with iget/iput to a FP when cached temporary
+ // with same VR is a Core register.
+ if (size == kSingle || size == kDouble) {
+ return kFPReg;
+ }
+
// X86_64 can handle any size.
if (cu_->target64) {
return RegClassBySize(size);
@@ -808,6 +824,7 @@
X86Mir2Lir::X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
: Mir2Lir(cu, mir_graph, arena),
+ in_to_reg_storage_x86_64_mapper_(this), in_to_reg_storage_x86_mapper_(this),
base_of_code_(nullptr), store_method_addr_(false), store_method_addr_used_(false),
method_address_insns_(arena->Adapter()),
class_type_address_insns_(arena->Adapter()),
@@ -1608,9 +1625,6 @@
}
void X86Mir2Lir::AppendOpcodeWithConst(X86OpCode opcode, int reg, MIR* mir) {
- // The literal pool needs position independent logic.
- store_method_addr_used_ = true;
-
// To deal with correct memory ordering, reverse order of constants.
int32_t constants[4];
constants[3] = mir->dalvikInsn.arg[0];
@@ -1624,20 +1638,28 @@
data_target = AddVectorLiteral(constants);
}
- // Address the start of the method.
- RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
- if (rl_method.wide) {
- rl_method = LoadValueWide(rl_method, kCoreReg);
- } else {
- rl_method = LoadValue(rl_method, kCoreReg);
- }
-
// Load the proper value from the literal area.
// We don't know the proper offset for the value, so pick one that will force
- // 4 byte offset. We will fix this up in the assembler later to have the right
- // value.
+ // 4 byte offset. We will fix this up in the assembler later to have the
+ // right value.
+ LIR* load;
ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
- LIR *load = NewLIR3(opcode, reg, rl_method.reg.GetReg(), 256 /* bogus */);
+ if (cu_->target64) {
+ load = NewLIR3(opcode, reg, kRIPReg, 256 /* bogus */);
+ } else {
+ // Address the start of the method.
+ RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
+ if (rl_method.wide) {
+ rl_method = LoadValueWide(rl_method, kCoreReg);
+ } else {
+ rl_method = LoadValue(rl_method, kCoreReg);
+ }
+
+ load = NewLIR3(opcode, reg, rl_method.reg.GetReg(), 256 /* bogus */);
+
+ // The literal pool needs position independent logic.
+ store_method_addr_used_ = true;
+ }
load->flags.fixup = kFixupLoad;
load->target = data_target;
}
@@ -2197,18 +2219,36 @@
// Handle float case.
// TODO Add support for fast math (not value safe) and do horizontal add in that case.
+ int extract_index = mir->dalvikInsn.arg[0];
+
rl_result = EvalLoc(rl_dest, kFPReg, true);
NewLIR2(kX86PxorRR, rl_result.reg.GetReg(), rl_result.reg.GetReg());
- NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), vector_src.GetReg());
- // Since FP must keep order of operation for value safety, we shift to low
- // 32-bits and add to result.
- for (int i = 0; i < 3; i++) {
- NewLIR3(kX86ShufpsRRI, vector_src.GetReg(), vector_src.GetReg(), 0x39);
+ if (LIKELY(extract_index != 0)) {
+ // We know the index of element which we want to extract. We want to extract it and
+ // keep values in vector register correct for future use. So the way we act is:
+ // 1. Generate shuffle mask that allows to swap zeroth and required elements;
+ // 2. Shuffle vector register with this mask;
+ // 3. Extract zeroth element where required value lies;
+ // 4. Shuffle with same mask again to restore original values in vector register.
+ // The mask is generated from equivalence mask 0b11100100 swapping 0th and extracted
+ // element indices.
+ int shuffle[4] = {0b00, 0b01, 0b10, 0b11};
+ shuffle[0] = extract_index;
+ shuffle[extract_index] = 0;
+ int mask = 0;
+ for (int i = 0; i < 4; i++) {
+ mask |= (shuffle[i] << (2 * i));
+ }
+ NewLIR3(kX86ShufpsRRI, vector_src.GetReg(), vector_src.GetReg(), mask);
+ NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), vector_src.GetReg());
+ NewLIR3(kX86ShufpsRRI, vector_src.GetReg(), vector_src.GetReg(), mask);
+ } else {
+ // We need to extract zeroth element and don't need any complex stuff to do it.
NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), vector_src.GetReg());
}
- StoreValue(rl_dest, rl_result);
+ StoreFinalValue(rl_dest, rl_result);
} else if (opsize == kDouble) {
// TODO Handle double case.
LOG(FATAL) << "Unsupported add reduce for double.";
@@ -2263,9 +2303,9 @@
StoreFinalValue(rl_dest, rl_result);
} else {
int displacement = SRegOffset(rl_result.s_reg_low);
+ ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
LIR *l = NewLIR4(extr_opcode, rs_rX86_SP_32.GetReg(), displacement, vector_src.GetReg(),
extract_index);
- AnnotateDalvikRegAccess(l, displacement >> 2, true /* is_load */, is_wide /* is_64bit */);
AnnotateDalvikRegAccess(l, displacement >> 2, false /* is_load */, is_wide /* is_64bit */);
}
}
@@ -2396,452 +2436,60 @@
}
// ------------ ABI support: mapping of args to physical registers -------------
-RegStorage X86Mir2Lir::InToRegStorageX86_64Mapper::GetNextReg(bool is_double_or_float, bool is_wide,
- bool is_ref) {
+RegStorage X86Mir2Lir::InToRegStorageX86_64Mapper::GetNextReg(ShortyArg arg) {
const SpecialTargetRegister coreArgMappingToPhysicalReg[] = {kArg1, kArg2, kArg3, kArg4, kArg5};
- const int coreArgMappingToPhysicalRegSize = sizeof(coreArgMappingToPhysicalReg) /
- sizeof(SpecialTargetRegister);
+ const size_t coreArgMappingToPhysicalRegSize = arraysize(coreArgMappingToPhysicalReg);
const SpecialTargetRegister fpArgMappingToPhysicalReg[] = {kFArg0, kFArg1, kFArg2, kFArg3,
kFArg4, kFArg5, kFArg6, kFArg7};
- const int fpArgMappingToPhysicalRegSize = sizeof(fpArgMappingToPhysicalReg) /
- sizeof(SpecialTargetRegister);
+ const size_t fpArgMappingToPhysicalRegSize = arraysize(fpArgMappingToPhysicalReg);
- if (is_double_or_float) {
+ if (arg.IsFP()) {
if (cur_fp_reg_ < fpArgMappingToPhysicalRegSize) {
- return ml_->TargetReg(fpArgMappingToPhysicalReg[cur_fp_reg_++], is_wide ? kWide : kNotWide);
+ return m2l_->TargetReg(fpArgMappingToPhysicalReg[cur_fp_reg_++],
+ arg.IsWide() ? kWide : kNotWide);
}
} else {
if (cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
- return ml_->TargetReg(coreArgMappingToPhysicalReg[cur_core_reg_++],
- is_ref ? kRef : (is_wide ? kWide : kNotWide));
+ return m2l_->TargetReg(coreArgMappingToPhysicalReg[cur_core_reg_++],
+ arg.IsRef() ? kRef : (arg.IsWide() ? kWide : kNotWide));
}
}
return RegStorage::InvalidReg();
}
-RegStorage X86Mir2Lir::InToRegStorageMapping::Get(int in_position) {
- DCHECK(IsInitialized());
- auto res = mapping_.find(in_position);
- return res != mapping_.end() ? res->second : RegStorage::InvalidReg();
-}
+RegStorage X86Mir2Lir::InToRegStorageX86Mapper::GetNextReg(ShortyArg arg) {
+ const SpecialTargetRegister coreArgMappingToPhysicalReg[] = {kArg1, kArg2, kArg3};
+ const size_t coreArgMappingToPhysicalRegSize = arraysize(coreArgMappingToPhysicalReg);
+ const SpecialTargetRegister fpArgMappingToPhysicalReg[] = {kFArg0, kFArg1, kFArg2, kFArg3};
+ const size_t fpArgMappingToPhysicalRegSize = arraysize(fpArgMappingToPhysicalReg);
-void X86Mir2Lir::InToRegStorageMapping::Initialize(RegLocation* arg_locs, int count,
- InToRegStorageMapper* mapper) {
- DCHECK(mapper != nullptr);
- max_mapped_in_ = -1;
- is_there_stack_mapped_ = false;
- for (int in_position = 0; in_position < count; in_position++) {
- RegStorage reg = mapper->GetNextReg(arg_locs[in_position].fp,
- arg_locs[in_position].wide, arg_locs[in_position].ref);
- if (reg.Valid()) {
- mapping_[in_position] = reg;
- max_mapped_in_ = std::max(max_mapped_in_, in_position);
- if (arg_locs[in_position].wide) {
- // We covered 2 args, so skip the next one
- in_position++;
- }
- } else {
- is_there_stack_mapped_ = true;
- }
+ RegStorage result = RegStorage::InvalidReg();
+ if (arg.IsFP()) {
+ if (cur_fp_reg_ < fpArgMappingToPhysicalRegSize) {
+ return m2l_->TargetReg(fpArgMappingToPhysicalReg[cur_fp_reg_++],
+ arg.IsWide() ? kWide : kNotWide);
+ }
+ } else if (cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
+ result = m2l_->TargetReg(coreArgMappingToPhysicalReg[cur_core_reg_++],
+ arg.IsRef() ? kRef : kNotWide);
+ if (arg.IsWide()) {
+ // This must be a long, as double is handled above.
+ // Ensure that we don't split a long across the last register and the stack.
+ if (cur_core_reg_ == coreArgMappingToPhysicalRegSize) {
+ // Leave the last core register unused and force the whole long to the stack.
+ cur_core_reg_++;
+ result = RegStorage::InvalidReg();
+ } else if (cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
+ result = RegStorage::MakeRegPair(
+ result, m2l_->TargetReg(coreArgMappingToPhysicalReg[cur_core_reg_++], kNotWide));
+ }
+ }
}
- initialized_ = true;
-}
-
-RegStorage X86Mir2Lir::GetArgMappingToPhysicalReg(int arg_num) {
- if (!cu_->target64) {
- return GetCoreArgMappingToPhysicalReg(arg_num);
- }
-
- if (!in_to_reg_storage_mapping_.IsInitialized()) {
- int start_vreg = cu_->mir_graph->GetFirstInVR();
- RegLocation* arg_locs = &mir_graph_->reg_location_[start_vreg];
-
- InToRegStorageX86_64Mapper mapper(this);
- in_to_reg_storage_mapping_.Initialize(arg_locs, mir_graph_->GetNumOfInVRs(), &mapper);
- }
- return in_to_reg_storage_mapping_.Get(arg_num);
-}
-
-RegStorage X86Mir2Lir::GetCoreArgMappingToPhysicalReg(int core_arg_num) const {
- // For the 32-bit internal ABI, the first 3 arguments are passed in registers.
- // Not used for 64-bit, TODO: Move X86_32 to the same framework
- switch (core_arg_num) {
- case 0: return TargetReg32(kArg1);
- case 1: return TargetReg32(kArg2);
- case 2: return TargetReg32(kArg3);
- default: return RegStorage::InvalidReg();
- }
+ return result;
}
// ---------End of ABI support: mapping of args to physical registers -------------
-/*
- * If there are any ins passed in registers that have not been promoted
- * to a callee-save register, flush them to the frame. Perform initial
- * assignment of promoted arguments.
- *
- * ArgLocs is an array of location records describing the incoming arguments
- * with one location record per word of argument.
- */
-void X86Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) {
- if (!cu_->target64) return Mir2Lir::FlushIns(ArgLocs, rl_method);
- /*
- * Dummy up a RegLocation for the incoming Method*
- * It will attempt to keep kArg0 live (or copy it to home location
- * if promoted).
- */
-
- RegLocation rl_src = rl_method;
- rl_src.location = kLocPhysReg;
- rl_src.reg = TargetReg(kArg0, kRef);
- rl_src.home = false;
- MarkLive(rl_src);
- StoreValue(rl_method, rl_src);
- // If Method* has been promoted, explicitly flush
- if (rl_method.location == kLocPhysReg) {
- const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
- StoreRefDisp(rs_rSP, 0, As32BitReg(TargetReg(kArg0, kRef)), kNotVolatile);
- }
-
- if (mir_graph_->GetNumOfInVRs() == 0) {
- return;
- }
-
- int start_vreg = cu_->mir_graph->GetFirstInVR();
- /*
- * Copy incoming arguments to their proper home locations.
- * NOTE: an older version of dx had an issue in which
- * it would reuse static method argument registers.
- * This could result in the same Dalvik virtual register
- * being promoted to both core and fp regs. To account for this,
- * we only copy to the corresponding promoted physical register
- * if it matches the type of the SSA name for the incoming
- * argument. It is also possible that long and double arguments
- * end up half-promoted. In those cases, we must flush the promoted
- * half to memory as well.
- */
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- for (uint32_t i = 0; i < mir_graph_->GetNumOfInVRs(); i++) {
- // get reg corresponding to input
- RegStorage reg = GetArgMappingToPhysicalReg(i);
-
- RegLocation* t_loc = &ArgLocs[i];
- if (reg.Valid()) {
- // If arriving in register.
-
- // We have already updated the arg location with promoted info
- // so we can be based on it.
- if (t_loc->location == kLocPhysReg) {
- // Just copy it.
- OpRegCopy(t_loc->reg, reg);
- } else {
- // Needs flush.
- if (t_loc->ref) {
- StoreRefDisp(rs_rX86_SP_64, SRegOffset(start_vreg + i), reg, kNotVolatile);
- } else {
- StoreBaseDisp(rs_rX86_SP_64, SRegOffset(start_vreg + i), reg, t_loc->wide ? k64 : k32,
- kNotVolatile);
- }
- }
- } else {
- // If arriving in frame & promoted.
- if (t_loc->location == kLocPhysReg) {
- if (t_loc->ref) {
- LoadRefDisp(rs_rX86_SP_64, SRegOffset(start_vreg + i), t_loc->reg, kNotVolatile);
- } else {
- LoadBaseDisp(rs_rX86_SP_64, SRegOffset(start_vreg + i), t_loc->reg,
- t_loc->wide ? k64 : k32, kNotVolatile);
- }
- }
- }
- if (t_loc->wide) {
- // Increment i to skip the next one.
- i++;
- }
- }
-}
-
-/*
- * Load up to 5 arguments, the first three of which will be in
- * kArg1 .. kArg3. On entry kArg0 contains the current method pointer,
- * and as part of the load sequence, it must be replaced with
- * the target method pointer. Note, this may also be called
- * for "range" variants if the number of arguments is 5 or fewer.
- */
-int X86Mir2Lir::GenDalvikArgsNoRange(CallInfo* info,
- int call_state, LIR** pcrLabel, NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx, uintptr_t direct_code,
- uintptr_t direct_method, InvokeType type, bool skip_this) {
- if (!cu_->target64) {
- return Mir2Lir::GenDalvikArgsNoRange(info,
- call_state, pcrLabel, next_call_insn,
- target_method,
- vtable_idx, direct_code,
- direct_method, type, skip_this);
- }
- return GenDalvikArgsRange(info,
- call_state, pcrLabel, next_call_insn,
- target_method,
- vtable_idx, direct_code,
- direct_method, type, skip_this);
-}
-
-/*
- * May have 0+ arguments (also used for jumbo). Note that
- * source virtual registers may be in physical registers, so may
- * need to be flushed to home location before copying. This
- * applies to arg3 and above (see below).
- *
- * Two general strategies:
- * If < 20 arguments
- * Pass args 3-18 using vldm/vstm block copy
- * Pass arg0, arg1 & arg2 in kArg1-kArg3
- * If 20+ arguments
- * Pass args arg19+ using memcpy block copy
- * Pass arg0, arg1 & arg2 in kArg1-kArg3
- *
- */
-int X86Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state,
- LIR** pcrLabel, NextCallInsn next_call_insn,
- const MethodReference& target_method,
- uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method,
- InvokeType type, bool skip_this) {
- if (!cu_->target64) {
- return Mir2Lir::GenDalvikArgsRange(info, call_state,
- pcrLabel, next_call_insn,
- target_method,
- vtable_idx, direct_code, direct_method,
- type, skip_this);
- }
-
- /* If no arguments, just return */
- if (info->num_arg_words == 0)
- return call_state;
-
- const int start_index = skip_this ? 1 : 0;
-
- InToRegStorageX86_64Mapper mapper(this);
- InToRegStorageMapping in_to_reg_storage_mapping;
- in_to_reg_storage_mapping.Initialize(info->args, info->num_arg_words, &mapper);
- const int last_mapped_in = in_to_reg_storage_mapping.GetMaxMappedIn();
- const int size_of_the_last_mapped = last_mapped_in == -1 ? 1 :
- info->args[last_mapped_in].wide ? 2 : 1;
- int regs_left_to_pass_via_stack = info->num_arg_words - (last_mapped_in + size_of_the_last_mapped);
-
- // Fisrt of all, check whether it make sense to use bulk copying
- // Optimization is aplicable only for range case
- // TODO: make a constant instead of 2
- if (info->is_range && regs_left_to_pass_via_stack >= 2) {
- // Scan the rest of the args - if in phys_reg flush to memory
- for (int next_arg = last_mapped_in + size_of_the_last_mapped; next_arg < info->num_arg_words;) {
- RegLocation loc = info->args[next_arg];
- if (loc.wide) {
- loc = UpdateLocWide(loc);
- if (loc.location == kLocPhysReg) {
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- StoreBaseDisp(rs_rX86_SP_64, SRegOffset(loc.s_reg_low), loc.reg, k64, kNotVolatile);
- }
- next_arg += 2;
- } else {
- loc = UpdateLoc(loc);
- if (loc.location == kLocPhysReg) {
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- StoreBaseDisp(rs_rX86_SP_64, SRegOffset(loc.s_reg_low), loc.reg, k32, kNotVolatile);
- }
- next_arg++;
- }
- }
-
- // The rest can be copied together
- int start_offset = SRegOffset(info->args[last_mapped_in + size_of_the_last_mapped].s_reg_low);
- int outs_offset = StackVisitor::GetOutVROffset(last_mapped_in + size_of_the_last_mapped,
- cu_->instruction_set);
-
- int current_src_offset = start_offset;
- int current_dest_offset = outs_offset;
-
- // Only davik regs are accessed in this loop; no next_call_insn() calls.
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- while (regs_left_to_pass_via_stack > 0) {
- // This is based on the knowledge that the stack itself is 16-byte aligned.
- bool src_is_16b_aligned = (current_src_offset & 0xF) == 0;
- bool dest_is_16b_aligned = (current_dest_offset & 0xF) == 0;
- size_t bytes_to_move;
-
- /*
- * The amount to move defaults to 32-bit. If there are 4 registers left to move, then do a
- * a 128-bit move because we won't get the chance to try to aligned. If there are more than
- * 4 registers left to move, consider doing a 128-bit only if either src or dest are aligned.
- * We do this because we could potentially do a smaller move to align.
- */
- if (regs_left_to_pass_via_stack == 4 ||
- (regs_left_to_pass_via_stack > 4 && (src_is_16b_aligned || dest_is_16b_aligned))) {
- // Moving 128-bits via xmm register.
- bytes_to_move = sizeof(uint32_t) * 4;
-
- // Allocate a free xmm temp. Since we are working through the calling sequence,
- // we expect to have an xmm temporary available. AllocTempDouble will abort if
- // there are no free registers.
- RegStorage temp = AllocTempDouble();
-
- LIR* ld1 = nullptr;
- LIR* ld2 = nullptr;
- LIR* st1 = nullptr;
- LIR* st2 = nullptr;
-
- /*
- * The logic is similar for both loads and stores. If we have 16-byte alignment,
- * do an aligned move. If we have 8-byte alignment, then do the move in two
- * parts. This approach prevents possible cache line splits. Finally, fall back
- * to doing an unaligned move. In most cases we likely won't split the cache
- * line but we cannot prove it and thus take a conservative approach.
- */
- bool src_is_8b_aligned = (current_src_offset & 0x7) == 0;
- bool dest_is_8b_aligned = (current_dest_offset & 0x7) == 0;
-
- ScopedMemRefType mem_ref_type2(this, ResourceMask::kDalvikReg);
- if (src_is_16b_aligned) {
- ld1 = OpMovRegMem(temp, rs_rX86_SP_64, current_src_offset, kMovA128FP);
- } else if (src_is_8b_aligned) {
- ld1 = OpMovRegMem(temp, rs_rX86_SP_64, current_src_offset, kMovLo128FP);
- ld2 = OpMovRegMem(temp, rs_rX86_SP_64, current_src_offset + (bytes_to_move >> 1),
- kMovHi128FP);
- } else {
- ld1 = OpMovRegMem(temp, rs_rX86_SP_64, current_src_offset, kMovU128FP);
- }
-
- if (dest_is_16b_aligned) {
- st1 = OpMovMemReg(rs_rX86_SP_64, current_dest_offset, temp, kMovA128FP);
- } else if (dest_is_8b_aligned) {
- st1 = OpMovMemReg(rs_rX86_SP_64, current_dest_offset, temp, kMovLo128FP);
- st2 = OpMovMemReg(rs_rX86_SP_64, current_dest_offset + (bytes_to_move >> 1),
- temp, kMovHi128FP);
- } else {
- st1 = OpMovMemReg(rs_rX86_SP_64, current_dest_offset, temp, kMovU128FP);
- }
-
- // TODO If we could keep track of aliasing information for memory accesses that are wider
- // than 64-bit, we wouldn't need to set up a barrier.
- if (ld1 != nullptr) {
- if (ld2 != nullptr) {
- // For 64-bit load we can actually set up the aliasing information.
- AnnotateDalvikRegAccess(ld1, current_src_offset >> 2, true, true);
- AnnotateDalvikRegAccess(ld2, (current_src_offset + (bytes_to_move >> 1)) >> 2, true, true);
- } else {
- // Set barrier for 128-bit load.
- ld1->u.m.def_mask = &kEncodeAll;
- }
- }
- if (st1 != nullptr) {
- if (st2 != nullptr) {
- // For 64-bit store we can actually set up the aliasing information.
- AnnotateDalvikRegAccess(st1, current_dest_offset >> 2, false, true);
- AnnotateDalvikRegAccess(st2, (current_dest_offset + (bytes_to_move >> 1)) >> 2, false, true);
- } else {
- // Set barrier for 128-bit store.
- st1->u.m.def_mask = &kEncodeAll;
- }
- }
-
- // Free the temporary used for the data movement.
- FreeTemp(temp);
- } else {
- // Moving 32-bits via general purpose register.
- bytes_to_move = sizeof(uint32_t);
-
- // Instead of allocating a new temp, simply reuse one of the registers being used
- // for argument passing.
- RegStorage temp = TargetReg(kArg3, kNotWide);
-
- // Now load the argument VR and store to the outs.
- Load32Disp(rs_rX86_SP_64, current_src_offset, temp);
- Store32Disp(rs_rX86_SP_64, current_dest_offset, temp);
- }
-
- current_src_offset += bytes_to_move;
- current_dest_offset += bytes_to_move;
- regs_left_to_pass_via_stack -= (bytes_to_move >> 2);
- }
- DCHECK_EQ(regs_left_to_pass_via_stack, 0);
- }
-
- // Now handle rest not registers if they are
- if (in_to_reg_storage_mapping.IsThereStackMapped()) {
- RegStorage regSingle = TargetReg(kArg2, kNotWide);
- RegStorage regWide = TargetReg(kArg3, kWide);
- for (int i = start_index;
- i < last_mapped_in + size_of_the_last_mapped + regs_left_to_pass_via_stack; i++) {
- RegLocation rl_arg = info->args[i];
- rl_arg = UpdateRawLoc(rl_arg);
- RegStorage reg = in_to_reg_storage_mapping.Get(i);
- if (!reg.Valid()) {
- int out_offset = StackVisitor::GetOutVROffset(i, cu_->instruction_set);
-
- {
- ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- if (rl_arg.wide) {
- if (rl_arg.location == kLocPhysReg) {
- StoreBaseDisp(rs_rX86_SP_64, out_offset, rl_arg.reg, k64, kNotVolatile);
- } else {
- LoadValueDirectWideFixed(rl_arg, regWide);
- StoreBaseDisp(rs_rX86_SP_64, out_offset, regWide, k64, kNotVolatile);
- }
- } else {
- if (rl_arg.location == kLocPhysReg) {
- StoreBaseDisp(rs_rX86_SP_64, out_offset, rl_arg.reg, k32, kNotVolatile);
- } else {
- LoadValueDirectFixed(rl_arg, regSingle);
- StoreBaseDisp(rs_rX86_SP_64, out_offset, regSingle, k32, kNotVolatile);
- }
- }
- }
- call_state = next_call_insn(cu_, info, call_state, target_method,
- vtable_idx, direct_code, direct_method, type);
- }
- if (rl_arg.wide) {
- i++;
- }
- }
- }
-
- // Finish with mapped registers
- for (int i = start_index; i <= last_mapped_in; i++) {
- RegLocation rl_arg = info->args[i];
- rl_arg = UpdateRawLoc(rl_arg);
- RegStorage reg = in_to_reg_storage_mapping.Get(i);
- if (reg.Valid()) {
- if (rl_arg.wide) {
- LoadValueDirectWideFixed(rl_arg, reg);
- } else {
- LoadValueDirectFixed(rl_arg, reg);
- }
- call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
- direct_code, direct_method, type);
- }
- if (rl_arg.wide) {
- i++;
- }
- }
-
- call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
- direct_code, direct_method, type);
- if (pcrLabel) {
- if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) {
- *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags);
- } else {
- *pcrLabel = nullptr;
- // In lieu of generating a check for kArg1 being null, we need to
- // perform a load when doing implicit checks.
- RegStorage tmp = AllocTemp();
- Load32Disp(TargetReg(kArg1, kRef), 0, tmp);
- MarkPossibleNullPointerException(info->opt_flags);
- FreeTemp(tmp);
- }
- }
- return call_state;
-}
-
bool X86Mir2Lir::GenInlinedCharAt(CallInfo* info) {
// Location of reference to data array
int value_offset = mirror::String::ValueOffset().Int32Value();
@@ -2871,7 +2519,7 @@
if (rl_idx.is_const) {
LIR* comparison;
range_check_branch = OpCmpMemImmBranch(
- kCondUlt, RegStorage::InvalidReg(), rl_obj.reg, count_offset,
+ kCondLs, RegStorage::InvalidReg(), rl_obj.reg, count_offset,
mir_graph_->ConstantValue(rl_idx.orig_sreg), nullptr, &comparison);
MarkPossibleNullPointerExceptionAfter(0, comparison);
} else {
@@ -2969,4 +2617,122 @@
}
}
+int X86Mir2Lir::GenDalvikArgsBulkCopy(CallInfo* info, int first, int count) {
+ if (count < 4) {
+ // It does not make sense to use this utility if we have no chance to use
+ // 128-bit move.
+ return count;
+ }
+ GenDalvikArgsFlushPromoted(info, first);
+
+ // The rest can be copied together
+ int current_src_offset = SRegOffset(info->args[first].s_reg_low);
+ int current_dest_offset = StackVisitor::GetOutVROffset(first, cu_->instruction_set);
+
+ // Only davik regs are accessed in this loop; no next_call_insn() calls.
+ ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+ while (count > 0) {
+ // This is based on the knowledge that the stack itself is 16-byte aligned.
+ bool src_is_16b_aligned = (current_src_offset & 0xF) == 0;
+ bool dest_is_16b_aligned = (current_dest_offset & 0xF) == 0;
+ size_t bytes_to_move;
+
+ /*
+ * The amount to move defaults to 32-bit. If there are 4 registers left to move, then do a
+ * a 128-bit move because we won't get the chance to try to aligned. If there are more than
+ * 4 registers left to move, consider doing a 128-bit only if either src or dest are aligned.
+ * We do this because we could potentially do a smaller move to align.
+ */
+ if (count == 4 || (count > 4 && (src_is_16b_aligned || dest_is_16b_aligned))) {
+ // Moving 128-bits via xmm register.
+ bytes_to_move = sizeof(uint32_t) * 4;
+
+ // Allocate a free xmm temp. Since we are working through the calling sequence,
+ // we expect to have an xmm temporary available. AllocTempDouble will abort if
+ // there are no free registers.
+ RegStorage temp = AllocTempDouble();
+
+ LIR* ld1 = nullptr;
+ LIR* ld2 = nullptr;
+ LIR* st1 = nullptr;
+ LIR* st2 = nullptr;
+
+ /*
+ * The logic is similar for both loads and stores. If we have 16-byte alignment,
+ * do an aligned move. If we have 8-byte alignment, then do the move in two
+ * parts. This approach prevents possible cache line splits. Finally, fall back
+ * to doing an unaligned move. In most cases we likely won't split the cache
+ * line but we cannot prove it and thus take a conservative approach.
+ */
+ bool src_is_8b_aligned = (current_src_offset & 0x7) == 0;
+ bool dest_is_8b_aligned = (current_dest_offset & 0x7) == 0;
+
+ if (src_is_16b_aligned) {
+ ld1 = OpMovRegMem(temp, TargetPtrReg(kSp), current_src_offset, kMovA128FP);
+ } else if (src_is_8b_aligned) {
+ ld1 = OpMovRegMem(temp, TargetPtrReg(kSp), current_src_offset, kMovLo128FP);
+ ld2 = OpMovRegMem(temp, TargetPtrReg(kSp), current_src_offset + (bytes_to_move >> 1),
+ kMovHi128FP);
+ } else {
+ ld1 = OpMovRegMem(temp, TargetPtrReg(kSp), current_src_offset, kMovU128FP);
+ }
+
+ if (dest_is_16b_aligned) {
+ st1 = OpMovMemReg(TargetPtrReg(kSp), current_dest_offset, temp, kMovA128FP);
+ } else if (dest_is_8b_aligned) {
+ st1 = OpMovMemReg(TargetPtrReg(kSp), current_dest_offset, temp, kMovLo128FP);
+ st2 = OpMovMemReg(TargetPtrReg(kSp), current_dest_offset + (bytes_to_move >> 1),
+ temp, kMovHi128FP);
+ } else {
+ st1 = OpMovMemReg(TargetPtrReg(kSp), current_dest_offset, temp, kMovU128FP);
+ }
+
+ // TODO If we could keep track of aliasing information for memory accesses that are wider
+ // than 64-bit, we wouldn't need to set up a barrier.
+ if (ld1 != nullptr) {
+ if (ld2 != nullptr) {
+ // For 64-bit load we can actually set up the aliasing information.
+ AnnotateDalvikRegAccess(ld1, current_src_offset >> 2, true, true);
+ AnnotateDalvikRegAccess(ld2, (current_src_offset + (bytes_to_move >> 1)) >> 2, true,
+ true);
+ } else {
+ // Set barrier for 128-bit load.
+ ld1->u.m.def_mask = &kEncodeAll;
+ }
+ }
+ if (st1 != nullptr) {
+ if (st2 != nullptr) {
+ // For 64-bit store we can actually set up the aliasing information.
+ AnnotateDalvikRegAccess(st1, current_dest_offset >> 2, false, true);
+ AnnotateDalvikRegAccess(st2, (current_dest_offset + (bytes_to_move >> 1)) >> 2, false,
+ true);
+ } else {
+ // Set barrier for 128-bit store.
+ st1->u.m.def_mask = &kEncodeAll;
+ }
+ }
+
+ // Free the temporary used for the data movement.
+ FreeTemp(temp);
+ } else {
+ // Moving 32-bits via general purpose register.
+ bytes_to_move = sizeof(uint32_t);
+
+ // Instead of allocating a new temp, simply reuse one of the registers being used
+ // for argument passing.
+ RegStorage temp = TargetReg(kArg3, kNotWide);
+
+ // Now load the argument VR and store to the outs.
+ Load32Disp(TargetPtrReg(kSp), current_src_offset, temp);
+ Store32Disp(TargetPtrReg(kSp), current_dest_offset, temp);
+ }
+
+ current_src_offset += bytes_to_move;
+ current_dest_offset += bytes_to_move;
+ count -= (bytes_to_move >> 2);
+ }
+ DCHECK_EQ(count, 0);
+ return count;
+}
+
} // namespace art
diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc
index ad3222c..893b98a 100644
--- a/compiler/dex/quick/x86/utility_x86.cc
+++ b/compiler/dex/quick/x86/utility_x86.cc
@@ -15,12 +15,15 @@
*/
#include "codegen_x86.h"
+
+#include "base/logging.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "dex/dataflow_iterator-inl.h"
-#include "x86_lir.h"
#include "dex/quick/dex_file_method_inliner.h"
#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "dex/reg_storage_eq.h"
+#include "driver/compiler_driver.h"
+#include "x86_lir.h"
namespace art {
@@ -509,7 +512,7 @@
}
}
if (r_dest != r_src) {
- if (false && op == kOpLsl && value >= 0 && value <= 3) { // lea shift special case
+ if ((false) && op == kOpLsl && value >= 0 && value <= 3) { // lea shift special case
// TODO: fix bug in LEA encoding when disp == 0
return NewLIR5(kX86Lea32RA, r_dest.GetReg(), r5sib_no_base /* base */,
r_src.GetReg() /* index */, value /* scale */, 0 /* disp */);
@@ -570,32 +573,36 @@
if (is_fp) {
DCHECK(r_dest.IsDouble());
if (value == 0) {
- return NewLIR2(kX86XorpsRR, low_reg_val, low_reg_val);
- } else if (base_of_code_ != nullptr) {
+ return NewLIR2(kX86XorpdRR, low_reg_val, low_reg_val);
+ } else if (base_of_code_ != nullptr || cu_->target64) {
// We will load the value from the literal area.
LIR* data_target = ScanLiteralPoolWide(literal_list_, val_lo, val_hi);
if (data_target == NULL) {
data_target = AddWideData(&literal_list_, val_lo, val_hi);
}
- // Address the start of the method
- RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
- if (rl_method.wide) {
- rl_method = LoadValueWide(rl_method, kCoreReg);
- } else {
- rl_method = LoadValue(rl_method, kCoreReg);
- }
-
// Load the proper value from the literal area.
- // We don't know the proper offset for the value, so pick one that will force
- // 4 byte offset. We will fix this up in the assembler later to have the right
- // value.
+ // We don't know the proper offset for the value, so pick one that
+ // will force 4 byte offset. We will fix this up in the assembler
+ // later to have the right value.
ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
- res = LoadBaseDisp(rl_method.reg, 256 /* bogus */, RegStorage::FloatSolo64(low_reg_val),
- kDouble, kNotVolatile);
+ if (cu_->target64) {
+ res = NewLIR3(kX86MovsdRM, low_reg_val, kRIPReg, 256 /* bogus */);
+ } else {
+ // Address the start of the method.
+ RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
+ if (rl_method.wide) {
+ rl_method = LoadValueWide(rl_method, kCoreReg);
+ } else {
+ rl_method = LoadValue(rl_method, kCoreReg);
+ }
+
+ res = LoadBaseDisp(rl_method.reg, 256 /* bogus */, RegStorage::FloatSolo64(low_reg_val),
+ kDouble, kNotVolatile);
+ store_method_addr_used_ = true;
+ }
res->target = data_target;
res->flags.fixup = kFixupLoad;
- store_method_addr_used_ = true;
} else {
if (r_dest.IsPair()) {
if (val_lo == 0) {
@@ -960,12 +967,14 @@
curr_bb = iter.Next();
}
- // Did we need a pointer to the method code?
+ // Did we need a pointer to the method code? Not in 64 bit mode.
+ base_of_code_ = nullptr;
+
+ // store_method_addr_ must be false for x86_64, since RIP addressing is used.
+ CHECK(!(cu_->target64 && store_method_addr_));
if (store_method_addr_) {
- base_of_code_ = mir_graph_->GetNewCompilerTemp(kCompilerTempBackend, cu_->target64 == true);
+ base_of_code_ = mir_graph_->GetNewCompilerTemp(kCompilerTempBackend, false);
DCHECK(base_of_code_ != nullptr);
- } else {
- base_of_code_ = nullptr;
}
}
@@ -994,19 +1003,22 @@
AnalyzeFPInstruction(opcode, bb, mir);
break;
case kMirOpConstVector:
- store_method_addr_ = true;
+ if (!cu_->target64) {
+ store_method_addr_ = true;
+ }
break;
case kMirOpPackedMultiply:
case kMirOpPackedShiftLeft:
case kMirOpPackedSignedShiftRight:
- case kMirOpPackedUnsignedShiftRight: {
- // Byte emulation requires constants from the literal pool.
- OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
- if (opsize == kSignedByte || opsize == kUnsignedByte) {
- store_method_addr_ = true;
+ case kMirOpPackedUnsignedShiftRight:
+ if (!cu_->target64) {
+ // Byte emulation requires constants from the literal pool.
+ OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
+ if (opsize == kSignedByte || opsize == kUnsignedByte) {
+ store_method_addr_ = true;
+ }
}
break;
- }
default:
// Ignore the rest.
break;
@@ -1016,6 +1028,7 @@
void X86Mir2Lir::AnalyzeMIR(int opcode, BasicBlock* bb, MIR* mir) {
// Looking for
// - Do we need a pointer to the code (used for packed switches and double lits)?
+ // 64 bit uses RIP addressing instead.
switch (opcode) {
// Instructions referencing doubles.
@@ -1038,7 +1051,9 @@
// Packed switches and array fills need a pointer to the base of the method.
case Instruction::FILL_ARRAY_DATA:
case Instruction::PACKED_SWITCH:
- store_method_addr_ = true;
+ if (!cu_->target64) {
+ store_method_addr_ = true;
+ }
break;
case Instruction::INVOKE_STATIC:
case Instruction::INVOKE_STATIC_RANGE:
@@ -1115,7 +1130,8 @@
void X86Mir2Lir::AnalyzeInvokeStatic(int opcode, BasicBlock* bb, MIR* mir) {
UNUSED(opcode, bb);
- // For now this is only actual for x86-32.
+
+ // 64 bit RIP addressing doesn't need store_method_addr_ set.
if (cu_->target64) {
return;
}
diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h
index 76a67c4..7dea09a 100644
--- a/compiler/dex/quick/x86/x86_lir.h
+++ b/compiler/dex/quick/x86/x86_lir.h
@@ -17,7 +17,8 @@
#ifndef ART_COMPILER_DEX_QUICK_X86_X86_LIR_H_
#define ART_COMPILER_DEX_QUICK_X86_X86_LIR_H_
-#include "dex/compiler_internals.h"
+#include "dex/reg_location.h"
+#include "dex/reg_storage.h"
namespace art {
@@ -56,15 +57,15 @@
* x86-64/x32 gs: holds it.
*
* For floating point we don't support CPUs without SSE2 support (ie newer than PIII):
- * Native: x86 | x86-64 / x32 | ART x86 | ART x86-64
- * XMM0: caller | caller, arg1 | caller, float return value | caller, arg1, float return value
- * XMM1: caller | caller, arg2 | caller, scratch | caller, arg2, scratch
- * XMM2: caller | caller, arg3 | caller, scratch | caller, arg3, scratch
- * XMM3: caller | caller, arg4 | caller, scratch | caller, arg4, scratch
- * XMM4: caller | caller, arg5 | caller, scratch | caller, arg5, scratch
- * XMM5: caller | caller, arg6 | caller, scratch | caller, arg6, scratch
- * XMM6: caller | caller, arg7 | caller, scratch | caller, arg7, scratch
- * XMM7: caller | caller, arg8 | caller, scratch | caller, arg8, scratch
+ * Native: x86 | x86-64 / x32 | ART x86 | ART x86-64
+ * XMM0: caller | caller, arg1 | caller, arg1, float return value | caller, arg1, float return value
+ * XMM1: caller | caller, arg2 | caller, arg2, scratch | caller, arg2, scratch
+ * XMM2: caller | caller, arg3 | caller, arg3, scratch | caller, arg3, scratch
+ * XMM3: caller | caller, arg4 | caller, arg4, scratch | caller, arg4, scratch
+ * XMM4: caller | caller, arg5 | caller, scratch | caller, arg5, scratch
+ * XMM5: caller | caller, arg6 | caller, scratch | caller, arg6, scratch
+ * XMM6: caller | caller, arg7 | caller, scratch | caller, arg7, scratch
+ * XMM7: caller | caller, arg8 | caller, scratch | caller, arg8, scratch
* --- x86-64/x32 registers
* XMM8 .. 11: caller save available as scratch registers for ART.
* XMM12 .. 15: callee save available as promoted registers for ART.
@@ -217,6 +218,9 @@
xr14 = RegStorage::k128BitSolo | 14,
xr15 = RegStorage::k128BitSolo | 15,
+ // Special value for RIP 64 bit addressing.
+ kRIPReg = 255,
+
// TODO: as needed, add 256, 512 and 1024-bit xmm views.
};
diff --git a/compiler/dex/reg_location.h b/compiler/dex/reg_location.h
index 38f59da..aa8ed46 100644
--- a/compiler/dex/reg_location.h
+++ b/compiler/dex/reg_location.h
@@ -21,6 +21,7 @@
namespace art {
+static constexpr int16_t INVALID_SREG = -1;
/*
* Whereas a SSA name describes a definition of a Dalvik vreg, the RegLocation describes
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index ed33882..f15f9be 100644
--- a/compiler/dex/ssa_transformation.cc
+++ b/compiler/dex/ssa_transformation.cc
@@ -15,7 +15,8 @@
*/
#include "base/bit_vector-inl.h"
-#include "compiler_internals.h"
+#include "base/logging.h"
+#include "compiler_ir.h"
#include "dataflow_iterator-inl.h"
#include "utils/scoped_arena_containers.h"
@@ -103,12 +104,12 @@
num_reachable_blocks_ = dfs_order_.size();
- if (num_reachable_blocks_ != num_blocks_) {
- // Hide all unreachable blocks.
+ if (num_reachable_blocks_ != GetNumBlocks()) {
+ // Kill all unreachable blocks.
AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
if (!bb->visited) {
- bb->Hide(this);
+ bb->Kill(this);
}
}
}
@@ -173,9 +174,9 @@
dom_post_order_traversal_.reserve(num_reachable_blocks_);
ClearAllVisitedFlags();
- DCHECK(temp_scoped_alloc_.get() != nullptr);
+ ScopedArenaAllocator allocator(&cu_->arena_stack);
ScopedArenaVector<std::pair<BasicBlock*, ArenaBitVector::IndexIterator>> work_stack(
- temp_scoped_alloc_->Adapter());
+ allocator.Adapter());
bb->visited = true;
work_stack.push_back(std::make_pair(bb, bb->i_dominated->Indexes().begin()));
while (!work_stack.empty()) {
@@ -197,12 +198,6 @@
dom_post_order_traversal_.push_back(curr_bb->id);
}
work_stack.pop_back();
-
- /* hacky loop detection */
- if ((curr_bb->taken != NullBasicBlockId) && curr_bb->dominators->IsBitSet(curr_bb->taken)) {
- curr_bb->nesting_depth++;
- attributes_ |= METHOD_HAS_LOOP;
- }
}
}
}
@@ -408,6 +403,8 @@
for (BasicBlock* bb = iter5.Next(); bb != NULL; bb = iter5.Next()) {
ComputeDominanceFrontier(bb);
}
+
+ domination_up_to_date_ = true;
}
/*
@@ -466,24 +463,28 @@
return false;
}
-/* Insert phi nodes to for each variable to the dominance frontiers */
-void MIRGraph::InsertPhiNodes() {
- int dalvik_reg;
- ArenaBitVector* phi_blocks = new (temp_scoped_alloc_.get()) ArenaBitVector(
- temp_scoped_alloc_.get(), GetNumBlocks(), false, kBitMapPhi);
- ArenaBitVector* input_blocks = new (temp_scoped_alloc_.get()) ArenaBitVector(
- temp_scoped_alloc_.get(), GetNumBlocks(), false, kBitMapInputBlocks);
-
+/* For each dalvik reg, find blocks that need phi nodes according to the dominance frontiers. */
+void MIRGraph::FindPhiNodeBlocks() {
RepeatingPostOrderDfsIterator iter(this);
bool change = false;
for (BasicBlock* bb = iter.Next(false); bb != NULL; bb = iter.Next(change)) {
change = ComputeBlockLiveIns(bb);
}
+ ArenaBitVector* phi_blocks = new (temp_scoped_alloc_.get()) ArenaBitVector(
+ temp_scoped_alloc_.get(), GetNumBlocks(), false, kBitMapBMatrix);
+
+ // Reuse the def_block_matrix storage for phi_node_blocks.
+ ArenaBitVector** def_block_matrix = temp_.ssa.def_block_matrix;
+ ArenaBitVector** phi_node_blocks = def_block_matrix;
+ DCHECK(temp_.ssa.phi_node_blocks == nullptr);
+ temp_.ssa.phi_node_blocks = phi_node_blocks;
+ temp_.ssa.def_block_matrix = nullptr;
+
/* Iterate through each Dalvik register */
- for (dalvik_reg = GetNumOfCodeAndTempVRs() - 1; dalvik_reg >= 0; dalvik_reg--) {
- input_blocks->Copy(temp_.ssa.def_block_matrix[dalvik_reg]);
+ for (int dalvik_reg = GetNumOfCodeAndTempVRs() - 1; dalvik_reg >= 0; dalvik_reg--) {
phi_blocks->ClearAllBits();
+ ArenaBitVector* input_blocks = def_block_matrix[dalvik_reg];
do {
// TUNING: When we repeat this, we could skip indexes from the previous pass.
for (uint32_t idx : input_blocks->Indexes()) {
@@ -494,23 +495,8 @@
}
} while (input_blocks->Union(phi_blocks));
- /*
- * Insert a phi node for dalvik_reg in the phi_blocks if the Dalvik
- * register is in the live-in set.
- */
- for (uint32_t idx : phi_blocks->Indexes()) {
- BasicBlock* phi_bb = GetBasicBlock(idx);
- /* Variable will be clobbered before being used - no need for phi */
- if (!phi_bb->data_flow_info->live_in_v->IsBitSet(dalvik_reg)) {
- continue;
- }
- MIR *phi = NewMIR();
- phi->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpPhi);
- phi->dalvikInsn.vA = dalvik_reg;
- phi->offset = phi_bb->start_offset;
- phi->m_unit_index = 0; // Arbitrarily assign all Phi nodes to outermost method.
- phi_bb->PrependMIR(phi);
- }
+ def_block_matrix[dalvik_reg] = phi_blocks;
+ phi_blocks = input_blocks; // Reuse the bit vector in next iteration.
}
}
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 4929b5b..4ff173d 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -16,15 +16,14 @@
#include "verification_results.h"
+#include "base/logging.h"
#include "base/stl_util.h"
-#include "base/mutex.h"
#include "base/mutex-inl.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "thread.h"
#include "thread-inl.h"
#include "verified_method.h"
-#include "verifier/method_verifier.h"
#include "verifier/method_verifier-inl.h"
namespace art {
@@ -57,8 +56,8 @@
const VerifiedMethod* verified_method = VerifiedMethod::Create(method_verifier, compile);
if (verified_method == nullptr) {
- DCHECK(method_verifier->HasFailures());
- return false;
+ // Do not report an error to the verifier. We'll just punt this later.
+ return true;
}
WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
@@ -84,6 +83,15 @@
return (it != verified_methods_.end()) ? it->second : nullptr;
}
+void VerificationResults::RemoveVerifiedMethod(MethodReference ref) {
+ WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
+ auto it = verified_methods_.find(ref);
+ if (it != verified_methods_.end()) {
+ delete it->second;
+ verified_methods_.erase(it);
+ }
+}
+
void VerificationResults::AddRejectedClass(ClassReference ref) {
{
WriterMutexLock mu(Thread::Current(), rejected_classes_lock_);
@@ -97,18 +105,8 @@
return (rejected_classes_.find(ref) != rejected_classes_.end());
}
-bool VerificationResults::IsCandidateForCompilation(MethodReference& method_ref,
+bool VerificationResults::IsCandidateForCompilation(MethodReference&,
const uint32_t access_flags) {
-#ifdef ART_SEA_IR_MODE
- bool use_sea = compiler_options_->GetSeaIrMode();
- use_sea = use_sea && (std::string::npos != PrettyMethod(
- method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci"));
- if (use_sea) {
- return true;
- }
-#else
- UNUSED(method_ref);
-#endif
if (!compiler_options_->IsCompilationEnabled()) {
return false;
}
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index 0e7923f..7fc2a23 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -48,6 +48,7 @@
const VerifiedMethod* GetVerifiedMethod(MethodReference ref)
LOCKS_EXCLUDED(verified_methods_lock_);
+ void RemoveVerifiedMethod(MethodReference ref) LOCKS_EXCLUDED(verified_methods_lock_);
void AddRejectedClass(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
bool IsClassRejected(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index 17328c4..21e965d 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -23,20 +23,13 @@
#include "base/logging.h"
#include "base/stl_util.h"
#include "dex_file.h"
-#include "dex_instruction.h"
#include "dex_instruction-inl.h"
-#include "base/mutex.h"
-#include "base/mutex-inl.h"
-#include "mirror/art_method.h"
#include "mirror/art_method-inl.h"
-#include "mirror/class.h"
#include "mirror/class-inl.h"
-#include "mirror/dex_cache.h"
#include "mirror/dex_cache-inl.h"
-#include "mirror/object.h"
#include "mirror/object-inl.h"
+#include "utils.h"
#include "verifier/dex_gc_map.h"
-#include "verifier/method_verifier.h"
#include "verifier/method_verifier-inl.h"
#include "verifier/reg_type-inl.h"
#include "verifier/register_line-inl.h"
@@ -49,7 +42,6 @@
if (compile) {
/* Generate a register map. */
if (!verified_method->GenerateGcMap(method_verifier)) {
- CHECK(method_verifier->HasFailures());
return nullptr; // Not a real failure, but a failure to encode.
}
if (kIsDebugBuild) {
@@ -82,33 +74,33 @@
size_t num_entries, ref_bitmap_bits, pc_bits;
ComputeGcMapSizes(method_verifier, &num_entries, &ref_bitmap_bits, &pc_bits);
// There's a single byte to encode the size of each bitmap.
- if (ref_bitmap_bits >= (8 /* bits per byte */ * 8192 /* 13-bit size */ )) {
- // TODO: either a better GC map format or per method failures
- method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD)
- << "Cannot encode GC map for method with " << ref_bitmap_bits << " registers";
+ if (ref_bitmap_bits >= kBitsPerByte * 8192 /* 13-bit size */) {
+ LOG(WARNING) << "Cannot encode GC map for method with " << ref_bitmap_bits << " registers: "
+ << PrettyMethod(method_verifier->GetMethodReference().dex_method_index,
+ *method_verifier->GetMethodReference().dex_file);
return false;
}
- size_t ref_bitmap_bytes = (ref_bitmap_bits + 7) / 8;
+ size_t ref_bitmap_bytes = RoundUp(ref_bitmap_bits, kBitsPerByte) / kBitsPerByte;
// There are 2 bytes to encode the number of entries.
if (num_entries >= 65536) {
- // TODO: Either a better GC map format or per method failures.
- method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD)
- << "Cannot encode GC map for method with " << num_entries << " entries";
+ LOG(WARNING) << "Cannot encode GC map for method with " << num_entries << " entries: "
+ << PrettyMethod(method_verifier->GetMethodReference().dex_method_index,
+ *method_verifier->GetMethodReference().dex_file);
return false;
}
size_t pc_bytes;
verifier::RegisterMapFormat format;
- if (pc_bits <= 8) {
+ if (pc_bits <= kBitsPerByte) {
format = verifier::kRegMapFormatCompact8;
pc_bytes = 1;
- } else if (pc_bits <= 16) {
+ } else if (pc_bits <= kBitsPerByte * 2) {
format = verifier::kRegMapFormatCompact16;
pc_bytes = 2;
} else {
- // TODO: Either a better GC map format or per method failures.
- method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD)
- << "Cannot encode GC map for method with "
- << (1 << pc_bits) << " instructions (number is rounded up to nearest power of 2)";
+ LOG(WARNING) << "Cannot encode GC map for method with "
+ << (1 << pc_bits) << " instructions (number is rounded up to nearest power of 2): "
+ << PrettyMethod(method_verifier->GetMethodReference().dex_method_index,
+ *method_verifier->GetMethodReference().dex_file);
return false;
}
size_t table_size = ((pc_bytes + ref_bitmap_bytes) * num_entries) + 4;
@@ -152,16 +144,16 @@
verifier::RegisterLine* line = method_verifier->GetRegLine(i);
for (size_t j = 0; j < code_item->registers_size_; j++) {
if (line->GetRegisterType(method_verifier, j).IsNonZeroReferenceTypes()) {
- DCHECK_LT(j / 8, map.RegWidth());
- DCHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 1);
- } else if ((j / 8) < map.RegWidth()) {
- DCHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 0);
+ DCHECK_LT(j / kBitsPerByte, map.RegWidth());
+ DCHECK_EQ((reg_bitmap[j / kBitsPerByte] >> (j % kBitsPerByte)) & 1, 1);
+ } else if ((j / kBitsPerByte) < map.RegWidth()) {
+ DCHECK_EQ((reg_bitmap[j / kBitsPerByte] >> (j % kBitsPerByte)) & 1, 0);
} else {
// If a register doesn't contain a reference then the bitmap may be shorter than the line.
}
}
} else {
- DCHECK(reg_bitmap == NULL);
+ DCHECK(i >= 65536 || reg_bitmap == NULL);
}
}
}
@@ -190,6 +182,31 @@
*log2_max_gc_pc = i;
}
+void VerifiedMethod::GenerateDeQuickenMap(verifier::MethodVerifier* method_verifier) {
+ if (method_verifier->HasFailures()) {
+ return;
+ }
+ const DexFile::CodeItem* code_item = method_verifier->CodeItem();
+ const uint16_t* insns = code_item->insns_;
+ const Instruction* inst = Instruction::At(insns);
+ const Instruction* end = Instruction::At(insns + code_item->insns_size_in_code_units_);
+ for (; inst < end; inst = inst->Next()) {
+ const bool is_virtual_quick = inst->Opcode() == Instruction::INVOKE_VIRTUAL_QUICK;
+ const bool is_range_quick = inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK;
+ if (is_virtual_quick || is_range_quick) {
+ uint32_t dex_pc = inst->GetDexPc(insns);
+ verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
+ mirror::ArtMethod* method = method_verifier->GetQuickInvokedMethod(inst, line,
+ is_range_quick);
+ CHECK(method != nullptr);
+ // The verifier must know what the type of the object was or else we would have gotten a
+ // failure. Put the dex method index in the dequicken map since we need this to get number of
+ // arguments in the compiler.
+ dequicken_map_.Put(dex_pc, method->ToMethodReference());
+ }
+ }
+}
+
void VerifiedMethod::GenerateDevirtMap(verifier::MethodVerifier* method_verifier) {
// It is risky to rely on reg_types for sharpening in cases of soft
// verification, we might end up sharpening to a wrong implementation. Just abort.
@@ -203,10 +220,10 @@
const Instruction* end = Instruction::At(insns + code_item->insns_size_in_code_units_);
for (; inst < end; inst = inst->Next()) {
- bool is_virtual = (inst->Opcode() == Instruction::INVOKE_VIRTUAL) ||
- (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE);
- bool is_interface = (inst->Opcode() == Instruction::INVOKE_INTERFACE) ||
- (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE);
+ const bool is_virtual = inst->Opcode() == Instruction::INVOKE_VIRTUAL ||
+ inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE;
+ const bool is_interface = inst->Opcode() == Instruction::INVOKE_INTERFACE ||
+ inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE;
if (!is_interface && !is_virtual) {
continue;
@@ -214,8 +231,8 @@
// Get reg type for register holding the reference to the object that will be dispatched upon.
uint32_t dex_pc = inst->GetDexPc(insns);
verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
- bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) ||
- (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE);
+ const bool is_range = inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE ||
+ inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE;
const verifier::RegType&
reg_type(line->GetRegisterType(method_verifier,
is_range ? inst->VRegC_3rc() : inst->VRegC_35c()));
@@ -241,14 +258,14 @@
continue;
}
// Find the concrete method.
- mirror::ArtMethod* concrete_method = NULL;
+ mirror::ArtMethod* concrete_method = nullptr;
if (is_interface) {
concrete_method = reg_type.GetClass()->FindVirtualMethodForInterface(abstract_method);
}
if (is_virtual) {
concrete_method = reg_type.GetClass()->FindVirtualMethodForVirtual(abstract_method);
}
- if (concrete_method == NULL || concrete_method->IsAbstract()) {
+ if (concrete_method == nullptr || concrete_method->IsAbstract()) {
// In cases where concrete_method is not found, or is abstract, continue to the next invoke.
continue;
}
@@ -256,10 +273,7 @@
concrete_method->GetDeclaringClass()->IsFinal()) {
// If we knew exactly the class being dispatched upon, or if the target method cannot be
// overridden record the target to be used in the compiler driver.
- MethodReference concrete_ref(
- concrete_method->GetDeclaringClass()->GetDexCache()->GetDexFile(),
- concrete_method->GetDexMethodIndex());
- devirt_map_.Put(dex_pc, concrete_ref);
+ devirt_map_.Put(dex_pc, concrete_method->ToMethodReference());
}
}
}
diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h
index 257e70c..fe9dfd1 100644
--- a/compiler/dex/verified_method.h
+++ b/compiler/dex/verified_method.h
@@ -85,12 +85,19 @@
void GenerateDevirtMap(verifier::MethodVerifier* method_verifier)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Generate dequickening map into dequicken_map_.
+ void GenerateDeQuickenMap(verifier::MethodVerifier* method_verifier)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Generate safe case set into safe_cast_set_.
void GenerateSafeCastSet(verifier::MethodVerifier* method_verifier)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
std::vector<uint8_t> dex_gc_map_;
DevirtualizationMap devirt_map_;
+ // Dequicken map is required for having the compiler compiled quickened invokes. The quicken map
+ // enables us to get the dex method index so that we can get the required argument count.
+ DevirtualizationMap dequicken_map_;
SafeCastSet safe_cast_set_;
};
diff --git a/compiler/dex/vreg_analysis.cc b/compiler/dex/vreg_analysis.cc
index a541c7d..f70850a 100644
--- a/compiler/dex/vreg_analysis.cc
+++ b/compiler/dex/vreg_analysis.cc
@@ -14,8 +14,11 @@
* limitations under the License.
*/
-#include "compiler_internals.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "compiler_ir.h"
#include "dex/dataflow_iterator-inl.h"
+#include "dex_flags.h"
namespace art {
@@ -442,7 +445,7 @@
for (int i = 0; i < GetNumSSARegs(); i++) {
loc[i] = fresh_loc;
loc[i].s_reg_low = i;
- loc[i].is_const = is_constant_v_->IsBitSet(i);
+ loc[i].is_const = false; // Constants will be marked by constant propagation pass later.
loc[i].wide = false;
}
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 3a91b08..9948c82 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -19,7 +19,6 @@
#include "compiler_driver.h"
-#include "dex/compiler_ir.h"
#include "dex_compilation_unit.h"
#include "mirror/art_field-inl.h"
#include "mirror/art_method-inl.h"
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index ab9f41a..b8a8936 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -23,6 +23,10 @@
#include <vector>
#include <unistd.h>
+#ifndef __APPLE__
+#include <malloc.h> // For mallinfo
+#endif
+
#include "base/stl_util.h"
#include "base/timing_logger.h"
#include "class_linker.h"
@@ -58,6 +62,7 @@
#include "thread_pool.h"
#include "trampolines/trampoline_compiler.h"
#include "transaction.h"
+#include "utils/swap_space.h"
#include "verifier/method_verifier.h"
#include "verifier/method_verifier-inl.h"
@@ -334,9 +339,12 @@
const InstructionSetFeatures* instruction_set_features,
bool image, std::set<std::string>* image_classes,
std::set<std::string>* compiled_classes, size_t thread_count,
- bool dump_stats, bool dump_passes, CumulativeLogger* timer,
- const std::string& profile_file)
- : profile_present_(false), compiler_options_(compiler_options),
+ bool dump_stats, bool dump_passes,
+ const std::string& dump_cfg_file_name, CumulativeLogger* timer,
+ int swap_fd, const std::string& profile_file)
+ : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)),
+ swap_space_allocator_(new SwapAllocator<void>(swap_space_.get())),
+ profile_present_(false), compiler_options_(compiler_options),
verification_results_(verification_results),
method_inliner_map_(method_inliner_map),
compiler_(Compiler::Create(this, compiler_kind)),
@@ -345,7 +353,7 @@
freezing_constructor_lock_("freezing constructor lock"),
compiled_classes_lock_("compiled classes lock"),
compiled_methods_lock_("compiled method lock"),
- compiled_methods_(),
+ compiled_methods_(MethodTable::key_compare()),
non_relative_linker_patch_count_(0u),
image_(image),
image_classes_(image_classes),
@@ -354,21 +362,20 @@
stats_(new AOTCompilationStats),
dump_stats_(dump_stats),
dump_passes_(dump_passes),
+ dump_cfg_file_name_(dump_cfg_file_name),
timings_logger_(timer),
compiler_context_(nullptr),
- support_boot_image_fixup_(instruction_set != kMips),
- dedupe_code_("dedupe code"),
- dedupe_src_mapping_table_("dedupe source mapping table"),
- dedupe_mapping_table_("dedupe mapping table"),
- dedupe_vmap_table_("dedupe vmap table"),
- dedupe_gc_map_("dedupe gc map"),
- dedupe_cfi_info_("dedupe cfi info") {
+ support_boot_image_fixup_(instruction_set != kMips && instruction_set != kMips64),
+ dedupe_code_("dedupe code", *swap_space_allocator_),
+ dedupe_src_mapping_table_("dedupe source mapping table", *swap_space_allocator_),
+ dedupe_mapping_table_("dedupe mapping table", *swap_space_allocator_),
+ dedupe_vmap_table_("dedupe vmap table", *swap_space_allocator_),
+ dedupe_gc_map_("dedupe gc map", *swap_space_allocator_),
+ dedupe_cfi_info_("dedupe cfi info", *swap_space_allocator_) {
DCHECK(compiler_options_ != nullptr);
DCHECK(verification_results_ != nullptr);
DCHECK(method_inliner_map_ != nullptr);
- CHECK_PTHREAD_CALL(pthread_key_create, (&tls_key_, nullptr), "compiler tls key");
-
dex_to_dex_compiler_ = reinterpret_cast<DexToDexCompilerFn>(ArtCompileDEX);
compiler_->Init();
@@ -391,31 +398,28 @@
}
}
-std::vector<uint8_t>* CompilerDriver::DeduplicateCode(const std::vector<uint8_t>& code) {
+SwapVector<uint8_t>* CompilerDriver::DeduplicateCode(const ArrayRef<const uint8_t>& code) {
return dedupe_code_.Add(Thread::Current(), code);
}
-SrcMap* CompilerDriver::DeduplicateSrcMappingTable(const SrcMap& src_map) {
+SwapSrcMap* CompilerDriver::DeduplicateSrcMappingTable(const ArrayRef<SrcMapElem>& src_map) {
return dedupe_src_mapping_table_.Add(Thread::Current(), src_map);
}
-std::vector<uint8_t>* CompilerDriver::DeduplicateMappingTable(const std::vector<uint8_t>& code) {
+SwapVector<uint8_t>* CompilerDriver::DeduplicateMappingTable(const ArrayRef<const uint8_t>& code) {
return dedupe_mapping_table_.Add(Thread::Current(), code);
}
-std::vector<uint8_t>* CompilerDriver::DeduplicateVMapTable(const std::vector<uint8_t>& code) {
+SwapVector<uint8_t>* CompilerDriver::DeduplicateVMapTable(const ArrayRef<const uint8_t>& code) {
return dedupe_vmap_table_.Add(Thread::Current(), code);
}
-std::vector<uint8_t>* CompilerDriver::DeduplicateGCMap(const std::vector<uint8_t>& code) {
+SwapVector<uint8_t>* CompilerDriver::DeduplicateGCMap(const ArrayRef<const uint8_t>& code) {
return dedupe_gc_map_.Add(Thread::Current(), code);
}
-std::vector<uint8_t>* CompilerDriver::DeduplicateCFIInfo(const std::vector<uint8_t>* cfi_info) {
- if (cfi_info == nullptr) {
- return nullptr;
- }
- return dedupe_cfi_info_.Add(Thread::Current(), *cfi_info);
+SwapVector<uint8_t>* CompilerDriver::DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info) {
+ return dedupe_cfi_info_.Add(Thread::Current(), cfi_info);
}
CompilerDriver::~CompilerDriver() {
@@ -426,22 +430,13 @@
}
{
MutexLock mu(self, compiled_methods_lock_);
- STLDeleteValues(&compiled_methods_);
+ for (auto& pair : compiled_methods_) {
+ CompiledMethod::ReleaseSwapAllocatedCompiledMethod(this, pair.second);
+ }
}
- CHECK_PTHREAD_CALL(pthread_key_delete, (tls_key_), "delete tls key");
compiler_->UnInit();
}
-CompilerTls* CompilerDriver::GetTls() {
- // Lazily create thread-local storage
- CompilerTls* res = static_cast<CompilerTls*>(pthread_getspecific(tls_key_));
- if (res == nullptr) {
- res = compiler_->CreateNewCompilerTls();
- CHECK_PTHREAD_CALL(pthread_setspecific, (tls_key_, res), "compiler tls");
- }
- return res;
-}
-
#define CREATE_TRAMPOLINE(type, abi, offset) \
if (Is64BitInstructionSet(instruction_set_)) { \
return CreateTrampoline64(instruction_set_, abi, \
@@ -463,18 +458,6 @@
CREATE_TRAMPOLINE(JNI, kJniAbi, pDlsymLookup)
}
-const std::vector<uint8_t>* CompilerDriver::CreatePortableImtConflictTrampoline() const {
- CREATE_TRAMPOLINE(PORTABLE, kPortableAbi, pPortableImtConflictTrampoline)
-}
-
-const std::vector<uint8_t>* CompilerDriver::CreatePortableResolutionTrampoline() const {
- CREATE_TRAMPOLINE(PORTABLE, kPortableAbi, pPortableResolutionTrampoline)
-}
-
-const std::vector<uint8_t>* CompilerDriver::CreatePortableToInterpreterBridge() const {
- CREATE_TRAMPOLINE(PORTABLE, kPortableAbi, pPortableToInterpreterBridge)
-}
-
const std::vector<uint8_t>* CompilerDriver::CreateQuickGenericJniTrampoline() const {
CREATE_TRAMPOLINE(QUICK, kQuickAbi, pQuickGenericJniTrampoline)
}
@@ -497,6 +480,7 @@
TimingLogger* timings) {
DCHECK(!Runtime::Current()->IsStarted());
std::unique_ptr<ThreadPool> thread_pool(new ThreadPool("Compiler driver thread pool", thread_count_ - 1));
+ VLOG(compiler) << "Before precompile " << GetMemoryUsageString(false);
PreCompile(class_loader, dex_files, thread_pool.get(), timings);
Compile(class_loader, dex_files, thread_pool.get(), timings);
if (dump_stats_) {
@@ -593,20 +577,25 @@
void CompilerDriver::PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool, TimingLogger* timings) {
LoadImageClasses(timings);
+ VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false);
Resolve(class_loader, dex_files, thread_pool, timings);
+ VLOG(compiler) << "Resolve: " << GetMemoryUsageString(false);
if (!compiler_options_->IsVerificationEnabled()) {
- LOG(INFO) << "Verify none mode specified, skipping verification.";
+ VLOG(compiler) << "Verify none mode specified, skipping verification.";
SetVerified(class_loader, dex_files, thread_pool, timings);
return;
}
Verify(class_loader, dex_files, thread_pool, timings);
+ VLOG(compiler) << "Verify: " << GetMemoryUsageString(false);
InitializeClasses(class_loader, dex_files, thread_pool, timings);
+ VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false);
UpdateImageClasses(timings);
+ VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false);
}
bool CompilerDriver::IsImageClass(const char* descriptor) const {
@@ -1273,18 +1262,11 @@
// TODO This is somewhat hacky. We should refactor all of this invoke codepath.
const bool force_relocations = (compiling_boot ||
GetCompilerOptions().GetIncludePatchInformation());
- if (compiler_->IsPortable()) {
- if (sharp_type != kStatic && sharp_type != kDirect) {
- return;
- }
- use_dex_cache = true;
- } else {
- if (sharp_type != kStatic && sharp_type != kDirect) {
- return;
- }
- // TODO: support patching on all architectures.
- use_dex_cache = use_dex_cache || (force_relocations && !support_boot_image_fixup_);
+ if (sharp_type != kStatic && sharp_type != kDirect) {
+ return;
}
+ // TODO: support patching on all architectures.
+ use_dex_cache = use_dex_cache || (force_relocations && !support_boot_image_fixup_);
bool method_code_in_boot = (method->GetDeclaringClass()->GetClassLoader() == nullptr);
if (!use_dex_cache) {
if (!method_code_in_boot) {
@@ -1303,7 +1285,15 @@
*stats_flags |= kFlagDirectCallToBoot | kFlagDirectMethodToBoot;
}
if (!use_dex_cache && force_relocations) {
- if (!IsImage() || !IsImageClass(method->GetDeclaringClassDescriptor())) {
+ bool is_in_image;
+ if (IsImage()) {
+ is_in_image = IsImageClass(method->GetDeclaringClassDescriptor());
+ } else {
+ is_in_image = instruction_set_ != kX86 && instruction_set_ != kX86_64 &&
+ Runtime::Current()->GetHeap()->FindSpaceFromObject(method->GetDeclaringClass(),
+ false)->IsImageSpace();
+ }
+ if (!is_in_image) {
// We can only branch directly to Methods that are resolved in the DexCache.
// Otherwise we won't invoke the resolution trampoline.
use_dex_cache = true;
@@ -1395,8 +1385,7 @@
if (resolved_method != nullptr) {
*vtable_idx = GetResolvedMethodVTableIndex(resolved_method, orig_invoke_type);
- if (enable_devirtualization) {
- DCHECK(mUnit->GetVerifiedMethod() != nullptr);
+ if (enable_devirtualization && mUnit->GetVerifiedMethod() != nullptr) {
const MethodReference* devirt_target = mUnit->GetVerifiedMethod()->GetDevirtTarget(dex_pc);
stats_flags = IsFastInvoke(
@@ -1948,7 +1937,7 @@
*file_log << exception->Dump() << "\n";
}
soa.Self()->ClearException();
- transaction.Abort();
+ transaction.Rollback();
CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored";
}
}
@@ -2002,6 +1991,7 @@
CHECK(dex_file != nullptr);
CompileDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
}
+ VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
}
void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, size_t class_def_index) {
@@ -2114,6 +2104,7 @@
case kArm64:
case kThumb2:
case kMips:
+ case kMips64:
case kX86:
case kX86_64: return true;
default: return false;
@@ -2128,6 +2119,7 @@
bool compilation_enabled) {
CompiledMethod* compiled_method = nullptr;
uint64_t start_ns = kTimeCompileMethod ? NanoTime() : 0;
+ MethodReference method_ref(&dex_file, method_idx);
if ((access_flags & kAccNative) != 0) {
// Are we interpreting only and have support for generic JNI down calls?
@@ -2141,9 +2133,12 @@
} else if ((access_flags & kAccAbstract) != 0) {
// Abstract methods don't have code.
} else {
- MethodReference method_ref(&dex_file, method_idx);
+ bool has_verified_method = verification_results_->GetVerifiedMethod(method_ref) != nullptr;
bool compile = compilation_enabled &&
- verification_results_->IsCandidateForCompilation(method_ref, access_flags);
+ // Basic checks, e.g., not <clinit>.
+ verification_results_->IsCandidateForCompilation(method_ref, access_flags) &&
+ // Did not fail to create VerifiedMethod metadata.
+ has_verified_method;
if (compile) {
// NOTE: if compiler declines to compile this method, it will return nullptr.
compiled_method = compiler_->Compile(code_item, access_flags, invoke_type, class_def_idx,
@@ -2151,10 +2146,12 @@
}
if (compiled_method == nullptr && dex_to_dex_compilation_level != kDontDexToDexCompile) {
// TODO: add a command-line option to disable DEX-to-DEX compilation ?
+ // Do not optimize if a VerifiedMethod is missing. SafeCast elision, for example, relies on
+ // it.
(*dex_to_dex_compiler_)(*this, code_item, access_flags,
invoke_type, class_def_idx,
method_idx, class_loader, dex_file,
- dex_to_dex_compilation_level);
+ has_verified_method ? dex_to_dex_compilation_level : kRequired);
}
}
if (kTimeCompileMethod) {
@@ -2178,16 +2175,18 @@
// When compiling with PIC, there should be zero non-relative linker patches
CHECK(!compile_pic || non_relative_linker_patch_count == 0u);
- MethodReference ref(&dex_file, method_idx);
- DCHECK(GetCompiledMethod(ref) == nullptr) << PrettyMethod(method_idx, dex_file);
+ DCHECK(GetCompiledMethod(method_ref) == nullptr) << PrettyMethod(method_idx, dex_file);
{
MutexLock mu(self, compiled_methods_lock_);
- compiled_methods_.Put(ref, compiled_method);
+ compiled_methods_.Put(method_ref, compiled_method);
non_relative_linker_patch_count_ += non_relative_linker_patch_count;
}
- DCHECK(GetCompiledMethod(ref) != nullptr) << PrettyMethod(method_idx, dex_file);
+ DCHECK(GetCompiledMethod(method_ref) != nullptr) << PrettyMethod(method_idx, dex_file);
}
+ // Done compiling, delete the verified method to reduce native memory usage.
+ verification_results_->RemoveVerifiedMethod(method_ref);
+
if (self->IsExceptionPending()) {
ScopedObjectAccess soa(self);
LOG(FATAL) << "Unexpected exception compiling: " << PrettyMethod(method_idx, dex_file) << "\n"
@@ -2337,4 +2336,31 @@
}
return !compile;
}
+
+std::string CompilerDriver::GetMemoryUsageString(bool extended) const {
+ std::ostringstream oss;
+ const ArenaPool* arena_pool = GetArenaPool();
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ oss << "arena alloc=" << PrettySize(arena_pool->GetBytesAllocated());
+ oss << " java alloc=" << PrettySize(heap->GetBytesAllocated());
+#ifdef HAVE_MALLOC_H
+ struct mallinfo info = mallinfo();
+ const size_t allocated_space = static_cast<size_t>(info.uordblks);
+ const size_t free_space = static_cast<size_t>(info.fordblks);
+ oss << " native alloc=" << PrettySize(allocated_space) << " free="
+ << PrettySize(free_space);
+#endif
+ if (swap_space_.get() != nullptr) {
+ oss << " swap=" << PrettySize(swap_space_->GetSize());
+ }
+ if (extended) {
+ oss << "\nCode dedupe: " << dedupe_code_.DumpStats();
+ oss << "\nMapping table dedupe: " << dedupe_mapping_table_.DumpStats();
+ oss << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats();
+ oss << "\nGC map dedupe: " << dedupe_gc_map_.DumpStats();
+ oss << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats();
+ }
+ return oss.str();
+}
+
} // namespace art
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index d837dbc..2fca2e5 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -28,6 +28,7 @@
#include "compiled_method.h"
#include "compiler.h"
#include "dex_file.h"
+#include "dex/verified_method.h"
#include "driver/compiler_options.h"
#include "invoke_type.h"
#include "method_reference.h"
@@ -39,6 +40,8 @@
#include "thread_pool.h"
#include "utils/arena_allocator.h"
#include "utils/dedupe_set.h"
+#include "utils/swap_space.h"
+#include "utils.h"
namespace art {
@@ -65,8 +68,6 @@
kInterpreterAbi,
// ABI of calls to a method's native code, only used for native methods.
kJniAbi,
- // ABI of calls to a method's portable code entry point.
- kPortableAbi,
// ABI of calls to a method's quick code entry point.
kQuickAbi
};
@@ -78,6 +79,8 @@
};
std::ostream& operator<<(std::ostream& os, const DexToDexCompilationLevel& rhs);
+static constexpr bool kUseMurmur3Hash = true;
+
class CompilerDriver {
public:
// Create a compiler targeting the requested "instruction_set".
@@ -94,7 +97,9 @@
bool image, std::set<std::string>* image_classes,
std::set<std::string>* compiled_classes,
size_t thread_count, bool dump_stats, bool dump_passes,
- CumulativeLogger* timer, const std::string& profile_file);
+ const std::string& dump_cfg_file_name,
+ CumulativeLogger* timer, int swap_fd,
+ const std::string& profile_file);
~CompilerDriver();
@@ -143,8 +148,6 @@
return image_classes_.get();
}
- CompilerTls* GetTls();
-
// Generate the trampolines that are invoked by unresolved direct methods.
const std::vector<uint8_t>* CreateInterpreterToInterpreterBridge() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -152,12 +155,6 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateJniDlsymLookup() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const std::vector<uint8_t>* CreatePortableImtConflictTrampoline() const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const std::vector<uint8_t>* CreatePortableResolutionTrampoline() const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const std::vector<uint8_t>* CreatePortableToInterpreterBridge() const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateQuickGenericJniTrampoline() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateQuickImtConflictTrampoline() const
@@ -343,6 +340,9 @@
const ArenaPool* GetArenaPool() const {
return &arena_pool_;
}
+ SwapAllocator<void>& GetSwapSpaceAllocator() {
+ return *swap_space_allocator_.get();
+ }
bool WriteElf(const std::string& android_root,
bool is_host,
@@ -372,6 +372,10 @@
return dump_passes_;
}
+ const std::string& GetDumpCfgFileName() const {
+ return dump_cfg_file_name_;
+ }
+
CumulativeLogger* GetTimingsLogger() const {
return timings_logger_;
}
@@ -385,19 +389,19 @@
void RecordClassStatus(ClassReference ref, mirror::Class::Status status)
LOCKS_EXCLUDED(compiled_classes_lock_);
- std::vector<uint8_t>* DeduplicateCode(const std::vector<uint8_t>& code);
- SrcMap* DeduplicateSrcMappingTable(const SrcMap& src_map);
- std::vector<uint8_t>* DeduplicateMappingTable(const std::vector<uint8_t>& code);
- std::vector<uint8_t>* DeduplicateVMapTable(const std::vector<uint8_t>& code);
- std::vector<uint8_t>* DeduplicateGCMap(const std::vector<uint8_t>& code);
- std::vector<uint8_t>* DeduplicateCFIInfo(const std::vector<uint8_t>* cfi_info);
-
- ProfileFile profile_file_;
- bool profile_present_;
+ SwapVector<uint8_t>* DeduplicateCode(const ArrayRef<const uint8_t>& code);
+ SwapSrcMap* DeduplicateSrcMappingTable(const ArrayRef<SrcMapElem>& src_map);
+ SwapVector<uint8_t>* DeduplicateMappingTable(const ArrayRef<const uint8_t>& code);
+ SwapVector<uint8_t>* DeduplicateVMapTable(const ArrayRef<const uint8_t>& code);
+ SwapVector<uint8_t>* DeduplicateGCMap(const ArrayRef<const uint8_t>& code);
+ SwapVector<uint8_t>* DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info);
// Should the compiler run on this method given profile information?
bool SkipCompilation(const std::string& method_name);
+ // Get memory usage during compilation.
+ std::string GetMemoryUsageString(bool extended) const;
+
private:
// These flags are internal to CompilerDriver for collecting INVOKE resolution statistics.
// The only external contract is that unresolved method has flags 0 and resolved non-0.
@@ -490,6 +494,14 @@
static void CompileClass(const ParallelCompilationManager* context, size_t class_def_index)
LOCKS_EXCLUDED(Locks::mutator_lock_);
+ // Swap pool and allocator used for native allocations. May be file-backed. Needs to be first
+ // as other fields rely on this.
+ std::unique_ptr<SwapSpace> swap_space_;
+ std::unique_ptr<SwapAllocator<void> > swap_space_allocator_;
+
+ ProfileFile profile_file_;
+ bool profile_present_;
+
const CompilerOptions* const compiler_options_;
VerificationResults* const verification_results_;
DexFileToMethodInlinerMap* const method_inliner_map_;
@@ -535,6 +547,7 @@
bool dump_stats_;
const bool dump_passes_;
+ const std::string& dump_cfg_file_name_;
CumulativeLogger* const timings_logger_;
@@ -551,55 +564,98 @@
void* compiler_context_;
- pthread_key_t tls_key_;
-
// Arena pool used by the compiler.
ArenaPool arena_pool_;
bool support_boot_image_fixup_;
// DeDuplication data structures, these own the corresponding byte arrays.
- template <typename ByteArray>
+ template <typename ContentType>
class DedupeHashFunc {
public:
- size_t operator()(const ByteArray& array) const {
- // For small arrays compute a hash using every byte.
- static const size_t kSmallArrayThreshold = 16;
- size_t hash = 0x811c9dc5;
- if (array.size() <= kSmallArrayThreshold) {
- for (auto b : array) {
- hash = (hash * 16777619) ^ static_cast<uint8_t>(b);
+ size_t operator()(const ArrayRef<ContentType>& array) const {
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(array.data());
+ static_assert(IsPowerOfTwo(sizeof(ContentType)),
+ "ContentType is not power of two, don't know whether array layout is as assumed");
+ uint32_t len = sizeof(ContentType) * array.size();
+ if (kUseMurmur3Hash) {
+ static constexpr uint32_t c1 = 0xcc9e2d51;
+ static constexpr uint32_t c2 = 0x1b873593;
+ static constexpr uint32_t r1 = 15;
+ static constexpr uint32_t r2 = 13;
+ static constexpr uint32_t m = 5;
+ static constexpr uint32_t n = 0xe6546b64;
+
+ uint32_t hash = 0;
+
+ const int nblocks = len / 4;
+ typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t;
+ const unaligned_uint32_t *blocks = reinterpret_cast<const uint32_t*>(data);
+ int i;
+ for (i = 0; i < nblocks; i++) {
+ uint32_t k = blocks[i];
+ k *= c1;
+ k = (k << r1) | (k >> (32 - r1));
+ k *= c2;
+
+ hash ^= k;
+ hash = ((hash << r2) | (hash >> (32 - r2))) * m + n;
}
+
+ const uint8_t *tail = reinterpret_cast<const uint8_t*>(data + nblocks * 4);
+ uint32_t k1 = 0;
+
+ switch (len & 3) {
+ case 3:
+ k1 ^= tail[2] << 16;
+ FALLTHROUGH_INTENDED;
+ case 2:
+ k1 ^= tail[1] << 8;
+ FALLTHROUGH_INTENDED;
+ case 1:
+ k1 ^= tail[0];
+
+ k1 *= c1;
+ k1 = (k1 << r1) | (k1 >> (32 - r1));
+ k1 *= c2;
+ hash ^= k1;
+ }
+
+ hash ^= len;
+ hash ^= (hash >> 16);
+ hash *= 0x85ebca6b;
+ hash ^= (hash >> 13);
+ hash *= 0xc2b2ae35;
+ hash ^= (hash >> 16);
+
+ return hash;
} else {
- // For larger arrays use the 2 bytes at 6 bytes (the location of a push registers
- // instruction field for quick generated code on ARM) and then select a number of other
- // values at random.
- static const size_t kRandomHashCount = 16;
- for (size_t i = 0; i < 2; ++i) {
- uint8_t b = static_cast<uint8_t>(array[i + 6]);
- hash = (hash * 16777619) ^ b;
+ size_t hash = 0x811c9dc5;
+ for (uint32_t i = 0; i < len; ++i) {
+ hash = (hash * 16777619) ^ data[i];
}
- for (size_t i = 2; i < kRandomHashCount; ++i) {
- size_t r = i * 1103515245 + 12345;
- uint8_t b = static_cast<uint8_t>(array[r % array.size()]);
- hash = (hash * 16777619) ^ b;
- }
+ hash += hash << 13;
+ hash ^= hash >> 7;
+ hash += hash << 3;
+ hash ^= hash >> 17;
+ hash += hash << 5;
+ return hash;
}
- hash += hash << 13;
- hash ^= hash >> 7;
- hash += hash << 3;
- hash ^= hash >> 17;
- hash += hash << 5;
- return hash;
}
};
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_code_;
- DedupeSet<SrcMap, size_t, DedupeHashFunc<SrcMap>, 4> dedupe_src_mapping_table_;
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_mapping_table_;
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_vmap_table_;
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_gc_map_;
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_cfi_info_;
+ DedupeSet<ArrayRef<const uint8_t>,
+ SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_code_;
+ DedupeSet<ArrayRef<SrcMapElem>,
+ SwapSrcMap, size_t, DedupeHashFunc<SrcMapElem>, 4> dedupe_src_mapping_table_;
+ DedupeSet<ArrayRef<const uint8_t>,
+ SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_mapping_table_;
+ DedupeSet<ArrayRef<const uint8_t>,
+ SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_vmap_table_;
+ DedupeSet<ArrayRef<const uint8_t>,
+ SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_gc_map_;
+ DedupeSet<ArrayRef<const uint8_t>,
+ SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_cfi_info_;
DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
};
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 5a0ec2f..a02e25e 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -106,40 +106,37 @@
// All libcore references should resolve
ScopedObjectAccess soa(Thread::Current());
- const DexFile* dex = java_lang_dex_file_;
- mirror::DexCache* dex_cache = class_linker_->FindDexCache(*dex);
- EXPECT_EQ(dex->NumStringIds(), dex_cache->NumStrings());
+ ASSERT_TRUE(java_lang_dex_file_ != NULL);
+ const DexFile& dex = *java_lang_dex_file_;
+ mirror::DexCache* dex_cache = class_linker_->FindDexCache(dex);
+ EXPECT_EQ(dex.NumStringIds(), dex_cache->NumStrings());
for (size_t i = 0; i < dex_cache->NumStrings(); i++) {
const mirror::String* string = dex_cache->GetResolvedString(i);
EXPECT_TRUE(string != NULL) << "string_idx=" << i;
}
- EXPECT_EQ(dex->NumTypeIds(), dex_cache->NumResolvedTypes());
+ EXPECT_EQ(dex.NumTypeIds(), dex_cache->NumResolvedTypes());
for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
mirror::Class* type = dex_cache->GetResolvedType(i);
EXPECT_TRUE(type != NULL) << "type_idx=" << i
- << " " << dex->GetTypeDescriptor(dex->GetTypeId(i));
+ << " " << dex.GetTypeDescriptor(dex.GetTypeId(i));
}
- EXPECT_EQ(dex->NumMethodIds(), dex_cache->NumResolvedMethods());
+ EXPECT_EQ(dex.NumMethodIds(), dex_cache->NumResolvedMethods());
for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) {
mirror::ArtMethod* method = dex_cache->GetResolvedMethod(i);
EXPECT_TRUE(method != NULL) << "method_idx=" << i
- << " " << dex->GetMethodDeclaringClassDescriptor(dex->GetMethodId(i))
- << " " << dex->GetMethodName(dex->GetMethodId(i));
+ << " " << dex.GetMethodDeclaringClassDescriptor(dex.GetMethodId(i))
+ << " " << dex.GetMethodName(dex.GetMethodId(i));
EXPECT_TRUE(method->GetEntryPointFromQuickCompiledCode() != NULL) << "method_idx=" << i
<< " "
- << dex->GetMethodDeclaringClassDescriptor(dex->GetMethodId(i))
- << " " << dex->GetMethodName(dex->GetMethodId(i));
- EXPECT_TRUE(method->GetEntryPointFromPortableCompiledCode() != NULL) << "method_idx=" << i
- << " "
- << dex->GetMethodDeclaringClassDescriptor(dex->GetMethodId(i))
- << " " << dex->GetMethodName(dex->GetMethodId(i));
+ << dex.GetMethodDeclaringClassDescriptor(dex.GetMethodId(i))
+ << " " << dex.GetMethodName(dex.GetMethodId(i));
}
- EXPECT_EQ(dex->NumFieldIds(), dex_cache->NumResolvedFields());
+ EXPECT_EQ(dex.NumFieldIds(), dex_cache->NumResolvedFields());
for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) {
mirror::ArtField* field = dex_cache->GetResolvedField(i);
EXPECT_TRUE(field != NULL) << "field_idx=" << i
- << " " << dex->GetFieldDeclaringClassDescriptor(dex->GetFieldId(i))
- << " " << dex->GetFieldName(dex->GetFieldId(i));
+ << " " << dex.GetFieldDeclaringClassDescriptor(dex.GetFieldId(i))
+ << " " << dex.GetFieldName(dex.GetFieldId(i));
}
// TODO check Class::IsVerified for all classes
@@ -148,7 +145,6 @@
}
TEST_F(CompilerDriverTest, AbstractMethodErrorStub) {
- TEST_DISABLED_FOR_PORTABLE();
TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
jobject class_loader;
{
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
new file mode 100644
index 0000000..09ec9a2
--- /dev/null
+++ b/compiler/driver/compiler_options.cc
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+#include "compiler_options.h"
+
+#include "dex/pass_manager.h"
+
+namespace art {
+
+CompilerOptions::CompilerOptions()
+ : compiler_filter_(kDefaultCompilerFilter),
+ huge_method_threshold_(kDefaultHugeMethodThreshold),
+ large_method_threshold_(kDefaultLargeMethodThreshold),
+ small_method_threshold_(kDefaultSmallMethodThreshold),
+ tiny_method_threshold_(kDefaultTinyMethodThreshold),
+ num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold),
+ generate_gdb_information_(false),
+ include_patch_information_(kDefaultIncludePatchInformation),
+ top_k_profile_threshold_(kDefaultTopKProfileThreshold),
+ include_debug_symbols_(kDefaultIncludeDebugSymbols),
+ implicit_null_checks_(true),
+ implicit_so_checks_(true),
+ implicit_suspend_checks_(false),
+ compile_pic_(false),
+ verbose_methods_(nullptr),
+ pass_manager_options_(new PassManagerOptions),
+ init_failure_output_(nullptr) {
+}
+
+CompilerOptions::CompilerOptions(CompilerFilter compiler_filter,
+ size_t huge_method_threshold,
+ size_t large_method_threshold,
+ size_t small_method_threshold,
+ size_t tiny_method_threshold,
+ size_t num_dex_methods_threshold,
+ bool generate_gdb_information,
+ bool include_patch_information,
+ double top_k_profile_threshold,
+ bool include_debug_symbols,
+ bool implicit_null_checks,
+ bool implicit_so_checks,
+ bool implicit_suspend_checks,
+ bool compile_pic,
+ const std::vector<std::string>* verbose_methods,
+ PassManagerOptions* pass_manager_options,
+ std::ostream* init_failure_output
+ ) : // NOLINT(whitespace/parens)
+ compiler_filter_(compiler_filter),
+ huge_method_threshold_(huge_method_threshold),
+ large_method_threshold_(large_method_threshold),
+ small_method_threshold_(small_method_threshold),
+ tiny_method_threshold_(tiny_method_threshold),
+ num_dex_methods_threshold_(num_dex_methods_threshold),
+ generate_gdb_information_(generate_gdb_information),
+ include_patch_information_(include_patch_information),
+ top_k_profile_threshold_(top_k_profile_threshold),
+ include_debug_symbols_(include_debug_symbols),
+ implicit_null_checks_(implicit_null_checks),
+ implicit_so_checks_(implicit_so_checks),
+ implicit_suspend_checks_(implicit_suspend_checks),
+ compile_pic_(compile_pic),
+ verbose_methods_(verbose_methods),
+ pass_manager_options_(pass_manager_options),
+ init_failure_output_(init_failure_output) {
+}
+
+} // namespace art
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index aec7d24..122ae4b 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -26,6 +26,8 @@
namespace art {
+class PassManagerOptions;
+
class CompilerOptions FINAL {
public:
enum CompilerFilter {
@@ -53,27 +55,7 @@
static const bool kDefaultIncludeDebugSymbols = kIsDebugBuild;
static const bool kDefaultIncludePatchInformation = false;
- CompilerOptions() :
- compiler_filter_(kDefaultCompilerFilter),
- huge_method_threshold_(kDefaultHugeMethodThreshold),
- large_method_threshold_(kDefaultLargeMethodThreshold),
- small_method_threshold_(kDefaultSmallMethodThreshold),
- tiny_method_threshold_(kDefaultTinyMethodThreshold),
- num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold),
- generate_gdb_information_(false),
- include_patch_information_(kDefaultIncludePatchInformation),
- top_k_profile_threshold_(kDefaultTopKProfileThreshold),
- include_debug_symbols_(kDefaultIncludeDebugSymbols),
- implicit_null_checks_(false),
- implicit_so_checks_(false),
- implicit_suspend_checks_(false),
- compile_pic_(false),
-#ifdef ART_SEA_IR_MODE
- sea_ir_mode_(false),
-#endif
- verbose_methods_(nullptr),
- init_failure_output_(nullptr) {
- }
+ CompilerOptions();
CompilerOptions(CompilerFilter compiler_filter,
size_t huge_method_threshold,
@@ -89,32 +71,9 @@
bool implicit_so_checks,
bool implicit_suspend_checks,
bool compile_pic,
-#ifdef ART_SEA_IR_MODE
- bool sea_ir_mode,
-#endif
const std::vector<std::string>* verbose_methods,
- std::ostream* init_failure_output
- ) : // NOLINT(whitespace/parens)
- compiler_filter_(compiler_filter),
- huge_method_threshold_(huge_method_threshold),
- large_method_threshold_(large_method_threshold),
- small_method_threshold_(small_method_threshold),
- tiny_method_threshold_(tiny_method_threshold),
- num_dex_methods_threshold_(num_dex_methods_threshold),
- generate_gdb_information_(generate_gdb_information),
- include_patch_information_(include_patch_information),
- top_k_profile_threshold_(top_k_profile_threshold),
- include_debug_symbols_(include_debug_symbols),
- implicit_null_checks_(implicit_null_checks),
- implicit_so_checks_(implicit_so_checks),
- implicit_suspend_checks_(implicit_suspend_checks),
- compile_pic_(compile_pic),
-#ifdef ART_SEA_IR_MODE
- sea_ir_mode_(sea_ir_mode),
-#endif
- verbose_methods_(verbose_methods),
- init_failure_output_(init_failure_output) {
- }
+ PassManagerOptions* pass_manager_options,
+ std::ostream* init_failure_output);
CompilerFilter GetCompilerFilter() const {
return compiler_filter_;
@@ -189,12 +148,6 @@
return implicit_suspend_checks_;
}
-#ifdef ART_SEA_IR_MODE
- bool GetSeaIrMode() const {
- return sea_ir_mode_;
- }
-#endif
-
bool GetGenerateGDBInformation() const {
return generate_gdb_information_;
}
@@ -225,6 +178,10 @@
return init_failure_output_;
}
+ const PassManagerOptions* GetPassManagerOptions() const {
+ return pass_manager_options_.get();
+ }
+
private:
CompilerFilter compiler_filter_;
const size_t huge_method_threshold_;
@@ -242,13 +199,11 @@
const bool implicit_suspend_checks_;
const bool compile_pic_;
-#ifdef ART_SEA_IR_MODE
- const bool sea_ir_mode_;
-#endif
-
// Vector of methods to have verbose output enabled for.
const std::vector<std::string>* const verbose_methods_;
+ std::unique_ptr<PassManagerOptions> pass_manager_options_;
+
// Log initialization of initialization failures to this stream if not null.
std::ostream* const init_failure_output_;
diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc
index 986fc71..e6c8c18 100644
--- a/compiler/driver/dex_compilation_unit.cc
+++ b/compiler/driver/dex_compilation_unit.cc
@@ -18,7 +18,6 @@
#include "base/stringprintf.h"
#include "dex/compiler_ir.h"
-#include "dex/mir_graph.h"
#include "utils.h"
namespace art {
diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h
index 84f5799..03ae489 100644
--- a/compiler/driver/dex_compilation_unit.h
+++ b/compiler/driver/dex_compilation_unit.h
@@ -102,6 +102,10 @@
return verified_method_;
}
+ void ClearVerifiedMethod() {
+ verified_method_ = nullptr;
+ }
+
const std::string& GetSymbol();
private:
@@ -117,7 +121,7 @@
const uint16_t class_def_idx_;
const uint32_t dex_method_idx_;
const uint32_t access_flags_;
- const VerifiedMethod* const verified_method_;
+ const VerifiedMethod* verified_method_;
std::string symbol_;
};
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index 273b62d..94268de 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -1108,6 +1108,14 @@
EF_MIPS_ARCH_32R2);
break;
}
+ case kMips64: {
+ elf_header_.e_machine = EM_MIPS;
+ elf_header_.e_flags = (EF_MIPS_NOREORDER |
+ EF_MIPS_PIC |
+ EF_MIPS_CPIC |
+ EF_MIPS_ARCH_64R6);
+ break;
+ }
default: {
fatal_error_ = true;
LOG(FATAL) << "Unknown instruction set: " << isa;
diff --git a/compiler/elf_writer_mclinker.cc b/compiler/elf_writer_mclinker.cc
deleted file mode 100644
index 7705b9c..0000000
--- a/compiler/elf_writer_mclinker.cc
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "elf_writer_mclinker.h"
-
-#include <llvm/Support/ELF.h>
-#include <llvm/Support/TargetSelect.h>
-
-#include <mcld/Environment.h>
-#include <mcld/IRBuilder.h>
-#include <mcld/Linker.h>
-#include <mcld/LinkerConfig.h>
-#include <mcld/LinkerScript.h>
-#include <mcld/MC/ZOption.h>
-#include <mcld/Module.h>
-#include <mcld/Support/Path.h>
-#include <mcld/Support/TargetSelect.h>
-
-#include "base/unix_file/fd_file.h"
-#include "class_linker.h"
-#include "dex_method_iterator.h"
-#include "driver/compiler_driver.h"
-#include "elf_file.h"
-#include "globals.h"
-#include "mirror/art_method.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
-#include "oat_writer.h"
-#include "scoped_thread_state_change.h"
-#include "vector_output_stream.h"
-
-namespace art {
-
-ElfWriterMclinker::ElfWriterMclinker(const CompilerDriver& driver, File* elf_file)
- : ElfWriter(driver, elf_file), oat_input_(nullptr) {
-}
-
-ElfWriterMclinker::~ElfWriterMclinker() {
-}
-
-bool ElfWriterMclinker::Create(File* elf_file,
- OatWriter* oat_writer,
- const std::vector<const DexFile*>& dex_files,
- const std::string& android_root,
- bool is_host,
- const CompilerDriver& driver) {
- ElfWriterMclinker elf_writer(driver, elf_file);
- return elf_writer.Write(oat_writer, dex_files, android_root, is_host);
-}
-
-bool ElfWriterMclinker::Write(OatWriter* oat_writer,
- const std::vector<const DexFile*>& dex_files,
- const std::string& android_root,
- bool is_host) {
- std::vector<uint8_t> oat_contents;
- oat_contents.reserve(oat_writer->GetSize());
-
- Init();
- mcld::LDSection* oat_section = AddOatInput(oat_writer, &oat_contents);
- if (kUsePortableCompiler) {
- AddMethodInputs(dex_files);
- AddRuntimeInputs(android_root, is_host);
- }
-
- // link inputs
- if (!linker_->link(*module_.get(), *ir_builder_.get())) {
- LOG(ERROR) << "Failed to link " << elf_file_->GetPath();
- return false;
- }
-
- // Fill oat_contents.
- VectorOutputStream output_stream("oat contents", &oat_contents);
- oat_writer->SetOatDataOffset(oat_section->offset());
- CHECK(oat_writer->Write(&output_stream));
- CHECK_EQ(oat_writer->GetSize(), oat_contents.size());
-
- // emit linked output
- // TODO: avoid dup of fd by fixing Linker::emit to not close the argument fd.
- int fd = dup(elf_file_->Fd());
- if (fd == -1) {
- PLOG(ERROR) << "Failed to dup file descriptor for " << elf_file_->GetPath();
- return false;
- }
- if (!linker_->emit(*module_.get(), fd)) {
- LOG(ERROR) << "Failed to emit " << elf_file_->GetPath();
- return false;
- }
- mcld::Finalize();
- LOG(INFO) << "ELF file written successfully: " << elf_file_->GetPath();
-
- oat_contents.clear();
- if (kUsePortableCompiler) {
- FixupOatMethodOffsets(dex_files);
- }
- return true;
-}
-
-static void InitializeLLVM() {
- // TODO: this is lifted from art's compiler_llvm.cc, should be factored out
- if (kIsTargetBuild) {
- llvm::InitializeNativeTarget();
- // TODO: odd that there is no InitializeNativeTargetMC?
- } else {
- llvm::InitializeAllTargets();
- llvm::InitializeAllTargetMCs();
- }
-}
-
-void ElfWriterMclinker::Init() {
- std::string target_triple;
- std::string target_cpu;
- std::string target_attr;
- CompilerDriver::InstructionSetToLLVMTarget(compiler_driver_->GetInstructionSet(),
- &target_triple,
- &target_cpu,
- &target_attr);
-
- // Based on mclinker's llvm-mcld.cpp main() and LinkerTest
- //
- // TODO: LinkerTest uses mcld::Initialize(), but it does an
- // llvm::InitializeAllTargets, which we don't want. Basically we
- // want mcld::InitializeNative, but it doesn't exist yet, so we
- // inline the minimal we need here.
- InitializeLLVM();
- mcld::InitializeAllTargets();
- mcld::InitializeAllLinkers();
- mcld::InitializeAllEmulations();
- mcld::InitializeAllDiagnostics();
-
- linker_config_.reset(new mcld::LinkerConfig(target_triple));
- CHECK(linker_config_.get() != NULL);
- linker_config_->setCodeGenType(mcld::LinkerConfig::DynObj);
- linker_config_->options().setSOName(elf_file_->GetPath());
-
- // error on undefined symbols.
- // TODO: should this just be set if kIsDebugBuild?
- linker_config_->options().setNoUndefined(true);
-
- if (compiler_driver_->GetInstructionSet() == kMips) {
- // MCLinker defaults MIPS section alignment to 0x10000, not
- // 0x1000. The ABI says this is because the max page size is
- // general is 64k but that isn't true on Android.
- mcld::ZOption z_option;
- z_option.setKind(mcld::ZOption::MaxPageSize);
- z_option.setPageSize(kPageSize);
- linker_config_->options().addZOption(z_option);
- }
-
- // TODO: Wire up mcld DiagnosticEngine to LOG?
- linker_config_->options().setColor(false);
- if (false) {
- // enables some tracing of input file processing
- linker_config_->options().setTrace(true);
- }
-
- // Based on alone::Linker::config
- linker_script_.reset(new mcld::LinkerScript());
- module_.reset(new mcld::Module(linker_config_->options().soname(), *linker_script_.get()));
- CHECK(module_.get() != NULL);
- ir_builder_.reset(new mcld::IRBuilder(*module_.get(), *linker_config_.get()));
- CHECK(ir_builder_.get() != NULL);
- linker_.reset(new mcld::Linker());
- CHECK(linker_.get() != NULL);
- linker_->emulate(*linker_script_.get(), *linker_config_.get());
-}
-
-mcld::LDSection* ElfWriterMclinker::AddOatInput(OatWriter* oat_writer,
- std::vector<uint8_t>* oat_contents) {
- // NOTE: oat_contents has sufficient reserved space but it doesn't contain the data yet.
- const char* oat_data_start = reinterpret_cast<const char*>(&(*oat_contents)[0]);
- const size_t oat_data_length = oat_writer->GetOatHeader().GetExecutableOffset();
- const char* oat_code_start = oat_data_start + oat_data_length;
- const size_t oat_code_length = oat_writer->GetSize() - oat_data_length;
-
- // TODO: ownership of oat_input?
- oat_input_ = ir_builder_->CreateInput("oat contents",
- mcld::sys::fs::Path("oat contents path"),
- mcld::Input::Object);
- CHECK(oat_input_ != NULL);
-
- // TODO: ownership of null_section?
- mcld::LDSection* null_section = ir_builder_->CreateELFHeader(*oat_input_,
- "",
- mcld::LDFileFormat::Null,
- SHT_NULL,
- 0);
- CHECK(null_section != NULL);
-
- // TODO: we should split readonly data from readonly executable
- // code like .oat does. We need to control section layout with
- // linker script like functionality to guarantee references
- // between sections maintain relative position which isn't
- // possible right now with the mclinker APIs.
- CHECK(oat_code_start != NULL);
-
- // we need to ensure that oatdata is page aligned so when we
- // fixup the segment load addresses, they remain page aligned.
- uint32_t alignment = kPageSize;
-
- // TODO: ownership of text_section?
- mcld::LDSection* text_section = ir_builder_->CreateELFHeader(*oat_input_,
- ".text",
- SHT_PROGBITS,
- SHF_EXECINSTR | SHF_ALLOC,
- alignment);
- CHECK(text_section != NULL);
-
- mcld::SectionData* text_sectiondata = ir_builder_->CreateSectionData(*text_section);
- CHECK(text_sectiondata != NULL);
-
- // TODO: why does IRBuilder::CreateRegion take a non-const pointer?
- mcld::Fragment* text_fragment = ir_builder_->CreateRegion(const_cast<char*>(oat_data_start),
- oat_writer->GetSize());
- CHECK(text_fragment != NULL);
- ir_builder_->AppendFragment(*text_fragment, *text_sectiondata);
-
- ir_builder_->AddSymbol(*oat_input_,
- "oatdata",
- mcld::ResolveInfo::Object,
- mcld::ResolveInfo::Define,
- mcld::ResolveInfo::Global,
- oat_data_length, // size
- 0, // offset
- text_section);
-
- ir_builder_->AddSymbol(*oat_input_,
- "oatexec",
- mcld::ResolveInfo::Function,
- mcld::ResolveInfo::Define,
- mcld::ResolveInfo::Global,
- oat_code_length, // size
- oat_data_length, // offset
- text_section);
-
- ir_builder_->AddSymbol(*oat_input_,
- "oatlastword",
- mcld::ResolveInfo::Object,
- mcld::ResolveInfo::Define,
- mcld::ResolveInfo::Global,
- 0, // size
- // subtract a word so symbol is within section
- (oat_data_length + oat_code_length) - sizeof(uint32_t), // offset
- text_section);
-
- return text_section;
-}
-
-void ElfWriterMclinker::AddMethodInputs(const std::vector<const DexFile*>& dex_files) {
- DCHECK(oat_input_ != NULL);
-
- DexMethodIterator it(dex_files);
- while (it.HasNext()) {
- const DexFile& dex_file = it.GetDexFile();
- uint32_t method_idx = it.GetMemberIndex();
- const CompiledMethod* compiled_method =
- compiler_driver_->GetCompiledMethod(MethodReference(&dex_file, method_idx));
- if (compiled_method != NULL) {
- AddCompiledCodeInput(*compiled_method);
- }
- it.Next();
- }
- added_symbols_.clear();
-}
-
-void ElfWriterMclinker::AddCompiledCodeInput(const CompiledCode& compiled_code) {
- // Check if we've seen this compiled code before. If so skip
- // it. This can happen for reused code such as invoke stubs.
- const std::string& symbol = compiled_code.GetSymbol();
- SafeMap<const std::string*, const std::string*>::iterator it = added_symbols_.find(&symbol);
- if (it != added_symbols_.end()) {
- return;
- }
- added_symbols_.Put(&symbol, &symbol);
-
- // Add input to supply code for symbol
- const std::vector<uint8_t>* code = compiled_code.GetPortableCode();
- // TODO: ownership of code_input?
- // TODO: why does IRBuilder::ReadInput take a non-const pointer?
- mcld::Input* code_input = ir_builder_->ReadInput(symbol,
- const_cast<uint8_t*>(&(*code)[0]),
- code->size());
- CHECK(code_input != NULL);
-}
-
-void ElfWriterMclinker::AddRuntimeInputs(const std::string& android_root, bool is_host) {
- std::string libart_so(android_root);
- libart_so += kIsDebugBuild ? "/lib/libartd.so" : "/lib/libart.so";
- // TODO: ownership of libart_so_input?
- mcld::Input* libart_so_input = ir_builder_->ReadInput(libart_so, libart_so);
- CHECK(libart_so_input != NULL);
-
- std::string host_prebuilt_dir("prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6");
-
- std::string compiler_runtime_lib;
- if (is_host) {
- compiler_runtime_lib += host_prebuilt_dir;
- compiler_runtime_lib += "/lib/gcc/i686-linux/4.6.x-google/libgcc.a";
- } else {
- compiler_runtime_lib += android_root;
- compiler_runtime_lib += "/lib/libcompiler_rt.a";
- }
- // TODO: ownership of compiler_runtime_lib_input?
- mcld::Input* compiler_runtime_lib_input = ir_builder_->ReadInput(compiler_runtime_lib,
- compiler_runtime_lib);
- CHECK(compiler_runtime_lib_input != NULL);
-
- std::string libc_lib;
- if (is_host) {
- libc_lib += host_prebuilt_dir;
- libc_lib += "/sysroot/usr/lib/libc.so.6";
- } else {
- libc_lib += android_root;
- libc_lib += "/lib/libc.so";
- }
- // TODO: ownership of libc_lib_input?
- mcld::Input* libc_lib_input_input = ir_builder_->ReadInput(libc_lib, libc_lib);
- CHECK(libc_lib_input_input != NULL);
-
- std::string libm_lib;
- if (is_host) {
- libm_lib += host_prebuilt_dir;
- libm_lib += "/sysroot/usr/lib/libm.so";
- } else {
- libm_lib += android_root;
- libm_lib += "/lib/libm.so";
- }
- // TODO: ownership of libm_lib_input?
- mcld::Input* libm_lib_input_input = ir_builder_->ReadInput(libm_lib, libm_lib);
- CHECK(libm_lib_input_input != NULL);
-}
-
-void ElfWriterMclinker::FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files) {
- std::string error_msg;
- std::unique_ptr<ElfFile> elf_file(ElfFile::Open(elf_file_, true, false, &error_msg));
- CHECK(elf_file.get() != NULL) << elf_file_->GetPath() << ": " << error_msg;
-
- uint32_t oatdata_address = GetOatDataAddress(elf_file.get());
- DexMethodIterator it(dex_files);
- while (it.HasNext()) {
- const DexFile& dex_file = it.GetDexFile();
- uint32_t method_idx = it.GetMemberIndex();
- InvokeType invoke_type = it.GetInvokeType();
- mirror::ArtMethod* method = NULL;
- if (compiler_driver_->IsImage()) {
- ClassLinker* linker = Runtime::Current()->GetClassLinker();
- // Unchecked as we hold mutator_lock_ on entry.
- ScopedObjectAccessUnchecked soa(Thread::Current());
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->FindDexCache(dex_file)));
- method = linker->ResolveMethod(dex_file, method_idx, dex_cache,
- NullHandle<mirror::ClassLoader>(),
- NullHandle<mirror::ArtMethod>(), invoke_type);
- CHECK(method != NULL);
- }
- const CompiledMethod* compiled_method =
- compiler_driver_->GetCompiledMethod(MethodReference(&dex_file, method_idx));
- if (compiled_method != NULL) {
- uint32_t offset = FixupCompiledCodeOffset(*elf_file.get(), oatdata_address, *compiled_method);
- // Don't overwrite static method trampoline
- if (method != NULL &&
- (!method->IsStatic() ||
- method->IsConstructor() ||
- method->GetDeclaringClass()->IsInitialized())) {
- method->SetPortableOatCodeOffset(offset);
- }
- }
- it.Next();
- }
- symbol_to_compiled_code_offset_.clear();
-}
-
-uint32_t ElfWriterMclinker::FixupCompiledCodeOffset(ElfFile& elf_file,
- Elf32_Addr oatdata_address,
- const CompiledCode& compiled_code) {
- const std::string& symbol = compiled_code.GetSymbol();
- SafeMap<const std::string*, uint32_t>::iterator it = symbol_to_compiled_code_offset_.find(&symbol);
- if (it != symbol_to_compiled_code_offset_.end()) {
- return it->second;
- }
-
- Elf32_Addr compiled_code_address = elf_file.FindSymbolAddress(SHT_SYMTAB,
- symbol,
- true);
- CHECK_NE(0U, compiled_code_address) << symbol;
- CHECK_LT(oatdata_address, compiled_code_address) << symbol;
- uint32_t compiled_code_offset = compiled_code_address - oatdata_address;
- symbol_to_compiled_code_offset_.Put(&symbol, compiled_code_offset);
-
- const std::vector<uint32_t>& offsets = compiled_code.GetOatdataOffsetsToCompliledCodeOffset();
- for (uint32_t i = 0; i < offsets.size(); i++) {
- uint32_t oatdata_offset = oatdata_address + offsets[i];
- uint32_t* addr = reinterpret_cast<uint32_t*>(elf_file.Begin() + oatdata_offset);
- *addr = compiled_code_offset;
- }
- return compiled_code_offset;
-}
-
-} // namespace art
diff --git a/compiler/elf_writer_mclinker.h b/compiler/elf_writer_mclinker.h
deleted file mode 100644
index 489fefb..0000000
--- a/compiler/elf_writer_mclinker.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_ELF_WRITER_MCLINKER_H_
-#define ART_COMPILER_ELF_WRITER_MCLINKER_H_
-
-#include <memory>
-
-#include "elf_writer.h"
-#include "safe_map.h"
-
-namespace mcld {
-class IRBuilder;
-class Input;
-class LDSection;
-class LDSymbol;
-class Linker;
-class LinkerConfig;
-class LinkerScript;
-class Module;
-} // namespace mcld
-
-namespace art {
-
-class CompiledCode;
-
-class ElfWriterMclinker FINAL : public ElfWriter {
- public:
- // Write an ELF file. Returns true on success, false on failure.
- static bool Create(File* file,
- OatWriter* oat_writer,
- const std::vector<const DexFile*>& dex_files,
- const std::string& android_root,
- bool is_host,
- const CompilerDriver& driver)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- protected:
- bool Write(OatWriter* oat_writer,
- const std::vector<const DexFile*>& dex_files,
- const std::string& android_root,
- bool is_host)
- OVERRIDE
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- private:
- ElfWriterMclinker(const CompilerDriver& driver, File* elf_file);
- ~ElfWriterMclinker();
-
- void Init();
- mcld::LDSection* AddOatInput(OatWriter* oat_writer, std::vector<uint8_t>* oat_contents);
- void AddMethodInputs(const std::vector<const DexFile*>& dex_files);
- void AddCompiledCodeInput(const CompiledCode& compiled_code);
- void AddRuntimeInputs(const std::string& android_root, bool is_host);
- void FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- uint32_t FixupCompiledCodeOffset(ElfFile& elf_file,
- uint32_t oatdata_address,
- const CompiledCode& compiled_code);
-
- // Setup by Init()
- std::unique_ptr<mcld::LinkerConfig> linker_config_;
- std::unique_ptr<mcld::LinkerScript> linker_script_;
- std::unique_ptr<mcld::Module> module_;
- std::unique_ptr<mcld::IRBuilder> ir_builder_;
- std::unique_ptr<mcld::Linker> linker_;
-
- // Setup by AddOatInput()
- // TODO: ownership of oat_input_?
- mcld::Input* oat_input_;
-
- // Setup by AddCompiledCodeInput
- // set of symbols for already added mcld::Inputs
- SafeMap<const std::string*, const std::string*> added_symbols_;
-
- // Setup by FixupCompiledCodeOffset
- // map of symbol names to oatdata offset
- SafeMap<const std::string*, uint32_t> symbol_to_compiled_code_offset_;
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterMclinker);
-};
-
-} // namespace art
-
-#endif // ART_COMPILER_ELF_WRITER_MCLINKER_H_
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 25cf086..9ec4f28 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -358,8 +358,8 @@
};
// TODO: rewriting it using DexFile::DecodeDebugInfo needs unneeded stuff.
-static void GetLineInfoForJava(const uint8_t* dbgstream, const SrcMap& pc2dex,
- SrcMap* result, uint32_t start_pc = 0) {
+static void GetLineInfoForJava(const uint8_t* dbgstream, const SwapSrcMap& pc2dex,
+ DefaultSrcMap* result, uint32_t start_pc = 0) {
if (dbgstream == nullptr) {
return;
}
@@ -415,7 +415,7 @@
dex_offset += adjopcode / DexFile::DBG_LINE_RANGE;
java_line += DexFile::DBG_LINE_BASE + (adjopcode % DexFile::DBG_LINE_RANGE);
- for (SrcMap::const_iterator found = pc2dex.FindByTo(dex_offset);
+ for (SwapSrcMap::const_iterator found = pc2dex.FindByTo(dex_offset);
found != pc2dex.end() && found->to_ == static_cast<int32_t>(dex_offset);
found++) {
result->push_back({found->from_ + start_pc, static_cast<int32_t>(java_line)});
@@ -615,7 +615,7 @@
LineTableGenerator line_table_generator(LINE_BASE, LINE_RANGE, OPCODE_BASE,
dbg_line, 0, 1);
- SrcMap pc2java_map;
+ DefaultSrcMap pc2java_map;
for (size_t i = 0; i < method_info.size(); ++i) {
const OatWriter::DebugInfo &dbg = method_info[i];
const char* file_name = (dbg.src_file_name_ == nullptr) ? "null" : dbg.src_file_name_;
@@ -669,6 +669,8 @@
template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
typename Elf_Phdr, typename Elf_Shdr>
+// Do not inline to avoid Clang stack frame problems. b/18738594
+NO_INLINE
static void WriteDebugSymbols(const CompilerDriver* compiler_driver,
ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder,
@@ -698,7 +700,7 @@
DCHECK(it->compiled_method_ != nullptr);
// Copy in the FDE, if present
- const std::vector<uint8_t>* fde = it->compiled_method_->GetCFIInfo();
+ const SwapVector<uint8_t>* fde = it->compiled_method_->GetCFIInfo();
if (fde != nullptr) {
// Copy the information into cfi_info and then fix the address in the new copy.
int cur_offset = cfi_info->size();
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
index 2ffbd10..fd3a912 100644
--- a/compiler/elf_writer_test.cc
+++ b/compiler/elf_writer_test.cc
@@ -46,15 +46,12 @@
EXPECT_EQ(expected_value, ef->FindDynamicSymbolAddress(symbol_name)); \
} while (false)
+#if defined(ART_USE_OPTIMIZING_COMPILER)
+TEST_F(ElfWriterTest, DISABLED_dlsym) {
+#else
TEST_F(ElfWriterTest, dlsym) {
- std::string elf_location;
- if (IsHost()) {
- const char* host_dir = getenv("ANDROID_HOST_OUT");
- CHECK(host_dir != NULL);
- elf_location = StringPrintf("%s/framework/core.oat", host_dir);
- } else {
- elf_location = "/data/art-test/core.oat";
- }
+#endif
+ std::string elf_location = GetCoreOatLocation();
std::string elf_filename = GetSystemImageFilename(elf_location.c_str(), kRuntimeISA);
LOG(INFO) << "elf_filename=" << elf_filename;
@@ -63,28 +60,6 @@
void* dl_oatexec = NULL;
void* dl_oatlastword = NULL;
-#if defined(ART_USE_PORTABLE_COMPILER)
- {
- // We only use dlopen for loading with portable. See OatFile::Open.
- void* dl_oat_so = dlopen(elf_filename.c_str(), RTLD_NOW);
- ASSERT_TRUE(dl_oat_so != NULL) << dlerror();
- dl_oatdata = dlsym(dl_oat_so, "oatdata");
- ASSERT_TRUE(dl_oatdata != NULL);
-
- OatHeader* dl_oat_header = reinterpret_cast<OatHeader*>(dl_oatdata);
- ASSERT_TRUE(dl_oat_header->IsValid());
- dl_oatexec = dlsym(dl_oat_so, "oatexec");
- ASSERT_TRUE(dl_oatexec != NULL);
- ASSERT_LT(dl_oatdata, dl_oatexec);
-
- dl_oatlastword = dlsym(dl_oat_so, "oatlastword");
- ASSERT_TRUE(dl_oatlastword != NULL);
- ASSERT_LT(dl_oatexec, dl_oatlastword);
-
- ASSERT_EQ(0, dlclose(dl_oat_so));
- }
-#endif
-
std::unique_ptr<File> file(OS::OpenFileForReading(elf_filename.c_str()));
ASSERT_TRUE(file.get() != NULL);
{
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index dac1ef4..cf97943 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -72,11 +72,6 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
TimingLogger timings("ImageTest::WriteRead", false, false);
TimingLogger::ScopedTiming t("CompileAll", &timings);
- if (kUsePortableCompiler) {
- // TODO: we disable this for portable so the test executes in a reasonable amount of time.
- // We shouldn't need to do this.
- compiler_options_->SetCompilerFilter(CompilerOptions::kInterpretOnly);
- }
for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
dex_file->EnableWrite();
}
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 178ee43..c588e1a 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -77,6 +77,7 @@
Thread::Current()->TransitionFromSuspendedToRunnable();
PruneNonImageClasses(); // Remove junk
ComputeLazyFieldsForImageClasses(); // Add useful information
+ ProcessStrings();
Thread::Current()->TransitionFromRunnableToSuspended(kNative);
}
gc::Heap* heap = Runtime::Current()->GetHeap();
@@ -126,13 +127,6 @@
jni_dlsym_lookup_offset_ = oat_file_->GetOatHeader().GetJniDlsymLookupOffset();
- portable_imt_conflict_trampoline_offset_ =
- oat_file_->GetOatHeader().GetPortableImtConflictTrampolineOffset();
- portable_resolution_trampoline_offset_ =
- oat_file_->GetOatHeader().GetPortableResolutionTrampolineOffset();
- portable_to_interpreter_bridge_offset_ =
- oat_file_->GetOatHeader().GetPortableToInterpreterBridgeOffset();
-
quick_generic_jni_trampoline_offset_ =
oat_file_->GetOatHeader().GetQuickGenericJniTrampolineOffset();
quick_imt_conflict_trampoline_offset_ =
@@ -279,13 +273,7 @@
void ImageWriter::AssignImageBinSlot(mirror::Object* object) {
DCHECK(object != nullptr);
- size_t object_size;
- if (object->IsArtMethod()) {
- // Methods are sized based on the target pointer size.
- object_size = mirror::ArtMethod::InstanceSize(target_ptr_size_);
- } else {
- object_size = object->SizeOf();
- }
+ size_t object_size = object->SizeOf();
// The magic happens here. We segregate objects into different bins based
// on how likely they are to get dirty at runtime.
@@ -479,116 +467,93 @@
};
// Compare strings based on length, used for sorting strings by length / reverse length.
-class StringLengthComparator {
+class LexicographicalStringComparator {
public:
- explicit StringLengthComparator(Handle<mirror::ObjectArray<mirror::String>> strings)
- : strings_(strings) {
+ bool operator()(const mirror::HeapReference<mirror::String>& lhs,
+ const mirror::HeapReference<mirror::String>& rhs) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::String* lhs_s = lhs.AsMirrorPtr();
+ mirror::String* rhs_s = rhs.AsMirrorPtr();
+ uint16_t* lhs_begin = lhs_s->GetCharArray()->GetData() + lhs_s->GetOffset();
+ uint16_t* rhs_begin = rhs_s->GetCharArray()->GetData() + rhs_s->GetOffset();
+ return std::lexicographical_compare(lhs_begin, lhs_begin + lhs_s->GetLength(),
+ rhs_begin, rhs_begin + rhs_s->GetLength());
}
- bool operator()(size_t a, size_t b) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return strings_->GetWithoutChecks(a)->GetLength() < strings_->GetWithoutChecks(b)->GetLength();
- }
-
- private:
- Handle<mirror::ObjectArray<mirror::String>> strings_;
};
-// Normal string < comparison through the chars_ array.
-class SubstringComparator {
- public:
- explicit SubstringComparator(const std::vector<uint16_t>* const chars) : chars_(chars) {
+static bool IsPrefix(mirror::String* pref, mirror::String* full)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (pref->GetLength() > full->GetLength()) {
+ return false;
}
- bool operator()(const std::pair<size_t, size_t>& a, const std::pair<size_t, size_t>& b) {
- return std::lexicographical_compare(chars_->begin() + a.first,
- chars_->begin() + a.first + a.second,
- chars_->begin() + b.first,
- chars_->begin() + b.first + b.second);
- }
-
- private:
- const std::vector<uint16_t>* const chars_;
-};
+ uint16_t* pref_begin = pref->GetCharArray()->GetData() + pref->GetOffset();
+ uint16_t* full_begin = full->GetCharArray()->GetData() + full->GetOffset();
+ return std::equal(pref_begin, pref_begin + pref->GetLength(), full_begin);
+}
void ImageWriter::ProcessStrings() {
size_t total_strings = 0;
gc::Heap* heap = Runtime::Current()->GetHeap();
ClassLinker* cl = Runtime::Current()->GetClassLinker();
- {
- ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
- heap->VisitObjects(CountStringsCallback, &total_strings); // Count the strings.
- }
+ // Count the strings.
+ heap->VisitObjects(CountStringsCallback, &total_strings);
Thread* self = Thread::Current();
StackHandleScope<1> hs(self);
auto strings = hs.NewHandle(cl->AllocStringArray(self, total_strings));
StringCollector string_collector(strings, 0U);
- {
- ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
- // Read strings into the array.
- heap->VisitObjects(StringCollector::Callback, &string_collector);
- }
+ // Read strings into the array.
+ heap->VisitObjects(StringCollector::Callback, &string_collector);
// Some strings could have gotten freed if AllocStringArray caused a GC.
CHECK_LE(string_collector.GetIndex(), total_strings);
total_strings = string_collector.GetIndex();
- size_t total_length = 0;
- std::vector<size_t> reverse_sorted_strings;
- for (size_t i = 0; i < total_strings; ++i) {
- mirror::String* s = strings->GetWithoutChecks(i);
- // Look up the string in the array.
- total_length += s->GetLength();
- reverse_sorted_strings.push_back(i);
- }
- // Sort by reverse length.
- StringLengthComparator comparator(strings);
- std::sort(reverse_sorted_strings.rbegin(), reverse_sorted_strings.rend(), comparator);
- // Deduplicate prefixes and add strings to the char array.
- std::vector<uint16_t> combined_chars(total_length, 0U);
- size_t num_chars = 0;
+ auto* strings_begin = reinterpret_cast<mirror::HeapReference<mirror::String>*>(
+ strings->GetRawData(sizeof(mirror::HeapReference<mirror::String>), 0));
+ std::sort(strings_begin, strings_begin + total_strings, LexicographicalStringComparator());
// Characters of strings which are non equal prefix of another string (not the same string).
// We don't count the savings from equal strings since these would get interned later anyways.
size_t prefix_saved_chars = 0;
- std::set<std::pair<size_t, size_t>, SubstringComparator> existing_strings((
- SubstringComparator(&combined_chars)));
- for (size_t i = 0; i < total_strings; ++i) {
- mirror::String* s = strings->GetWithoutChecks(reverse_sorted_strings[i]);
- // Add the string to the end of the char array.
+ // Count characters needed for the strings.
+ size_t num_chars = 0u;
+ mirror::String* prev_s = nullptr;
+ for (size_t idx = 0; idx != total_strings; ++idx) {
+ mirror::String* s = strings->GetWithoutChecks(idx);
size_t length = s->GetLength();
- for (size_t j = 0; j < length; ++j) {
- combined_chars[num_chars++] = s->CharAt(j);
- }
- // Try to see if the string exists as a prefix of an existing string.
- size_t new_offset = 0;
- std::pair<size_t, size_t> new_string(num_chars - length, length);
- auto it = existing_strings.lower_bound(new_string);
- bool is_prefix = false;
- if (it != existing_strings.end()) {
- CHECK_LE(length, it->second);
- is_prefix = std::equal(combined_chars.begin() + it->first,
- combined_chars.begin() + it->first + it->second,
- combined_chars.begin() + new_string.first);
- }
- if (is_prefix) {
- // Shares a prefix, set the offset to where the new offset will be.
- new_offset = it->first;
- // Remove the added chars.
- num_chars -= length;
- if (it->second != length) {
- prefix_saved_chars += length;
+ num_chars += length;
+ if (prev_s != nullptr && IsPrefix(prev_s, s)) {
+ size_t prev_length = prev_s->GetLength();
+ num_chars -= prev_length;
+ if (prev_length != length) {
+ prefix_saved_chars += prev_length;
}
- } else {
- new_offset = new_string.first;
- existing_strings.insert(new_string);
}
- s->SetOffset(new_offset);
+ prev_s = s;
}
- // Allocate and update the char arrays.
- auto* array = mirror::CharArray::Alloc(self, num_chars);
- for (size_t i = 0; i < num_chars; ++i) {
- array->SetWithoutChecks<false>(i, combined_chars[i]);
+ // Create character array, copy characters and point the strings there.
+ mirror::CharArray* array = mirror::CharArray::Alloc(self, num_chars);
+ string_data_array_ = array;
+ uint16_t* array_data = array->GetData();
+ size_t pos = 0u;
+ prev_s = nullptr;
+ for (size_t idx = 0; idx != total_strings; ++idx) {
+ mirror::String* s = strings->GetWithoutChecks(idx);
+ uint16_t* s_data = s->GetCharArray()->GetData() + s->GetOffset();
+ int32_t s_length = s->GetLength();
+ int32_t prefix_length = 0u;
+ if (idx != 0u && IsPrefix(prev_s, s)) {
+ prefix_length = prev_s->GetLength();
+ }
+ memcpy(array_data + pos, s_data + prefix_length, (s_length - prefix_length) * sizeof(*s_data));
+ s->SetOffset(pos - prefix_length);
+ s->SetArray(array);
+ pos += s_length - prefix_length;
+ prev_s = s;
}
- for (size_t i = 0; i < total_strings; ++i) {
- strings->GetWithoutChecks(i)->SetArray(array);
+ CHECK_EQ(pos, num_chars);
+
+ if (kIsDebugBuild || VLOG_IS_ON(compiler)) {
+ LOG(INFO) << "Total # image strings=" << total_strings << " combined length="
+ << num_chars << " prefix saved chars=" << prefix_saved_chars;
}
- LOG(INFO) << "Total # image strings=" << total_strings << " combined length="
- << total_length << " prefix saved chars=" << prefix_saved_chars;
ComputeEagerResolvedStrings();
}
@@ -622,7 +587,6 @@
}
void ImageWriter::ComputeEagerResolvedStrings() {
- ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
Runtime::Current()->GetHeap()->VisitObjects(ComputeEagerResolvedStringsCallback, this);
}
@@ -695,7 +659,6 @@
void ImageWriter::CheckNonImageClassesRemoved() {
if (compiler_driver_.GetImageClasses() != nullptr) {
gc::Heap* heap = Runtime::Current()->GetHeap();
- ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
heap->VisitObjects(CheckNonImageClassesRemovedCallback, this);
}
}
@@ -896,17 +859,14 @@
// know where image_roots is going to end up
image_end_ += RoundUp(sizeof(ImageHeader), kObjectAlignment); // 64-bit-alignment
- {
- WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
- // TODO: Image spaces only?
- DCHECK_LT(image_end_, image_->Size());
- image_objects_offset_begin_ = image_end_;
- // Clear any pre-existing monitors which may have been in the monitor words, assign bin slots.
- heap->VisitObjects(WalkFieldsCallback, this);
- // Transform each object's bin slot into an offset which will be used to do the final copy.
- heap->VisitObjects(UnbinObjectsIntoOffsetCallback, this);
- DCHECK(saved_hashes_map_.empty()); // All binslot hashes should've been put into vector by now.
- }
+ // TODO: Image spaces only?
+ DCHECK_LT(image_end_, image_->Size());
+ image_objects_offset_begin_ = image_end_;
+ // Clear any pre-existing monitors which may have been in the monitor words, assign bin slots.
+ heap->VisitObjects(WalkFieldsCallback, this);
+ // Transform each object's bin slot into an offset which will be used to do the final copy.
+ heap->VisitObjects(UnbinObjectsIntoOffsetCallback, this);
+ DCHECK(saved_hashes_map_.empty()); // All binslot hashes should've been put into vector by now.
DCHECK_GT(image_end_, GetBinSizeSum());
@@ -942,12 +902,10 @@
}
void ImageWriter::CopyAndFixupObjects() {
- ScopedAssertNoThreadSuspension ants(Thread::Current(), "ImageWriter");
gc::Heap* heap = Runtime::Current()->GetHeap();
// TODO: heap validation can't handle this fix up pass
heap->DisableObjectValidation();
// TODO: Image spaces only?
- WriterMutexLock mu(ants.Self(), *Locks::heap_bitmap_lock_);
heap->VisitObjects(CopyAndFixupObjectsCallback, this);
// Fix up the object previously had hash codes.
for (const std::pair<mirror::Object*, uint32_t>& hash_pair : saved_hashes_) {
@@ -968,7 +926,7 @@
if (obj->IsArtMethod()) {
// Size without pointer fields since we don't want to overrun the buffer if target art method
// is 32 bits but source is 64 bits.
- n = mirror::ArtMethod::SizeWithoutPointerFields(sizeof(void*));
+ n = mirror::ArtMethod::SizeWithoutPointerFields(image_writer->target_ptr_size_);
} else {
n = obj->SizeOf();
}
@@ -1053,10 +1011,6 @@
}
if (orig->IsArtMethod<kVerifyNone>()) {
FixupMethod(orig->AsArtMethod<kVerifyNone>(), down_cast<ArtMethod*>(copy));
- } else if (orig->IsClass() && orig->AsClass()->IsArtMethodClass()) {
- // Set the right size for the target.
- size_t size = mirror::ArtMethod::InstanceSize(target_ptr_size_);
- down_cast<mirror::Class*>(copy)->SetObjectSizeWithoutChecks(size);
}
}
@@ -1068,7 +1022,9 @@
// trampoline.
// Quick entrypoint:
- const uint8_t* quick_code = GetOatAddress(method->GetQuickOatCodeOffset());
+ uint32_t quick_oat_code_offset = PointerToLowMemUInt32(
+ method->GetEntryPointFromQuickCompiledCodePtrSize(target_ptr_size_));
+ const uint8_t* quick_code = GetOatAddress(quick_oat_code_offset);
*quick_is_interpreted = false;
if (quick_code != nullptr &&
(!method->IsStatic() || method->IsConstructor() || method->GetDeclaringClass()->IsInitialized())) {
@@ -1119,25 +1075,20 @@
// locations.
// Copy all of the fields from the runtime methods to the target methods first since we did a
// bytewise copy earlier.
- copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
- orig->GetEntryPointFromPortableCompiledCode(), target_ptr_size_);
- copy->SetEntryPointFromInterpreterPtrSize<kVerifyNone>(orig->GetEntryPointFromInterpreter(),
- target_ptr_size_);
- copy->SetEntryPointFromJniPtrSize<kVerifyNone>(orig->GetEntryPointFromJni(), target_ptr_size_);
+ copy->SetEntryPointFromInterpreterPtrSize<kVerifyNone>(
+ orig->GetEntryPointFromInterpreterPtrSize(target_ptr_size_), target_ptr_size_);
+ copy->SetEntryPointFromJniPtrSize<kVerifyNone>(
+ orig->GetEntryPointFromJniPtrSize(target_ptr_size_), target_ptr_size_);
copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(
- orig->GetEntryPointFromQuickCompiledCode(), target_ptr_size_);
+ orig->GetEntryPointFromQuickCompiledCodePtrSize(target_ptr_size_), target_ptr_size_);
// The resolution method has a special trampoline to call.
Runtime* runtime = Runtime::Current();
if (UNLIKELY(orig == runtime->GetResolutionMethod())) {
- copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
- GetOatAddress(portable_resolution_trampoline_offset_), target_ptr_size_);
copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(
GetOatAddress(quick_resolution_trampoline_offset_), target_ptr_size_);
} else if (UNLIKELY(orig == runtime->GetImtConflictMethod() ||
orig == runtime->GetImtUnimplementedMethod())) {
- copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
- GetOatAddress(portable_imt_conflict_trampoline_offset_), target_ptr_size_);
copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(
GetOatAddress(quick_imt_conflict_trampoline_offset_), target_ptr_size_);
} else {
@@ -1145,8 +1096,6 @@
// resolution trampoline. Abstract methods never have code and so we need to make sure their
// use results in an AbstractMethodError. We use the interpreter to achieve this.
if (UNLIKELY(orig->IsAbstract())) {
- copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
- GetOatAddress(portable_to_interpreter_bridge_offset_), target_ptr_size_);
copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(
GetOatAddress(quick_to_interpreter_bridge_offset_), target_ptr_size_);
copy->SetEntryPointFromInterpreterPtrSize<kVerifyNone>(
@@ -1157,29 +1106,6 @@
const uint8_t* quick_code = GetQuickCode(orig, &quick_is_interpreted);
copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(quick_code, target_ptr_size_);
- // Portable entrypoint:
- const uint8_t* portable_code = GetOatAddress(orig->GetPortableOatCodeOffset());
- bool portable_is_interpreted = false;
- if (portable_code != nullptr &&
- (!orig->IsStatic() || orig->IsConstructor() || orig->GetDeclaringClass()->IsInitialized())) {
- // We have code for a non-static or initialized method, just use the code.
- } else if (portable_code == nullptr && orig->IsNative() &&
- (!orig->IsStatic() || orig->GetDeclaringClass()->IsInitialized())) {
- // Non-static or initialized native method missing compiled code, use generic JNI version.
- // TODO: generic JNI support for LLVM.
- portable_code = GetOatAddress(portable_resolution_trampoline_offset_);
- } else if (portable_code == nullptr && !orig->IsNative()) {
- // We don't have code at all for a non-native method, use the interpreter.
- portable_code = GetOatAddress(portable_to_interpreter_bridge_offset_);
- portable_is_interpreted = true;
- } else {
- CHECK(!orig->GetDeclaringClass()->IsInitialized());
- // We have code for a static method, but need to go through the resolution stub for class
- // initialization.
- portable_code = GetOatAddress(portable_resolution_trampoline_offset_);
- }
- copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
- portable_code, target_ptr_size_);
// JNI entrypoint:
if (orig->IsNative()) {
// The native method's pointer is set to a stub to lookup via dlsym.
@@ -1190,7 +1116,7 @@
// Interpreter entrypoint:
// Set the interpreter entrypoint depending on whether there is compiled code or not.
- uint32_t interpreter_code = (quick_is_interpreted && portable_is_interpreted)
+ uint32_t interpreter_code = (quick_is_interpreted)
? interpreter_to_interpreter_bridge_offset_
: interpreter_to_compiled_code_bridge_offset_;
EntryPointFromInterpreter* interpreter_entrypoint =
@@ -1255,4 +1181,13 @@
return lockword_ & ~kBinMask;
}
+void ImageWriter::FreeStringDataArray() {
+ if (string_data_array_ != nullptr) {
+ gc::space::LargeObjectSpace* los = Runtime::Current()->GetHeap()->GetLargeObjectsSpace();
+ if (los != nullptr) {
+ los->Free(Thread::Current(), reinterpret_cast<mirror::Object*>(string_data_array_));
+ }
+ }
+}
+
} // namespace art
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 8c84b68..53f5ce4 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_IMAGE_WRITER_H_
#include <stdint.h>
+#include <valgrind.h>
#include <cstddef>
#include <memory>
@@ -47,8 +48,7 @@
image_end_(0), image_objects_offset_begin_(0), image_roots_address_(0), oat_file_(nullptr),
oat_data_begin_(nullptr), interpreter_to_interpreter_bridge_offset_(0),
interpreter_to_compiled_code_bridge_offset_(0), jni_dlsym_lookup_offset_(0),
- portable_imt_conflict_trampoline_offset_(0), portable_resolution_trampoline_offset_(0),
- portable_to_interpreter_bridge_offset_(0), quick_generic_jni_trampoline_offset_(0),
+ quick_generic_jni_trampoline_offset_(0),
quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0),
quick_to_interpreter_bridge_offset_(0), compile_pic_(compile_pic),
target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())),
@@ -56,7 +56,15 @@
CHECK_NE(image_begin, 0U);
}
- ~ImageWriter() {}
+ ~ImageWriter() {
+ // For interned strings a large array is allocated to hold all the character data and avoid
+ // overhead. However, no GC is run anymore at this point. As the array is likely large, it
+ // will be allocated in the large object space, where valgrind can track every single
+ // allocation. Not explicitly freeing that array will be recognized as a leak.
+ if (RUNNING_ON_VALGRIND != 0) {
+ FreeStringDataArray();
+ }
+ }
bool PrepareImageAddressSpace();
@@ -169,12 +177,9 @@
}
const uint8_t* GetOatAddress(uint32_t offset) const {
-#if !defined(ART_USE_PORTABLE_COMPILER)
// With Quick, code is within the OatFile, as there are all in one
- // .o ELF object. However with Portable, the code is always in
- // different .o ELF objects.
+ // .o ELF object.
DCHECK_LT(offset, oat_file_->Size());
-#endif
if (offset == 0u) {
return nullptr;
}
@@ -254,6 +259,9 @@
// Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins.
size_t GetBinSizeSum(Bin up_to = kBinSize) const;
+ // Release the string_data_array_.
+ void FreeStringDataArray();
+
const CompilerDriver& compiler_driver_;
// Beginning target image address for the output image.
@@ -290,9 +298,6 @@
uint32_t interpreter_to_interpreter_bridge_offset_;
uint32_t interpreter_to_compiled_code_bridge_offset_;
uint32_t jni_dlsym_lookup_offset_;
- uint32_t portable_imt_conflict_trampoline_offset_;
- uint32_t portable_resolution_trampoline_offset_;
- uint32_t portable_to_interpreter_bridge_offset_;
uint32_t quick_generic_jni_trampoline_offset_;
uint32_t quick_imt_conflict_trampoline_offset_;
uint32_t quick_resolution_trampoline_offset_;
@@ -306,6 +311,8 @@
size_t bin_slot_sizes_[kBinSize]; // Number of bytes in a bin
size_t bin_slot_count_[kBinSize]; // Number of objects in a bin
+ void* string_data_array_; // The backing for the interned strings.
+
friend class FixupVisitor;
friend class FixupClassVisitor;
DISALLOW_COPY_AND_ASSIGN(ImageWriter);
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index 2755442..f513ea8 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -80,8 +80,6 @@
CompileMethod(method);
ASSERT_TRUE(method->GetEntryPointFromQuickCompiledCode() != nullptr)
<< method_name << " " << method_sig;
- ASSERT_TRUE(method->GetEntryPointFromPortableCompiledCode() != nullptr)
- << method_name << " " << method_sig;
}
}
}
@@ -204,7 +202,6 @@
}
void JniCompilerTest::CompileAndRunNoArgMethodImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClassNatives_foo));
EXPECT_EQ(0, gJava_MyClassNatives_foo_calls);
@@ -219,7 +216,6 @@
JNI_TEST(CompileAndRunNoArgMethod)
void JniCompilerTest::CompileAndRunIntMethodThroughStubImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(false, "bar", "(I)I", nullptr);
// calling through stub will link with &Java_MyClassNatives_bar
@@ -234,7 +230,6 @@
JNI_TEST(CompileAndRunIntMethodThroughStub)
void JniCompilerTest::CompileAndRunStaticIntMethodThroughStubImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(true, "sbar", "(I)I", nullptr);
// calling through stub will link with &Java_MyClassNatives_sbar
@@ -262,7 +257,6 @@
}
void JniCompilerTest::CompileAndRunIntMethodImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(false, "fooI", "(I)I",
reinterpret_cast<void*>(&Java_MyClassNatives_fooI));
@@ -293,7 +287,6 @@
}
void JniCompilerTest::CompileAndRunIntIntMethodImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(false, "fooII", "(II)I",
reinterpret_cast<void*>(&Java_MyClassNatives_fooII));
@@ -325,7 +318,6 @@
}
void JniCompilerTest::CompileAndRunLongLongMethodImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(false, "fooJJ", "(JJ)J",
reinterpret_cast<void*>(&Java_MyClassNatives_fooJJ));
@@ -358,7 +350,6 @@
}
void JniCompilerTest::CompileAndRunDoubleDoubleMethodImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(false, "fooDD", "(DD)D",
reinterpret_cast<void*>(&Java_MyClassNatives_fooDD));
@@ -390,7 +381,6 @@
}
void JniCompilerTest::CompileAndRun_fooJJ_synchronizedImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(false, "fooJJ_synchronized", "(JJ)J",
reinterpret_cast<void*>(&Java_MyClassNatives_fooJJ_synchronized));
@@ -430,7 +420,6 @@
}
void JniCompilerTest::CompileAndRunIntObjectObjectMethodImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(false, "fooIOO",
"(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
reinterpret_cast<void*>(&Java_MyClassNatives_fooIOO));
@@ -479,7 +468,6 @@
}
void JniCompilerTest::CompileAndRunStaticIntIntMethodImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(true, "fooSII", "(II)I",
reinterpret_cast<void*>(&Java_MyClassNatives_fooSII));
@@ -507,7 +495,6 @@
}
void JniCompilerTest::CompileAndRunStaticDoubleDoubleMethodImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(true, "fooSDD", "(DD)D",
reinterpret_cast<void*>(&Java_MyClassNatives_fooSDD));
@@ -535,7 +522,6 @@
}
void JniCompilerTest::RunStaticLogDoubleMethodImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(true, "logD", "(D)D", reinterpret_cast<void*>(&Java_MyClassNatives_logD));
jdouble result = env_->CallStaticDoubleMethod(jklass_, jmethod_, 2.0);
@@ -549,7 +535,6 @@
}
void JniCompilerTest::RunStaticLogFloatMethodImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(true, "logF", "(F)F", reinterpret_cast<void*>(&Java_MyClassNatives_logF));
jfloat result = env_->CallStaticFloatMethod(jklass_, jmethod_, 2.0);
@@ -571,7 +556,6 @@
}
void JniCompilerTest::RunStaticReturnTrueImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(true, "returnTrue", "()Z", reinterpret_cast<void*>(&Java_MyClassNatives_returnTrue));
jboolean result = env_->CallStaticBooleanMethod(jklass_, jmethod_);
@@ -581,7 +565,6 @@
JNI_TEST(RunStaticReturnTrue)
void JniCompilerTest::RunStaticReturnFalseImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(true, "returnFalse", "()Z",
reinterpret_cast<void*>(&Java_MyClassNatives_returnFalse));
@@ -592,7 +575,6 @@
JNI_TEST(RunStaticReturnFalse)
void JniCompilerTest::RunGenericStaticReturnIntImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(true, "returnInt", "()I", reinterpret_cast<void*>(&Java_MyClassNatives_returnInt));
jint result = env_->CallStaticIntMethod(jklass_, jmethod_);
@@ -626,7 +608,6 @@
void JniCompilerTest::CompileAndRunStaticIntObjectObjectMethodImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(true, "fooSIOO",
"(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
reinterpret_cast<void*>(&Java_MyClassNatives_fooSIOO));
@@ -684,7 +665,6 @@
}
void JniCompilerTest::CompileAndRunStaticSynchronizedIntObjectObjectMethodImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(true, "fooSSIOO",
"(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
reinterpret_cast<void*>(&Java_MyClassNatives_fooSSIOO));
@@ -725,7 +705,6 @@
}
void JniCompilerTest::ExceptionHandlingImpl() {
- TEST_DISABLED_FOR_PORTABLE();
{
ASSERT_FALSE(runtime_->IsStarted());
ScopedObjectAccess soa(Thread::Current());
@@ -810,7 +789,6 @@
}
void JniCompilerTest::NativeStackTraceElementImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(false, "fooI", "(I)I",
reinterpret_cast<void*>(&Java_MyClassNatives_nativeUpCall));
jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 10);
@@ -824,7 +802,6 @@
}
void JniCompilerTest::ReturnGlobalRefImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(false, "fooO", "(Ljava/lang/Object;)Ljava/lang/Object;",
reinterpret_cast<void*>(&Java_MyClassNatives_fooO));
jobject result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, jobj_);
@@ -844,7 +821,6 @@
}
void JniCompilerTest::LocalReferenceTableClearingTestImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(false, "fooI", "(I)I", reinterpret_cast<void*>(&local_ref_test));
// 1000 invocations of a method that adds 10 local references
for (int i = 0; i < 1000; i++) {
@@ -865,7 +841,6 @@
}
void JniCompilerTest::JavaLangSystemArrayCopyImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(true, "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V",
reinterpret_cast<void*>(&my_arraycopy));
env_->CallStaticVoidMethod(jklass_, jmethod_, jobj_, 1234, jklass_, 5678, 9876);
@@ -883,7 +858,6 @@
}
void JniCompilerTest::CompareAndSwapIntImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(false, "compareAndSwapInt", "(Ljava/lang/Object;JII)Z",
reinterpret_cast<void*>(&my_casi));
jboolean result = env_->CallBooleanMethod(jobj_, jmethod_, jobj_, INT64_C(0x12345678ABCDEF88),
@@ -903,7 +877,6 @@
}
void JniCompilerTest::GetTextImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(true, "getText", "(JLjava/lang/Object;JLjava/lang/Object;)I",
reinterpret_cast<void*>(&my_gettext));
jint result = env_->CallStaticIntMethod(jklass_, jmethod_, 0x12345678ABCDEF88ll, jobj_,
@@ -931,7 +904,6 @@
}
void JniCompilerTest::GetSinkPropertiesNativeImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(false, "getSinkPropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;",
reinterpret_cast<void*>(&Java_MyClassNatives_GetSinkProperties));
@@ -957,12 +929,10 @@
}
void JniCompilerTest::UpcallReturnTypeChecking_InstanceImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(false, "instanceMethodThatShouldReturnClass", "()Ljava/lang/Class;",
reinterpret_cast<void*>(&Java_MyClassNatives_instanceMethodThatShouldReturnClass));
CheckJniAbortCatcher check_jni_abort_catcher;
- // TODO: check type of returns with portable JNI compiler.
// This native method is bad, and tries to return a jstring as a jclass.
env_->CallObjectMethod(jobj_, jmethod_);
check_jni_abort_catcher.Check("attempt to return an instance of java.lang.String from java.lang.Class MyClassNatives.instanceMethodThatShouldReturnClass()");
@@ -977,12 +947,10 @@
JNI_TEST(UpcallReturnTypeChecking_Instance)
void JniCompilerTest::UpcallReturnTypeChecking_StaticImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(true, "staticMethodThatShouldReturnClass", "()Ljava/lang/Class;",
reinterpret_cast<void*>(&Java_MyClassNatives_staticMethodThatShouldReturnClass));
CheckJniAbortCatcher check_jni_abort_catcher;
- // TODO: check type of returns with portable JNI compiler.
// This native method is bad, and tries to return a jstring as a jclass.
env_->CallStaticObjectMethod(jklass_, jmethod_);
check_jni_abort_catcher.Check("attempt to return an instance of java.lang.String from java.lang.Class MyClassNatives.staticMethodThatShouldReturnClass()");
@@ -1005,7 +973,9 @@
}
void JniCompilerTest::UpcallArgumentTypeChecking_InstanceImpl() {
- TEST_DISABLED_FOR_PORTABLE();
+ // This will lead to error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
SetUpForTest(false, "instanceMethodThatShouldTakeClass", "(ILjava/lang/Class;)V",
reinterpret_cast<void*>(&Java_MyClassNatives_instanceMethodThatShouldTakeClass));
@@ -1018,7 +988,9 @@
JNI_TEST(UpcallArgumentTypeChecking_Instance)
void JniCompilerTest::UpcallArgumentTypeChecking_StaticImpl() {
- TEST_DISABLED_FOR_PORTABLE();
+ // This will lead to error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
SetUpForTest(true, "staticMethodThatShouldTakeClass", "(ILjava/lang/Class;)V",
reinterpret_cast<void*>(&Java_MyClassNatives_staticMethodThatShouldTakeClass));
@@ -1041,7 +1013,6 @@
}
void JniCompilerTest::CompileAndRunFloatFloatMethodImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(false, "checkFloats", "(FF)F",
reinterpret_cast<void*>(&Java_MyClassNatives_checkFloats));
@@ -1071,7 +1042,6 @@
}
void JniCompilerTest::CheckParameterAlignImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(false, "checkParameterAlign", "(IJ)V",
reinterpret_cast<void*>(&Java_MyClassNatives_checkParameterAlign));
@@ -1486,7 +1456,6 @@
"Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V";
void JniCompilerTest::MaxParamNumberImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(false, "maxParamNumber", longSig,
reinterpret_cast<void*>(&Java_MyClassNatives_maxParamNumber));
@@ -1512,7 +1481,9 @@
JNI_TEST(MaxParamNumber)
void JniCompilerTest::WithoutImplementationImpl() {
- TEST_DISABLED_FOR_PORTABLE();
+ // This will lead to error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
SetUpForTest(false, "withoutImplementation", "()V", nullptr);
env_->CallVoidMethod(jobj_, jmethod_);
@@ -1562,7 +1533,6 @@
}
void JniCompilerTest::StackArgsIntsFirstImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(true, "stackArgsIntsFirst", "(IIIIIIIIIIFFFFFFFFFF)V",
reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsIntsFirst));
@@ -1633,7 +1603,6 @@
}
void JniCompilerTest::StackArgsFloatsFirstImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(true, "stackArgsFloatsFirst", "(FFFFFFFFFFIIIIIIIIII)V",
reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsFloatsFirst));
@@ -1703,7 +1672,6 @@
}
void JniCompilerTest::StackArgsMixedImpl() {
- TEST_DISABLED_FOR_PORTABLE();
SetUpForTest(true, "stackArgsMixed", "(IFIFIFIFIFIFIFIFIFIF)V",
reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsMixed));
diff --git a/compiler/jni/portable/jni_compiler.cc b/compiler/jni/portable/jni_compiler.cc
deleted file mode 100644
index ff37d85..0000000
--- a/compiler/jni/portable/jni_compiler.cc
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "jni_compiler.h"
-
-#include "base/logging.h"
-#include "class_linker.h"
-#include "compiled_method.h"
-#include "dex_file-inl.h"
-#include "driver/compiler_driver.h"
-#include "driver/dex_compilation_unit.h"
-#include "llvm/compiler_llvm.h"
-#include "llvm/ir_builder.h"
-#include "llvm/llvm_compilation_unit.h"
-#include "llvm/runtime_support_llvm_func.h"
-#include "llvm/utils_llvm.h"
-#include "mirror/art_method.h"
-#include "runtime.h"
-#include "stack.h"
-#include "thread.h"
-
-#include <llvm/ADT/SmallVector.h>
-#include <llvm/IR/BasicBlock.h>
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/Function.h>
-#include <llvm/IR/Type.h>
-
-namespace art {
-namespace llvm {
-
-using ::art::llvm::runtime_support::JniMethodEnd;
-using ::art::llvm::runtime_support::JniMethodEndSynchronized;
-using ::art::llvm::runtime_support::JniMethodEndWithReference;
-using ::art::llvm::runtime_support::JniMethodEndWithReferenceSynchronized;
-using ::art::llvm::runtime_support::JniMethodStart;
-using ::art::llvm::runtime_support::JniMethodStartSynchronized;
-using ::art::llvm::runtime_support::RuntimeId;
-
-JniCompiler::JniCompiler(LlvmCompilationUnit* cunit,
- CompilerDriver* driver,
- const DexCompilationUnit* dex_compilation_unit)
- : cunit_(cunit), driver_(driver), module_(cunit_->GetModule()),
- context_(cunit_->GetLLVMContext()), irb_(*cunit_->GetIRBuilder()),
- dex_compilation_unit_(dex_compilation_unit),
- func_(NULL), elf_func_idx_(0) {
- // Check: Ensure that JNI compiler will only get "native" method
- CHECK(dex_compilation_unit->IsNative());
-}
-
-CompiledMethod* JniCompiler::Compile() {
- const bool is_static = dex_compilation_unit_->IsStatic();
- const bool is_synchronized = dex_compilation_unit_->IsSynchronized();
- const DexFile* dex_file = dex_compilation_unit_->GetDexFile();
- DexFile::MethodId const& method_id =
- dex_file->GetMethodId(dex_compilation_unit_->GetDexMethodIndex());
- char const return_shorty = dex_file->GetMethodShorty(method_id)[0];
- ::llvm::Value* this_object_or_class_object;
-
- uint32_t method_idx = dex_compilation_unit_->GetDexMethodIndex();
- std::string func_name(StringPrintf("jni_%s",
- MangleForJni(PrettyMethod(method_idx, *dex_file)).c_str()));
- CreateFunction(func_name);
-
- // Set argument name
- ::llvm::Function::arg_iterator arg_begin(func_->arg_begin());
- ::llvm::Function::arg_iterator arg_end(func_->arg_end());
- ::llvm::Function::arg_iterator arg_iter(arg_begin);
-
- DCHECK_NE(arg_iter, arg_end);
- arg_iter->setName("method");
- ::llvm::Value* method_object_addr = arg_iter++;
-
- if (!is_static) {
- // Non-static, the second argument is "this object"
- this_object_or_class_object = arg_iter++;
- } else {
- // Load class object
- this_object_or_class_object =
- irb_.LoadFromObjectOffset(method_object_addr,
- mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
- irb_.getJObjectTy(),
- kTBAAConstJObject);
- }
- // Actual argument (ignore method and this object)
- arg_begin = arg_iter;
-
- // Count the number of Object* arguments
- uint32_t handle_scope_size = 1;
- // "this" object pointer for non-static
- // "class" object pointer for static
- for (unsigned i = 0; arg_iter != arg_end; ++i, ++arg_iter) {
-#if !defined(NDEBUG)
- arg_iter->setName(StringPrintf("a%u", i));
-#endif
- if (arg_iter->getType() == irb_.getJObjectTy()) {
- ++handle_scope_size;
- }
- }
-
- // Shadow stack
- ::llvm::StructType* shadow_frame_type = irb_.getShadowFrameTy(handle_scope_size);
- ::llvm::AllocaInst* shadow_frame_ = irb_.CreateAlloca(shadow_frame_type);
-
- // Store the dex pc
- irb_.StoreToObjectOffset(shadow_frame_,
- ShadowFrame::DexPCOffset(),
- irb_.getInt32(DexFile::kDexNoIndex),
- kTBAAShadowFrame);
-
- // Push the shadow frame
- ::llvm::Value* shadow_frame_upcast = irb_.CreateConstGEP2_32(shadow_frame_, 0, 0);
- ::llvm::Value* old_shadow_frame =
- irb_.Runtime().EmitPushShadowFrame(shadow_frame_upcast, method_object_addr, handle_scope_size);
-
- // Get JNIEnv
- ::llvm::Value* jni_env_object_addr =
- irb_.Runtime().EmitLoadFromThreadOffset(Thread::JniEnvOffset().Int32Value(),
- irb_.getJObjectTy(),
- kTBAARuntimeInfo);
-
- // Get callee code_addr
- ::llvm::Value* code_addr =
- irb_.LoadFromObjectOffset(method_object_addr,
- mirror::ArtMethod::NativeMethodOffset().Int32Value(),
- GetFunctionType(dex_compilation_unit_->GetDexMethodIndex(),
- is_static, true)->getPointerTo(),
- kTBAARuntimeInfo);
-
- // Load actual parameters
- std::vector< ::llvm::Value*> args;
-
- // The 1st parameter: JNIEnv*
- args.push_back(jni_env_object_addr);
-
- // Variables for GetElementPtr
- ::llvm::Value* gep_index[] = {
- irb_.getInt32(0), // No displacement for shadow frame pointer
- irb_.getInt32(1), // handle scope
- NULL,
- };
-
- size_t handle_scope_member_index = 0;
-
- // Store the "this object or class object" to handle scope
- gep_index[2] = irb_.getInt32(handle_scope_member_index++);
- ::llvm::Value* handle_scope_field_addr = irb_.CreateBitCast(irb_.CreateGEP(shadow_frame_, gep_index),
- irb_.getJObjectTy()->getPointerTo());
- irb_.CreateStore(this_object_or_class_object, handle_scope_field_addr, kTBAAShadowFrame);
- // Push the "this object or class object" to out args
- this_object_or_class_object = irb_.CreateBitCast(handle_scope_field_addr, irb_.getJObjectTy());
- args.push_back(this_object_or_class_object);
- // Store arguments to handle scope, and push back to args
- for (arg_iter = arg_begin; arg_iter != arg_end; ++arg_iter) {
- if (arg_iter->getType() == irb_.getJObjectTy()) {
- // Store the reference type arguments to handle scope
- gep_index[2] = irb_.getInt32(handle_scope_member_index++);
- ::llvm::Value* handle_scope_field_addr = irb_.CreateBitCast(irb_.CreateGEP(shadow_frame_, gep_index),
- irb_.getJObjectTy()->getPointerTo());
- irb_.CreateStore(arg_iter, handle_scope_field_addr, kTBAAShadowFrame);
- // Note null is placed in the handle scope but the jobject passed to the native code must be null
- // (not a pointer into the handle scope as with regular references).
- ::llvm::Value* equal_null = irb_.CreateICmpEQ(arg_iter, irb_.getJNull());
- ::llvm::Value* arg =
- irb_.CreateSelect(equal_null,
- irb_.getJNull(),
- irb_.CreateBitCast(handle_scope_field_addr, irb_.getJObjectTy()));
- args.push_back(arg);
- } else {
- args.push_back(arg_iter);
- }
- }
-
- ::llvm::Value* saved_local_ref_cookie;
- { // JniMethodStart
- RuntimeId func_id = is_synchronized ? JniMethodStartSynchronized
- : JniMethodStart;
- ::llvm::SmallVector< ::llvm::Value*, 2> args;
- if (is_synchronized) {
- args.push_back(this_object_or_class_object);
- }
- args.push_back(irb_.Runtime().EmitGetCurrentThread());
- saved_local_ref_cookie =
- irb_.CreateCall(irb_.GetRuntime(func_id), args);
- }
-
- // Call!!!
- ::llvm::Value* retval = irb_.CreateCall(code_addr, args);
-
- { // JniMethodEnd
- bool is_return_ref = return_shorty == 'L';
- RuntimeId func_id =
- is_return_ref ? (is_synchronized ? JniMethodEndWithReferenceSynchronized
- : JniMethodEndWithReference)
- : (is_synchronized ? JniMethodEndSynchronized
- : JniMethodEnd);
- ::llvm::SmallVector< ::llvm::Value*, 4> args;
- if (is_return_ref) {
- args.push_back(retval);
- }
- args.push_back(saved_local_ref_cookie);
- if (is_synchronized) {
- args.push_back(this_object_or_class_object);
- }
- args.push_back(irb_.Runtime().EmitGetCurrentThread());
-
- ::llvm::Value* decoded_jobject =
- irb_.CreateCall(irb_.GetRuntime(func_id), args);
-
- // Return decoded jobject if return reference.
- if (is_return_ref) {
- retval = decoded_jobject;
- }
- }
-
- // Pop the shadow frame
- irb_.Runtime().EmitPopShadowFrame(old_shadow_frame);
-
- // Return!
- switch (return_shorty) {
- case 'V':
- irb_.CreateRetVoid();
- break;
- case 'Z':
- case 'C':
- irb_.CreateRet(irb_.CreateZExt(retval, irb_.getInt32Ty()));
- break;
- case 'B':
- case 'S':
- irb_.CreateRet(irb_.CreateSExt(retval, irb_.getInt32Ty()));
- break;
- default:
- irb_.CreateRet(retval);
- break;
- }
-
- // Verify the generated bitcode
- VERIFY_LLVM_FUNCTION(*func_);
-
- cunit_->Materialize();
-
- return new CompiledMethod(*driver_, cunit_->GetInstructionSet(), cunit_->GetElfObject(),
- func_name);
-}
-
-
-void JniCompiler::CreateFunction(const std::string& func_name) {
- CHECK_NE(0U, func_name.size());
-
- const bool is_static = dex_compilation_unit_->IsStatic();
-
- // Get function type
- ::llvm::FunctionType* func_type =
- GetFunctionType(dex_compilation_unit_->GetDexMethodIndex(), is_static, false);
-
- // Create function
- func_ = ::llvm::Function::Create(func_type, ::llvm::Function::InternalLinkage,
- func_name, module_);
-
- // Create basic block
- ::llvm::BasicBlock* basic_block = ::llvm::BasicBlock::Create(*context_, "B0", func_);
-
- // Set insert point
- irb_.SetInsertPoint(basic_block);
-}
-
-
-::llvm::FunctionType* JniCompiler::GetFunctionType(uint32_t method_idx,
- bool is_static, bool is_native_function) {
- // Get method signature
- uint32_t shorty_size;
- const char* shorty = dex_compilation_unit_->GetShorty(&shorty_size);
- CHECK_GE(shorty_size, 1u);
-
- // Get return type
- ::llvm::Type* ret_type = NULL;
- switch (shorty[0]) {
- case 'V': ret_type = irb_.getJVoidTy(); break;
- case 'Z':
- case 'B':
- case 'C':
- case 'S':
- case 'I': ret_type = irb_.getJIntTy(); break;
- case 'F': ret_type = irb_.getJFloatTy(); break;
- case 'J': ret_type = irb_.getJLongTy(); break;
- case 'D': ret_type = irb_.getJDoubleTy(); break;
- case 'L': ret_type = irb_.getJObjectTy(); break;
- default: LOG(FATAL) << "Unreachable: unexpected return type in shorty " << shorty;
- UNREACHABLE();
- }
- // Get argument type
- std::vector< ::llvm::Type*> args_type;
-
- args_type.push_back(irb_.getJObjectTy()); // method object pointer
-
- if (!is_static || is_native_function) {
- // "this" object pointer for non-static
- // "class" object pointer for static naitve
- args_type.push_back(irb_.getJType('L'));
- }
-
- for (uint32_t i = 1; i < shorty_size; ++i) {
- args_type.push_back(irb_.getJType(shorty[i]));
- }
-
- return ::llvm::FunctionType::get(ret_type, args_type, false);
-}
-
-} // namespace llvm
-} // namespace art
diff --git a/compiler/jni/portable/jni_compiler.h b/compiler/jni/portable/jni_compiler.h
deleted file mode 100644
index ffabfe6..0000000
--- a/compiler/jni/portable/jni_compiler.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_JNI_PORTABLE_JNI_COMPILER_H_
-#define ART_COMPILER_JNI_PORTABLE_JNI_COMPILER_H_
-
-#include <stdint.h>
-
-#include <string>
-
-namespace art {
- class ClassLinker;
- class CompiledMethod;
- class CompilerDriver;
- class DexFile;
- class DexCompilationUnit;
- namespace mirror {
- class ArtMethod;
- class ClassLoader;
- class DexCache;
- } // namespace mirror
-} // namespace art
-
-namespace llvm {
- class AllocaInst;
- class Function;
- class FunctionType;
- class BasicBlock;
- class LLVMContext;
- class Module;
- class Type;
- class Value;
-} // namespace llvm
-
-namespace art {
-namespace llvm {
-
-class LlvmCompilationUnit;
-class IRBuilder;
-
-class JniCompiler {
- public:
- JniCompiler(LlvmCompilationUnit* cunit,
- CompilerDriver* driver,
- const DexCompilationUnit* dex_compilation_unit);
-
- CompiledMethod* Compile();
-
- private:
- void CreateFunction(const std::string& symbol);
-
- ::llvm::FunctionType* GetFunctionType(uint32_t method_idx,
- bool is_static, bool is_target_function);
-
- private:
- LlvmCompilationUnit* cunit_;
- CompilerDriver* const driver_;
-
- ::llvm::Module* module_;
- ::llvm::LLVMContext* context_;
- IRBuilder& irb_;
-
- const DexCompilationUnit* const dex_compilation_unit_;
-
- ::llvm::Function* func_;
- uint16_t elf_func_idx_;
-};
-
-
-} // namespace llvm
-} // namespace art
-
-
-#endif // ART_COMPILER_JNI_PORTABLE_JNI_COMPILER_H_
diff --git a/compiler/jni/quick/arm/calling_convention_arm.cc b/compiler/jni/quick/arm/calling_convention_arm.cc
index 769cd4c..669c3bb 100644
--- a/compiler/jni/quick/arm/calling_convention_arm.cc
+++ b/compiler/jni/quick/arm/calling_convention_arm.cc
@@ -16,6 +16,7 @@
#include "base/logging.h"
#include "calling_convention_arm.h"
+#include "handle_scope-inl.h"
#include "utils/arm/managed_register_arm.h"
namespace art {
@@ -167,9 +168,20 @@
} else {
// FIXME: Pointer this returns as both reference and long.
if (IsCurrentParamALong() && !IsCurrentParamAReference()) { // Long.
- if (gpr_index < arraysize(kHFCoreArgumentRegisters)) {
+ if (gpr_index < arraysize(kHFCoreArgumentRegisters) - 1) {
+ // Skip R1, and use R2_R3 if the long is the first parameter.
+ if (gpr_index == 1) {
+ gpr_index++;
+ }
+ }
+
+ // If it spans register and memory, we must use the value in memory.
+ if (gpr_index < arraysize(kHFCoreArgumentRegisters) - 1) {
entry_spills_.push_back(
ArmManagedRegister::FromCoreRegister(kHFCoreArgumentRegisters[gpr_index++]));
+ } else if (gpr_index == arraysize(kHFCoreArgumentRegisters) - 1) {
+ gpr_index++;
+ entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
} else {
entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
}
diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc
index 29763a2..b9c8178 100644
--- a/compiler/jni/quick/arm64/calling_convention_arm64.cc
+++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc
@@ -16,6 +16,7 @@
#include "base/logging.h"
#include "calling_convention_arm64.h"
+#include "handle_scope-inl.h"
#include "utils/arm64/managed_register_arm64.h"
namespace art {
diff --git a/compiler/jni/quick/calling_convention.h b/compiler/jni/quick/calling_convention.h
index 6db0c3b..0c64a36 100644
--- a/compiler/jni/quick/calling_convention.h
+++ b/compiler/jni/quick/calling_convention.h
@@ -141,7 +141,7 @@
if (IsStatic()) {
param++; // 0th argument must skip return value at start of the shorty
} else if (param == 0) {
- return true; // this argument
+ return false; // this argument
}
return shorty_[param] == 'J';
}
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index c3fe75b..ba73828 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -135,6 +135,7 @@
FrameOffset handle_scope_offset = main_jni_conv->CurrentParamHandleScopeEntryOffset();
// Check handle scope offset is within frame
CHECK_LT(handle_scope_offset.Uint32Value(), frame_size);
+ // TODO: Insert the read barrier for this load.
__ LoadRef(main_jni_conv->InterproceduralScratchRegister(),
mr_conv->MethodRegister(), mirror::ArtMethod::DeclaringClassOffset());
__ VerifyObject(main_jni_conv->InterproceduralScratchRegister(), false);
@@ -430,13 +431,18 @@
MemoryRegion code(&managed_code[0], managed_code.size());
__ FinalizeInstructions(code);
jni_asm->FinalizeFrameDescriptionEntry();
- return new CompiledMethod(driver,
- instruction_set,
- managed_code,
- frame_size,
- main_jni_conv->CoreSpillMask(),
- main_jni_conv->FpSpillMask(),
- jni_asm->GetFrameDescriptionEntry());
+ std::vector<uint8_t>* fde(jni_asm->GetFrameDescriptionEntry());
+ ArrayRef<const uint8_t> cfi_ref;
+ if (fde != nullptr) {
+ cfi_ref = ArrayRef<const uint8_t>(*fde);
+ }
+ return CompiledMethod::SwapAllocCompiledMethodCFI(driver,
+ instruction_set,
+ ArrayRef<const uint8_t>(managed_code),
+ frame_size,
+ main_jni_conv->CoreSpillMask(),
+ main_jni_conv->FpSpillMask(),
+ cfi_ref);
}
// Copy a single parameter from the managed to the JNI calling convention
diff --git a/compiler/jni/quick/mips/calling_convention_mips.cc b/compiler/jni/quick/mips/calling_convention_mips.cc
index f7a7be7..aefbf06 100644
--- a/compiler/jni/quick/mips/calling_convention_mips.cc
+++ b/compiler/jni/quick/mips/calling_convention_mips.cc
@@ -17,6 +17,7 @@
#include "calling_convention_mips.h"
#include "base/logging.h"
+#include "handle_scope-inl.h"
#include "utils/mips/managed_register_mips.h"
namespace art {
diff --git a/compiler/jni/quick/x86/calling_convention_x86.cc b/compiler/jni/quick/x86/calling_convention_x86.cc
index 9bf7d0f..8a45f0c 100644
--- a/compiler/jni/quick/x86/calling_convention_x86.cc
+++ b/compiler/jni/quick/x86/calling_convention_x86.cc
@@ -17,6 +17,7 @@
#include "calling_convention_x86.h"
#include "base/logging.h"
+#include "handle_scope-inl.h"
#include "utils/x86/managed_register_x86.h"
#include "utils.h"
@@ -76,12 +77,44 @@
}
bool X86ManagedRuntimeCallingConvention::IsCurrentParamOnStack() {
- return true; // Everything is passed by stack
+ // We assume all parameters are on stack, args coming via registers are spilled as entry_spills.
+ return true;
}
ManagedRegister X86ManagedRuntimeCallingConvention::CurrentParamRegister() {
- LOG(FATAL) << "Should not reach here";
- return ManagedRegister::NoRegister();
+ ManagedRegister res = ManagedRegister::NoRegister();
+ if (!IsCurrentParamAFloatOrDouble()) {
+ switch (gpr_arg_count_) {
+ case 0:
+ res = X86ManagedRegister::FromCpuRegister(ECX);
+ break;
+ case 1:
+ res = X86ManagedRegister::FromCpuRegister(EDX);
+ break;
+ case 2:
+ // Don't split a long between the last register and the stack.
+ if (IsCurrentParamALong()) {
+ return ManagedRegister::NoRegister();
+ }
+ res = X86ManagedRegister::FromCpuRegister(EBX);
+ break;
+ }
+ } else if (itr_float_and_doubles_ < 4) {
+ // First four float parameters are passed via XMM0..XMM3
+ res = X86ManagedRegister::FromXmmRegister(
+ static_cast<XmmRegister>(XMM0 + itr_float_and_doubles_));
+ }
+ return res;
+}
+
+ManagedRegister X86ManagedRuntimeCallingConvention::CurrentParamHighLongRegister() {
+ ManagedRegister res = ManagedRegister::NoRegister();
+ DCHECK(IsCurrentParamALong());
+ switch (gpr_arg_count_) {
+ case 0: res = X86ManagedRegister::FromCpuRegister(EDX); break;
+ case 1: res = X86ManagedRegister::FromCpuRegister(EBX); break;
+ }
+ return res;
}
FrameOffset X86ManagedRuntimeCallingConvention::CurrentParamStackOffset() {
@@ -94,15 +127,39 @@
// We spill the argument registers on X86 to free them up for scratch use, we then assume
// all arguments are on the stack.
if (entry_spills_.size() == 0) {
- size_t num_spills = NumArgs() + NumLongOrDoubleArgs();
- if (num_spills > 0) {
- entry_spills_.push_back(X86ManagedRegister::FromCpuRegister(ECX));
- if (num_spills > 1) {
- entry_spills_.push_back(X86ManagedRegister::FromCpuRegister(EDX));
- if (num_spills > 2) {
- entry_spills_.push_back(X86ManagedRegister::FromCpuRegister(EBX));
+ ResetIterator(FrameOffset(0));
+ while (HasNext()) {
+ ManagedRegister in_reg = CurrentParamRegister();
+ bool is_long = IsCurrentParamALong();
+ if (!in_reg.IsNoRegister()) {
+ int32_t size = IsParamADouble(itr_args_) ? 8 : 4;
+ int32_t spill_offset = CurrentParamStackOffset().Uint32Value();
+ ManagedRegisterSpill spill(in_reg, size, spill_offset);
+ entry_spills_.push_back(spill);
+ if (is_long) {
+ // special case, as we need a second register here.
+ in_reg = CurrentParamHighLongRegister();
+ DCHECK(!in_reg.IsNoRegister());
+ // We have to spill the second half of the long.
+ ManagedRegisterSpill spill2(in_reg, size, spill_offset + 4);
+ entry_spills_.push_back(spill2);
}
+
+ // Keep track of the number of GPRs allocated.
+ if (!IsCurrentParamAFloatOrDouble()) {
+ if (is_long) {
+ // Long was allocated in 2 registers.
+ gpr_arg_count_ += 2;
+ } else {
+ gpr_arg_count_++;
+ }
+ }
+ } else if (is_long) {
+ // We need to skip the unused last register, which is empty.
+ // If we are already out of registers, this is harmless.
+ gpr_arg_count_ += 2;
}
+ Next();
}
}
return entry_spills_;
diff --git a/compiler/jni/quick/x86/calling_convention_x86.h b/compiler/jni/quick/x86/calling_convention_x86.h
index 025eb6d..b1b3598 100644
--- a/compiler/jni/quick/x86/calling_convention_x86.h
+++ b/compiler/jni/quick/x86/calling_convention_x86.h
@@ -28,7 +28,8 @@
public:
explicit X86ManagedRuntimeCallingConvention(bool is_static, bool is_synchronized,
const char* shorty)
- : ManagedRuntimeCallingConvention(is_static, is_synchronized, shorty, kFramePointerSize) {}
+ : ManagedRuntimeCallingConvention(is_static, is_synchronized, shorty, kFramePointerSize),
+ gpr_arg_count_(0) {}
~X86ManagedRuntimeCallingConvention() OVERRIDE {}
// Calling convention
ManagedRegister ReturnRegister() OVERRIDE;
@@ -40,7 +41,10 @@
ManagedRegister CurrentParamRegister() OVERRIDE;
FrameOffset CurrentParamStackOffset() OVERRIDE;
const ManagedRegisterEntrySpills& EntrySpills() OVERRIDE;
+
private:
+ int gpr_arg_count_;
+ ManagedRegister CurrentParamHighLongRegister();
ManagedRegisterEntrySpills entry_spills_;
DISALLOW_COPY_AND_ASSIGN(X86ManagedRuntimeCallingConvention);
};
diff --git a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
index a100552..bbdf1fe 100644
--- a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
+++ b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
@@ -17,6 +17,7 @@
#include "calling_convention_x86_64.h"
#include "base/logging.h"
+#include "handle_scope-inl.h"
#include "utils/x86_64/managed_register_x86_64.h"
#include "utils.h"
diff --git a/compiler/llvm/art_module.ll b/compiler/llvm/art_module.ll
deleted file mode 100644
index 233692c..0000000
--- a/compiler/llvm/art_module.ll
+++ /dev/null
@@ -1,153 +0,0 @@
-;;
-;; Copyright (C) 2012 The Android Open Source Project
-;;
-;; Licensed under the Apache License, Version 2.0 (the "License");
-;; you may not use this file except in compliance with the License.
-;; You may obtain a copy of the License at
-;;
-;; http://www.apache.org/licenses/LICENSE-2.0
-;;
-;; Unless required by applicable law or agreed to in writing, software
-;; distributed under the License is distributed on an "AS IS" BASIS,
-;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-;; See the License for the specific language governing permissions and
-;; limitations under the License.
-;;
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Type
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-%JavaObject = type opaque
-
-%ShadowFrame = type { i32 ; Number of VRegs
- , %ShadowFrame* ; Previous frame
- , %JavaObject* ; Method object pointer
- , i32 ; Line number for stack backtrace
- ; [0 x i32] ; VRegs
- }
-
-declare void @__art_type_list(%JavaObject*, %ShadowFrame*)
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Thread
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-declare %JavaObject* @art_portable_get_current_thread_from_code()
-declare %JavaObject* @art_portable_set_current_thread_from_code(%JavaObject*)
-
-declare void @art_portable_lock_object_from_code(%JavaObject*, %JavaObject*)
-declare void @art_portable_unlock_object_from_code(%JavaObject*, %JavaObject*)
-
-declare void @art_portable_test_suspend_from_code(%JavaObject*)
-
-declare %ShadowFrame* @art_portable_push_shadow_frame_from_code(%JavaObject*, %ShadowFrame*, %JavaObject*, i32)
-declare void @art_portable_pop_shadow_frame_from_code(%ShadowFrame*)
-
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Exception
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-declare %JavaObject* @art_portable_get_and_clear_exception(%JavaObject*)
-declare void @art_portable_throw_div_zero_from_code()
-declare void @art_portable_throw_array_bounds_from_code(i32, i32)
-declare void @art_portable_throw_no_such_method_from_code(i32)
-declare void @art_portable_throw_null_pointer_exception_from_code(i32)
-declare void @art_portable_throw_stack_overflow_from_code()
-declare void @art_portable_throw_exception_from_code(%JavaObject*)
-
-declare i32 @art_portable_find_catch_block_from_code(%JavaObject*, i32)
-
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Object Space
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-declare %JavaObject* @art_portable_alloc_object_from_code(i32, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_alloc_object_from_code_with_access_check(i32, %JavaObject*, %JavaObject*)
-
-declare %JavaObject* @art_portable_alloc_array_from_code(i32, %JavaObject*, i32, %JavaObject*)
-declare %JavaObject* @art_portable_alloc_array_from_code_with_access_check(i32, %JavaObject*, i32, %JavaObject*)
-declare %JavaObject* @art_portable_check_and_alloc_array_from_code(i32, %JavaObject*, i32, %JavaObject*)
-declare %JavaObject* @art_portable_check_and_alloc_array_from_code_with_access_check(i32, %JavaObject*, i32, %JavaObject*)
-
-declare void @art_portable_find_instance_field_from_code(i32, %JavaObject*)
-declare void @art_portable_find_static_field_from_code(i32, %JavaObject*)
-
-declare %JavaObject* @art_portable_find_static_method_from_code_with_access_check(i32, %JavaObject*, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_find_direct_method_from_code_with_access_check(i32, %JavaObject*, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_find_virtual_method_from_code_with_access_check(i32, %JavaObject*, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_find_super_method_from_code_with_access_check(i32, %JavaObject*, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_find_interface_method_from_code_with_access_check(i32, %JavaObject*, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_find_interface_method_from_code(i32, %JavaObject*, %JavaObject*, %JavaObject*)
-
-declare %JavaObject* @art_portable_initialize_static_storage_from_code(i32, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_initialize_type_from_code(i32, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_initialize_type_and_verify_access_from_code(i32, %JavaObject*, %JavaObject*)
-
-declare %JavaObject* @art_portable_resolve_string_from_code(%JavaObject*, i32)
-
-declare i32 @art_portable_set32_static_from_code(i32, %JavaObject*, i32)
-declare i32 @art_portable_set64_static_from_code(i32, %JavaObject*, i64)
-declare i32 @art_portable_set_obj_static_from_code(i32, %JavaObject*, %JavaObject*)
-
-declare i32 @art_portable_get32_static_from_code(i32, %JavaObject*)
-declare i64 @art_portable_get64_static_from_code(i32, %JavaObject*)
-declare %JavaObject* @art_portable_get_obj_static_from_code(i32, %JavaObject*)
-
-declare i32 @art_portable_set32_instance_from_code(i32, %JavaObject*, %JavaObject*, i32)
-declare i32 @art_portable_set64_instance_from_code(i32, %JavaObject*, %JavaObject*, i64)
-declare i32 @art_portable_set_obj_instance_from_code(i32, %JavaObject*, %JavaObject*, %JavaObject*)
-
-declare i32 @art_portable_get32_instance_from_code(i32, %JavaObject*, %JavaObject*)
-declare i64 @art_portable_get64_instance_from_code(i32, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_get_obj_instance_from_code(i32, %JavaObject*, %JavaObject*)
-
-declare %JavaObject* @art_portable_decode_jobject_in_thread(%JavaObject*, %JavaObject*)
-
-declare void @art_portable_fill_array_data_from_code(%JavaObject*, i32, %JavaObject*, i32)
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Type Checking, in the nature of casting
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-declare i32 @art_portable_is_assignable_from_code(%JavaObject*, %JavaObject*)
-declare void @art_portable_check_cast_from_code(%JavaObject*, %JavaObject*)
-declare void @art_portable_check_put_array_element_from_code(%JavaObject*, %JavaObject*)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Math
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-declare i64 @art_d2l(double)
-declare i32 @art_d2i(double)
-declare i64 @art_f2l(float)
-declare i32 @art_f2i(float)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; JNI
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-declare i32 @art_portable_jni_method_start(%JavaObject*)
-declare i32 @art_portable_jni_method_start_synchronized(%JavaObject*, %JavaObject*)
-
-declare void @art_portable_jni_method_end(i32, %JavaObject*)
-declare void @art_portable_jni_method_end_synchronized(i32, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_jni_method_end_with_reference(%JavaObject*, i32, %JavaObject*)
-declare %JavaObject* @art_portable_jni_method_end_with_reference_synchronized(%JavaObject*, i32, %JavaObject*, %JavaObject*)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Temporary runtime support, will be removed in the future
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-declare i1 @art_portable_is_exception_pending_from_code()
-
-declare void @art_portable_mark_gc_card_from_code(%JavaObject*, %JavaObject*)
-
-declare void @art_portable_proxy_invoke_handler_from_code(%JavaObject*, ...)
diff --git a/compiler/llvm/backend_options.h b/compiler/llvm/backend_options.h
deleted file mode 100644
index 2a08bda..0000000
--- a/compiler/llvm/backend_options.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LLVM_BACKEND_OPTIONS_H_
-#define ART_COMPILER_LLVM_BACKEND_OPTIONS_H_
-
-#include <llvm/Support/CommandLine.h>
-
-#define DECLARE_ARM_BACKEND_OPTIONS \
-extern llvm::cl::opt<bool> EnableARMLongCalls; \
-extern llvm::cl::opt<bool> ReserveR9;
-
-#define INITIAL_ARM_BACKEND_OPTIONS \
-EnableARMLongCalls = true; \
-ReserveR9 = true;
-
-#define DECLARE_X86_BACKEND_OPTIONS
-#define INITIAL_X86_BACKEND_OPTIONS
-
-#define DECLARE_Mips_BACKEND_OPTIONS
-#define INITIAL_Mips_BACKEND_OPTIONS
-
-#define LLVM_TARGET(TargetName) DECLARE_##TargetName##_BACKEND_OPTIONS
-#include "llvm/Config/Targets.def"
-
-namespace art {
-namespace llvm {
-
-inline void InitialBackendOptions() {
-#define LLVM_TARGET(TargetName) INITIAL_##TargetName##_BACKEND_OPTIONS
-#include "llvm/Config/Targets.def"
-}
-
-} // namespace llvm
-} // namespace art
-
-#endif // ART_COMPILER_LLVM_BACKEND_OPTIONS_H_
diff --git a/compiler/llvm/backend_types.h b/compiler/llvm/backend_types.h
deleted file mode 100644
index 8ca88dd..0000000
--- a/compiler/llvm/backend_types.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LLVM_BACKEND_TYPES_H_
-#define ART_COMPILER_LLVM_BACKEND_TYPES_H_
-
-#include "base/logging.h"
-
-
-namespace art {
-namespace llvm {
-
-
-enum JType {
- kVoid,
- kBoolean,
- kByte,
- kChar,
- kShort,
- kInt,
- kLong,
- kFloat,
- kDouble,
- kObject,
- MAX_JTYPE
-};
-
-enum TBAASpecialType {
- kTBAARegister,
- kTBAAStackTemp,
- kTBAAHeapArray,
- kTBAAHeapInstance,
- kTBAAHeapStatic,
- kTBAAJRuntime,
- kTBAARuntimeInfo,
- kTBAAShadowFrame,
- kTBAAConstJObject,
- MAX_TBAA_SPECIAL_TYPE
-};
-
-
-enum ExpectCond {
- kLikely,
- kUnlikely,
- MAX_EXPECT
-};
-
-
-inline JType GetJTypeFromShorty(char shorty_jty) {
- switch (shorty_jty) {
- case 'V':
- return kVoid;
-
- case 'Z':
- return kBoolean;
-
- case 'B':
- return kByte;
-
- case 'C':
- return kChar;
-
- case 'S':
- return kShort;
-
- case 'I':
- return kInt;
-
- case 'J':
- return kLong;
-
- case 'F':
- return kFloat;
-
- case 'D':
- return kDouble;
-
- case 'L':
- return kObject;
-
- default:
- LOG(FATAL) << "Unknown Dalvik shorty descriptor: " << shorty_jty;
- return kVoid;
- }
-}
-
-} // namespace llvm
-} // namespace art
-
-
-#endif // ART_COMPILER_LLVM_BACKEND_TYPES_H_
diff --git a/compiler/llvm/compiler_llvm.cc b/compiler/llvm/compiler_llvm.cc
deleted file mode 100644
index 3aeecad..0000000
--- a/compiler/llvm/compiler_llvm.cc
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "compiler_llvm.h"
-
-#include "backend_options.h"
-#include "base/stl_util.h"
-#include "class_linker.h"
-#include "compiled_method.h"
-#include "dex/verification_results.h"
-#include "dex/verified_method.h"
-#include "driver/compiler_driver.h"
-#include "driver/dex_compilation_unit.h"
-#include "globals.h"
-#include "ir_builder.h"
-#include "jni/portable/jni_compiler.h"
-#include "llvm_compilation_unit.h"
-#include "thread-inl.h"
-#include "utils_llvm.h"
-#include "verifier/method_verifier.h"
-
-#include <llvm/LinkAllPasses.h>
-#include <llvm/Support/ManagedStatic.h>
-#include <llvm/Support/TargetSelect.h>
-#include <llvm/Support/Threading.h>
-
-namespace art {
-void CompileOneMethod(CompilerDriver& driver,
- Compiler* compiler,
- const DexFile::CodeItem* code_item,
- uint32_t access_flags, InvokeType invoke_type,
- uint16_t class_def_idx, uint32_t method_idx, jobject class_loader,
- const DexFile& dex_file,
- void* llvm_info);
-}
-
-namespace llvm {
- extern bool TimePassesIsEnabled;
-}
-
-namespace {
-
-pthread_once_t llvm_initialized = PTHREAD_ONCE_INIT;
-
-void InitializeLLVM() {
- // Initialize LLVM internal data structure for multithreading
- llvm::llvm_start_multithreaded();
-
- // NOTE: Uncomment following line to show the time consumption of LLVM passes
- // llvm::TimePassesIsEnabled = true;
-
- // Initialize LLVM target-specific options.
- art::llvm::InitialBackendOptions();
-
- // Initialize LLVM target, MC subsystem, asm printer, and asm parser.
- if (art::kIsTargetBuild) {
- // Don't initialize all targets on device. Just initialize the device's native target
- llvm::InitializeNativeTarget();
- llvm::InitializeNativeTargetAsmPrinter();
- llvm::InitializeNativeTargetAsmParser();
- } else {
- llvm::InitializeAllTargets();
- llvm::InitializeAllTargetMCs();
- llvm::InitializeAllAsmPrinters();
- llvm::InitializeAllAsmParsers();
- }
-
- // Initialize LLVM optimization passes
- llvm::PassRegistry ®istry = *llvm::PassRegistry::getPassRegistry();
-
- llvm::initializeCore(registry);
- llvm::initializeScalarOpts(registry);
- llvm::initializeIPO(registry);
- llvm::initializeAnalysis(registry);
- llvm::initializeIPA(registry);
- llvm::initializeTransformUtils(registry);
- llvm::initializeInstCombine(registry);
- llvm::initializeInstrumentation(registry);
- llvm::initializeTarget(registry);
-}
-
-// The Guard to Shutdown LLVM
-// llvm::llvm_shutdown_obj llvm_guard;
-// TODO: We are commenting out this line because this will cause SEGV from
-// time to time.
-// Two reasons: (1) the order of the destruction of static objects, or
-// (2) dlopen/dlclose side-effect on static objects.
-
-} // anonymous namespace
-
-
-namespace art {
-namespace llvm {
-
-
-::llvm::Module* makeLLVMModuleContents(::llvm::Module* module);
-
-
-CompilerLLVM::CompilerLLVM(CompilerDriver* driver, InstructionSet insn_set)
- : compiler_driver_(driver), insn_set_(insn_set),
- next_cunit_id_lock_("compilation unit id lock"), next_cunit_id_(1) {
-
- // Initialize LLVM libraries
- pthread_once(&llvm_initialized, InitializeLLVM);
-}
-
-
-CompilerLLVM::~CompilerLLVM() {
-}
-
-
-LlvmCompilationUnit* CompilerLLVM::AllocateCompilationUnit() {
- MutexLock GUARD(Thread::Current(), next_cunit_id_lock_);
- LlvmCompilationUnit* cunit = new LlvmCompilationUnit(this, next_cunit_id_++);
- if (!bitcode_filename_.empty()) {
- cunit->SetBitcodeFileName(StringPrintf("%s-%u",
- bitcode_filename_.c_str(),
- cunit->GetCompilationUnitId()));
- }
- return cunit;
-}
-
-
-CompiledMethod* CompilerLLVM::
-CompileDexMethod(DexCompilationUnit* dex_compilation_unit, InvokeType invoke_type) {
- std::unique_ptr<LlvmCompilationUnit> cunit(AllocateCompilationUnit());
-
- cunit->SetDexCompilationUnit(dex_compilation_unit);
- cunit->SetCompilerDriver(compiler_driver_);
- // TODO: consolidate ArtCompileMethods
- CompileOneMethod(compiler_driver_,
- compiler_driver_->GetCompiler(),
- dex_compilation_unit->GetCodeItem(),
- dex_compilation_unit->GetAccessFlags(),
- invoke_type,
- dex_compilation_unit->GetClassDefIndex(),
- dex_compilation_unit->GetDexMethodIndex(),
- dex_compilation_unit->GetClassLoader(),
- *dex_compilation_unit->GetDexFile(),
- cunit.get());
-
- cunit->Materialize();
-
- return new CompiledMethod(*compiler_driver_, compiler_driver_->GetInstructionSet(),
- cunit->GetElfObject(),
- dex_compilation_unit->GetVerifiedMethod()->GetDexGcMap(),
- cunit->GetDexCompilationUnit()->GetSymbol());
-}
-
-
-CompiledMethod* CompilerLLVM::
-CompileNativeMethod(DexCompilationUnit* dex_compilation_unit) {
- std::unique_ptr<LlvmCompilationUnit> cunit(AllocateCompilationUnit());
-
- std::unique_ptr<JniCompiler> jni_compiler(
- new JniCompiler(cunit.get(), compiler_driver_, dex_compilation_unit));
-
- return jni_compiler->Compile();
-}
-
-
-static CompilerLLVM* ContextOf(art::CompilerDriver* driver) {
- void *compiler_context = driver->GetCompilerContext();
- CHECK(compiler_context != NULL);
- return reinterpret_cast<CompilerLLVM*>(compiler_context);
-}
-
-static CompilerLLVM* ContextOf(const art::CompilerDriver& driver) {
- void *compiler_context = driver.GetCompilerContext();
- CHECK(compiler_context != NULL);
- return reinterpret_cast<CompilerLLVM*>(compiler_context);
-}
-
-void ArtInitCompilerContext(CompilerDriver* driver) {
- CHECK(driver->GetCompilerContext() == nullptr);
-
- CompilerLLVM* compiler_llvm = new CompilerLLVM(driver, driver->GetInstructionSet());
-
- driver->SetCompilerContext(compiler_llvm);
-}
-
-void ArtUnInitCompilerContext(CompilerDriver* driver) {
- delete ContextOf(driver);
- driver->SetCompilerContext(nullptr);
-}
-
-CompiledMethod* ArtCompileMethod(CompilerDriver* driver, const DexFile::CodeItem* code_item,
- uint32_t access_flags, InvokeType invoke_type,
- uint16_t class_def_idx, uint32_t method_idx, jobject class_loader,
- const DexFile& dex_file) {
- UNUSED(class_def_idx); // TODO: this is used with Compiler::RequiresConstructorBarrier.
- ClassLinker *class_linker = Runtime::Current()->GetClassLinker();
-
- DexCompilationUnit dex_compilation_unit(nullptr, class_loader, class_linker, dex_file, code_item,
- class_def_idx, method_idx, access_flags,
- driver->GetVerifiedMethod(&dex_file, method_idx));
- CompilerLLVM* compiler_llvm = ContextOf(driver);
- CompiledMethod* result = compiler_llvm->CompileDexMethod(&dex_compilation_unit, invoke_type);
- return result;
-}
-
-CompiledMethod* ArtLLVMJniCompileMethod(CompilerDriver* driver, uint32_t access_flags,
- uint32_t method_idx, const DexFile& dex_file) {
- ClassLinker *class_linker = Runtime::Current()->GetClassLinker();
-
- DexCompilationUnit dex_compilation_unit(nullptr, nullptr, class_linker, dex_file, nullptr,
- 0, method_idx, access_flags, nullptr);
-
- CompilerLLVM* compiler_llvm = ContextOf(driver);
- CompiledMethod* result = compiler_llvm->CompileNativeMethod(&dex_compilation_unit);
- return result;
-}
-
-void compilerLLVMSetBitcodeFileName(const CompilerDriver& driver, const std::string& filename) {
- ContextOf(driver)->SetBitcodeFileName(filename);
-}
-
-} // namespace llvm
-} // namespace art
-
diff --git a/compiler/llvm/compiler_llvm.h b/compiler/llvm/compiler_llvm.h
deleted file mode 100644
index 7d29198..0000000
--- a/compiler/llvm/compiler_llvm.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LLVM_COMPILER_LLVM_H_
-#define ART_COMPILER_LLVM_COMPILER_LLVM_H_
-
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/macros.h"
-#include "dex_file.h"
-#include "driver/compiler_driver.h"
-#include "instruction_set.h"
-#include "mirror/object.h"
-
-namespace art {
- class CompiledMethod;
- class CompilerDriver;
- class DexCompilationUnit;
- namespace mirror {
- class ArtMethod;
- class ClassLoader;
- } // namespace mirror
-} // namespace art
-
-
-namespace llvm {
- class Function;
- class LLVMContext;
- class Module;
- class PointerType;
- class StructType;
- class Type;
-} // namespace llvm
-
-
-namespace art {
-namespace llvm {
-
-class LlvmCompilationUnit;
-class IRBuilder;
-
-class CompilerLLVM {
- public:
- CompilerLLVM(CompilerDriver* driver, InstructionSet insn_set);
-
- ~CompilerLLVM();
-
- CompilerDriver* GetCompiler() const {
- return compiler_driver_;
- }
-
- InstructionSet GetInstructionSet() const {
- return insn_set_;
- }
-
- void SetBitcodeFileName(const std::string& filename) {
- bitcode_filename_ = filename;
- }
-
- CompiledMethod* CompileDexMethod(DexCompilationUnit* dex_compilation_unit,
- InvokeType invoke_type);
-
- CompiledMethod* CompileGBCMethod(DexCompilationUnit* dex_compilation_unit, std::string* func);
-
- CompiledMethod* CompileNativeMethod(DexCompilationUnit* dex_compilation_unit);
-
- private:
- LlvmCompilationUnit* AllocateCompilationUnit();
-
- CompilerDriver* const compiler_driver_;
-
- const InstructionSet insn_set_;
-
- Mutex next_cunit_id_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- size_t next_cunit_id_ GUARDED_BY(next_cunit_id_lock_);
-
- std::string bitcode_filename_;
-
- DISALLOW_COPY_AND_ASSIGN(CompilerLLVM);
-};
-
-void ArtInitCompilerContext(CompilerDriver* driver);
-
-void ArtUnInitCompilerContext(CompilerDriver* driver);
-
-CompiledMethod* ArtCompileMethod(CompilerDriver* driver, const DexFile::CodeItem* code_item,
- uint32_t access_flags, InvokeType invoke_type,
- uint16_t class_def_idx, uint32_t method_idx, jobject class_loader,
- const DexFile& dex_file);
-
-CompiledMethod* ArtLLVMJniCompileMethod(CompilerDriver* driver, uint32_t access_flags,
- uint32_t method_idx, const DexFile& dex_file);
-
-void compilerLLVMSetBitcodeFileName(const CompilerDriver& driver, const std::string& filename);
-
-} // namespace llvm
-} // namespace art
-
-#endif // ART_COMPILER_LLVM_COMPILER_LLVM_H_
diff --git a/compiler/llvm/gbc_expander.cc b/compiler/llvm/gbc_expander.cc
deleted file mode 100644
index 902f8dd..0000000
--- a/compiler/llvm/gbc_expander.cc
+++ /dev/null
@@ -1,3796 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "dex_file.h"
-#include "dex_file-inl.h"
-#include "driver/compiler_driver.h"
-#include "driver/dex_compilation_unit.h"
-#include "intrinsic_helper.h"
-#include "ir_builder.h"
-#include "method_reference.h"
-#include "mirror/art_method.h"
-#include "mirror/array.h"
-#include "mirror/string.h"
-#include "thread.h"
-#include "utils_llvm.h"
-#include "verifier/method_verifier.h"
-
-#include "dex/compiler_ir.h"
-#include "dex/mir_graph.h"
-#include "dex/quick/mir_to_lir.h"
-
-#include <llvm/ADT/STLExtras.h>
-#include <llvm/IR/Intrinsics.h>
-#include <llvm/IR/Metadata.h>
-#include <llvm/Pass.h>
-#include <llvm/Support/CFG.h>
-#include <llvm/Support/InstIterator.h>
-
-#include <vector>
-#include <map>
-#include <utility>
-
-using ::art::kMIRIgnoreNullCheck;
-using ::art::kMIRIgnoreRangeCheck;
-using ::art::llvm::IRBuilder;
-using ::art::llvm::IntrinsicHelper;
-using ::art::llvm::JType;
-using ::art::llvm::RuntimeSupportBuilder;
-using ::art::llvm::kBoolean;
-using ::art::llvm::kByte;
-using ::art::llvm::kChar;
-using ::art::llvm::kDouble;
-using ::art::llvm::kFloat;
-using ::art::llvm::kInt;
-using ::art::llvm::kLikely;
-using ::art::llvm::kLong;
-using ::art::llvm::kObject;
-using ::art::llvm::kShort;
-using ::art::llvm::kTBAAConstJObject;
-using ::art::llvm::kTBAAHeapArray;
-using ::art::llvm::kTBAAHeapInstance;
-using ::art::llvm::kTBAAHeapStatic;
-using ::art::llvm::kTBAARegister;
-using ::art::llvm::kTBAARuntimeInfo;
-using ::art::llvm::kTBAAShadowFrame;
-using ::art::llvm::kUnlikely;
-using ::art::llvm::kVoid;
-using ::art::llvm::runtime_support::AllocArray;
-using ::art::llvm::runtime_support::AllocArrayWithAccessCheck;
-using ::art::llvm::runtime_support::AllocObject;
-using ::art::llvm::runtime_support::AllocObjectWithAccessCheck;
-using ::art::llvm::runtime_support::CheckAndAllocArray;
-using ::art::llvm::runtime_support::CheckAndAllocArrayWithAccessCheck;
-using ::art::llvm::runtime_support::CheckCast;
-using ::art::llvm::runtime_support::CheckPutArrayElement;
-using ::art::llvm::runtime_support::FillArrayData;
-using ::art::llvm::runtime_support::FindCatchBlock;
-using ::art::llvm::runtime_support::FindDirectMethodWithAccessCheck;
-using ::art::llvm::runtime_support::FindInterfaceMethod;
-using ::art::llvm::runtime_support::FindInterfaceMethodWithAccessCheck;
-using ::art::llvm::runtime_support::FindStaticMethodWithAccessCheck;
-using ::art::llvm::runtime_support::FindSuperMethodWithAccessCheck;
-using ::art::llvm::runtime_support::FindVirtualMethodWithAccessCheck;
-using ::art::llvm::runtime_support::Get32Instance;
-using ::art::llvm::runtime_support::Get32Static;
-using ::art::llvm::runtime_support::Get64Instance;
-using ::art::llvm::runtime_support::Get64Static;
-using ::art::llvm::runtime_support::GetObjectInstance;
-using ::art::llvm::runtime_support::GetObjectStatic;
-using ::art::llvm::runtime_support::InitializeStaticStorage;
-using ::art::llvm::runtime_support::InitializeType;
-using ::art::llvm::runtime_support::InitializeTypeAndVerifyAccess;
-using ::art::llvm::runtime_support::IsAssignable;
-using ::art::llvm::runtime_support::ResolveString;
-using ::art::llvm::runtime_support::RuntimeId;
-using ::art::llvm::runtime_support::Set32Instance;
-using ::art::llvm::runtime_support::Set32Static;
-using ::art::llvm::runtime_support::Set64Instance;
-using ::art::llvm::runtime_support::Set64Static;
-using ::art::llvm::runtime_support::SetObjectInstance;
-using ::art::llvm::runtime_support::SetObjectStatic;
-using ::art::llvm::runtime_support::ThrowDivZeroException;
-using ::art::llvm::runtime_support::ThrowException;
-using ::art::llvm::runtime_support::ThrowIndexOutOfBounds;
-using ::art::llvm::runtime_support::ThrowNullPointerException;
-using ::art::llvm::runtime_support::ThrowStackOverflowException;
-using ::art::llvm::runtime_support::art_d2i;
-using ::art::llvm::runtime_support::art_d2l;
-using ::art::llvm::runtime_support::art_f2i;
-using ::art::llvm::runtime_support::art_f2l;
-
-namespace art {
-extern char RemapShorty(char shortyType);
-} // namespace art
-
-namespace {
-
-class GBCExpanderPass : public llvm::FunctionPass {
- private:
- const IntrinsicHelper& intrinsic_helper_;
- IRBuilder& irb_;
-
- llvm::LLVMContext& context_;
- RuntimeSupportBuilder& rtb_;
-
- private:
- llvm::AllocaInst* shadow_frame_;
- llvm::Value* old_shadow_frame_;
-
- private:
- art::CompilerDriver* const driver_;
-
- const art::DexCompilationUnit* const dex_compilation_unit_;
-
- llvm::Function* func_;
-
- std::vector<llvm::BasicBlock*> basic_blocks_;
-
- std::vector<llvm::BasicBlock*> basic_block_landing_pads_;
- llvm::BasicBlock* current_bb_;
- std::map<llvm::BasicBlock*, std::vector<std::pair<llvm::BasicBlock*, llvm::BasicBlock*>>>
- landing_pad_phi_mapping_;
- llvm::BasicBlock* basic_block_unwind_;
-
- // Maps each vreg to its shadow frame address.
- std::vector<llvm::Value*> shadow_frame_vreg_addresses_;
-
- bool changed_;
-
- private:
- //----------------------------------------------------------------------------
- // Constant for GBC expansion
- //----------------------------------------------------------------------------
- enum IntegerShiftKind {
- kIntegerSHL,
- kIntegerSHR,
- kIntegerUSHR,
- };
-
- private:
- //----------------------------------------------------------------------------
- // Helper function for GBC expansion
- //----------------------------------------------------------------------------
-
- llvm::Value* ExpandToRuntime(RuntimeId rt, llvm::CallInst& inst);
-
- uint64_t LV2UInt(llvm::Value* lv) {
- return llvm::cast<llvm::ConstantInt>(lv)->getZExtValue();
- }
-
- int64_t LV2SInt(llvm::Value* lv) {
- return llvm::cast<llvm::ConstantInt>(lv)->getSExtValue();
- }
-
- private:
- // TODO: Almost all Emit* are directly copy-n-paste from MethodCompiler.
- // Refactor these utility functions from MethodCompiler to avoid forking.
-
- void EmitStackOverflowCheck(llvm::Instruction* first_non_alloca);
-
- void RewriteFunction();
-
- void RewriteBasicBlock(llvm::BasicBlock* original_block);
-
- void UpdatePhiInstruction(llvm::BasicBlock* old_basic_block,
- llvm::BasicBlock* new_basic_block);
-
-
- // Sign or zero extend category 1 types < 32bits in size to 32bits.
- llvm::Value* SignOrZeroExtendCat1Types(llvm::Value* value, JType jty);
-
- // Truncate category 1 types from 32bits to the given JType size.
- llvm::Value* TruncateCat1Types(llvm::Value* value, JType jty);
-
- //----------------------------------------------------------------------------
- // Dex cache code generation helper function
- //----------------------------------------------------------------------------
- llvm::Value* EmitLoadDexCacheAddr(art::MemberOffset dex_cache_offset);
-
- llvm::Value* EmitLoadDexCacheResolvedTypeFieldAddr(uint32_t type_idx);
-
- llvm::Value* EmitLoadDexCacheResolvedMethodFieldAddr(uint32_t method_idx);
-
- llvm::Value* EmitLoadDexCacheStringFieldAddr(uint32_t string_idx);
-
- //----------------------------------------------------------------------------
- // Code generation helper function
- //----------------------------------------------------------------------------
- llvm::Value* EmitLoadMethodObjectAddr();
-
- llvm::Value* EmitLoadArrayLength(llvm::Value* array);
-
- llvm::Value* EmitLoadSDCalleeMethodObjectAddr(uint32_t callee_method_idx);
-
- llvm::Value* EmitLoadVirtualCalleeMethodObjectAddr(int vtable_idx,
- llvm::Value* this_addr);
-
- llvm::Value* EmitArrayGEP(llvm::Value* array_addr,
- llvm::Value* index_value,
- JType elem_jty);
-
- //----------------------------------------------------------------------------
- // Invoke helper function
- //----------------------------------------------------------------------------
- llvm::Value* EmitInvoke(llvm::CallInst& call_inst);
-
- //----------------------------------------------------------------------------
- // Inlining helper functions
- //----------------------------------------------------------------------------
- bool EmitIntrinsic(llvm::CallInst& call_inst, llvm::Value** result);
-
- bool EmitIntrinsicStringLengthOrIsEmpty(llvm::CallInst& call_inst,
- llvm::Value** result, bool is_empty);
-
- private:
- //----------------------------------------------------------------------------
- // Expand Greenland intrinsics
- //----------------------------------------------------------------------------
- void Expand_TestSuspend(llvm::CallInst& call_inst);
-
- void Expand_MarkGCCard(llvm::CallInst& call_inst);
-
- llvm::Value* Expand_LoadStringFromDexCache(llvm::Value* string_idx_value);
-
- llvm::Value* Expand_LoadTypeFromDexCache(llvm::Value* type_idx_value);
-
- void Expand_LockObject(llvm::Value* obj);
-
- void Expand_UnlockObject(llvm::Value* obj);
-
- llvm::Value* Expand_ArrayGet(llvm::Value* array_addr,
- llvm::Value* index_value,
- JType elem_jty);
-
- void Expand_ArrayPut(llvm::Value* new_value,
- llvm::Value* array_addr,
- llvm::Value* index_value,
- JType elem_jty);
-
- void Expand_FilledNewArray(llvm::CallInst& call_inst);
-
- llvm::Value* Expand_IGetFast(llvm::Value* field_offset_value,
- llvm::Value* is_volatile_value,
- llvm::Value* object_addr,
- JType field_jty);
-
- void Expand_IPutFast(llvm::Value* field_offset_value,
- llvm::Value* is_volatile_value,
- llvm::Value* object_addr,
- llvm::Value* new_value,
- JType field_jty);
-
- llvm::Value* Expand_SGetFast(llvm::Value* static_storage_addr,
- llvm::Value* field_offset_value,
- llvm::Value* is_volatile_value,
- JType field_jty);
-
- void Expand_SPutFast(llvm::Value* static_storage_addr,
- llvm::Value* field_offset_value,
- llvm::Value* is_volatile_value,
- llvm::Value* new_value,
- JType field_jty);
-
- llvm::Value* Expand_LoadDeclaringClassSSB(llvm::Value* method_object_addr);
-
- llvm::Value*
- Expand_GetSDCalleeMethodObjAddrFast(llvm::Value* callee_method_idx_value);
-
- llvm::Value*
- Expand_GetVirtualCalleeMethodObjAddrFast(llvm::Value* vtable_idx_value,
- llvm::Value* this_addr);
-
- llvm::Value* Expand_Invoke(llvm::CallInst& call_inst);
-
- llvm::Value* Expand_DivRem(llvm::CallInst& call_inst, bool is_div, JType op_jty);
-
- void Expand_AllocaShadowFrame(llvm::Value* num_vregs_value);
-
- void Expand_SetVReg(llvm::Value* entry_idx, llvm::Value* obj);
-
- void Expand_PopShadowFrame();
-
- void Expand_UpdateDexPC(llvm::Value* dex_pc_value);
-
- //----------------------------------------------------------------------------
- // Quick
- //----------------------------------------------------------------------------
-
- llvm::Value* Expand_FPCompare(llvm::Value* src1_value,
- llvm::Value* src2_value,
- bool gt_bias);
-
- llvm::Value* Expand_LongCompare(llvm::Value* src1_value, llvm::Value* src2_value);
-
- llvm::Value* EmitCompareResultSelection(llvm::Value* cmp_eq,
- llvm::Value* cmp_lt);
-
- llvm::Value* EmitLoadConstantClass(uint32_t dex_pc, uint32_t type_idx);
- llvm::Value* EmitLoadStaticStorage(uint32_t dex_pc, uint32_t type_idx);
-
- llvm::Value* Expand_HLIGet(llvm::CallInst& call_inst, JType field_jty);
- void Expand_HLIPut(llvm::CallInst& call_inst, JType field_jty);
-
- llvm::Value* Expand_HLSget(llvm::CallInst& call_inst, JType field_jty);
- void Expand_HLSput(llvm::CallInst& call_inst, JType field_jty);
-
- llvm::Value* Expand_HLArrayGet(llvm::CallInst& call_inst, JType field_jty);
- void Expand_HLArrayPut(llvm::CallInst& call_inst, JType field_jty);
-
- llvm::Value* Expand_ConstString(llvm::CallInst& call_inst);
- llvm::Value* Expand_ConstClass(llvm::CallInst& call_inst);
-
- void Expand_MonitorEnter(llvm::CallInst& call_inst);
- void Expand_MonitorExit(llvm::CallInst& call_inst);
-
- void Expand_HLCheckCast(llvm::CallInst& call_inst);
- llvm::Value* Expand_InstanceOf(llvm::CallInst& call_inst);
-
- llvm::Value* Expand_NewInstance(llvm::CallInst& call_inst);
-
- llvm::Value* Expand_HLInvoke(llvm::CallInst& call_inst);
-
- llvm::Value* Expand_OptArrayLength(llvm::CallInst& call_inst);
- llvm::Value* Expand_NewArray(llvm::CallInst& call_inst);
- llvm::Value* Expand_HLFilledNewArray(llvm::CallInst& call_inst);
- void Expand_HLFillArrayData(llvm::CallInst& call_inst);
-
- llvm::Value* EmitAllocNewArray(uint32_t dex_pc,
- llvm::Value* array_length_value,
- uint32_t type_idx,
- bool is_filled_new_array);
-
- llvm::Value* EmitCallRuntimeForCalleeMethodObjectAddr(uint32_t callee_method_idx,
- art::InvokeType invoke_type,
- llvm::Value* this_addr,
- uint32_t dex_pc,
- bool is_fast_path);
-
- void EmitMarkGCCard(llvm::Value* value, llvm::Value* target_addr);
-
- void EmitUpdateDexPC(uint32_t dex_pc);
-
- void EmitGuard_DivZeroException(uint32_t dex_pc,
- llvm::Value* denominator,
- JType op_jty);
-
- void EmitGuard_NullPointerException(uint32_t dex_pc, llvm::Value* object,
- int opt_flags);
-
- void EmitGuard_ArrayIndexOutOfBoundsException(uint32_t dex_pc,
- llvm::Value* array,
- llvm::Value* index,
- int opt_flags);
-
- llvm::FunctionType* GetFunctionType(llvm::Type* ret_type, uint32_t method_idx, bool is_static);
-
- llvm::BasicBlock* GetBasicBlock(uint32_t dex_pc);
-
- llvm::BasicBlock* CreateBasicBlockWithDexPC(uint32_t dex_pc,
- const char* postfix);
-
- int32_t GetTryItemOffset(uint32_t dex_pc);
-
- llvm::BasicBlock* GetLandingPadBasicBlock(uint32_t dex_pc);
-
- llvm::BasicBlock* GetUnwindBasicBlock();
-
- void EmitGuard_ExceptionLandingPad(uint32_t dex_pc);
-
- void EmitBranchExceptionLandingPad(uint32_t dex_pc);
-
- //----------------------------------------------------------------------------
- // Expand Arithmetic Helper Intrinsics
- //----------------------------------------------------------------------------
-
- llvm::Value* Expand_IntegerShift(llvm::Value* src1_value,
- llvm::Value* src2_value,
- IntegerShiftKind kind,
- JType op_jty);
-
- public:
- static char ID;
-
- GBCExpanderPass(const IntrinsicHelper& intrinsic_helper, IRBuilder& irb,
- art::CompilerDriver* driver, const art::DexCompilationUnit* dex_compilation_unit)
- : llvm::FunctionPass(ID), intrinsic_helper_(intrinsic_helper), irb_(irb),
- context_(irb.getContext()), rtb_(irb.Runtime()),
- shadow_frame_(NULL), old_shadow_frame_(NULL),
- driver_(driver),
- dex_compilation_unit_(dex_compilation_unit),
- func_(NULL), current_bb_(NULL), basic_block_unwind_(NULL), changed_(false) {}
-
- bool runOnFunction(llvm::Function& func);
-
- private:
- void InsertStackOverflowCheck(llvm::Function& func);
-
- llvm::Value* ExpandIntrinsic(IntrinsicHelper::IntrinsicId intr_id,
- llvm::CallInst& call_inst);
-};
-
-char GBCExpanderPass::ID = 0;
-
-bool GBCExpanderPass::runOnFunction(llvm::Function& func) {
- VLOG(compiler) << "GBC expansion on " << func.getName().str();
-
- // Runtime support or stub
- if (dex_compilation_unit_ == NULL) {
- return false;
- }
-
- // Setup rewrite context
- shadow_frame_ = NULL;
- old_shadow_frame_ = NULL;
- func_ = &func;
- changed_ = false; // Assume unchanged
-
- shadow_frame_vreg_addresses_.resize(dex_compilation_unit_->GetCodeItem()->registers_size_, NULL);
- basic_blocks_.resize(dex_compilation_unit_->GetCodeItem()->insns_size_in_code_units_);
- basic_block_landing_pads_.resize(dex_compilation_unit_->GetCodeItem()->tries_size_, NULL);
- basic_block_unwind_ = NULL;
- for (llvm::Function::iterator bb_iter = func_->begin(), bb_end = func_->end();
- bb_iter != bb_end;
- ++bb_iter) {
- if (bb_iter->begin()->getMetadata("DexOff") == NULL) {
- continue;
- }
- uint32_t dex_pc = LV2UInt(bb_iter->begin()->getMetadata("DexOff")->getOperand(0));
- basic_blocks_[dex_pc] = bb_iter;
- }
-
- // Insert stack overflow check
- InsertStackOverflowCheck(func); // TODO: Use intrinsic.
-
- // Rewrite the intrinsics
- RewriteFunction();
-
- VERIFY_LLVM_FUNCTION(func);
-
- return changed_;
-}
-
-void GBCExpanderPass::RewriteBasicBlock(llvm::BasicBlock* original_block) {
- llvm::BasicBlock* curr_basic_block = original_block;
-
- llvm::BasicBlock::iterator inst_iter = original_block->begin();
- llvm::BasicBlock::iterator inst_end = original_block->end();
-
- while (inst_iter != inst_end) {
- llvm::CallInst* call_inst = llvm::dyn_cast<llvm::CallInst>(inst_iter);
- IntrinsicHelper::IntrinsicId intr_id = IntrinsicHelper::UnknownId;
-
- if (call_inst) {
- llvm::Function* callee_func = call_inst->getCalledFunction();
- intr_id = intrinsic_helper_.GetIntrinsicId(callee_func);
- }
-
- if (intr_id == IntrinsicHelper::UnknownId) {
- // This is not intrinsic call. Skip this instruction.
- ++inst_iter;
- continue;
- }
-
- // Rewrite the intrinsic and change the function
- changed_ = true;
- irb_.SetInsertPoint(inst_iter);
-
- // Expand the intrinsic
- if (llvm::Value* new_value = ExpandIntrinsic(intr_id, *call_inst)) {
- inst_iter->replaceAllUsesWith(new_value);
- }
-
- // Remove the old intrinsic call instruction
- llvm::BasicBlock::iterator old_inst = inst_iter++;
- old_inst->eraseFromParent();
-
- // Splice the instruction to the new basic block
- llvm::BasicBlock* next_basic_block = irb_.GetInsertBlock();
- if (next_basic_block != curr_basic_block) {
- next_basic_block->getInstList().splice(
- irb_.GetInsertPoint(), curr_basic_block->getInstList(),
- inst_iter, inst_end);
- curr_basic_block = next_basic_block;
- inst_end = curr_basic_block->end();
- }
- }
-}
-
-
-void GBCExpanderPass::RewriteFunction() {
- size_t num_basic_blocks = func_->getBasicBlockList().size();
- // NOTE: We are not using (bb_iter != bb_end) as the for-loop condition,
- // because we will create new basic block while expanding the intrinsics.
- // We only want to iterate through the input basic blocks.
-
- landing_pad_phi_mapping_.clear();
-
- for (llvm::Function::iterator bb_iter = func_->begin();
- num_basic_blocks > 0; ++bb_iter, --num_basic_blocks) {
- // Set insert point to current basic block.
- irb_.SetInsertPoint(bb_iter);
-
- current_bb_ = bb_iter;
-
- // Rewrite the basic block
- RewriteBasicBlock(bb_iter);
-
- // Update the phi-instructions in the successor basic block
- llvm::BasicBlock* last_block = irb_.GetInsertBlock();
- if (last_block != bb_iter) {
- UpdatePhiInstruction(bb_iter, last_block);
- }
- }
-
- typedef std::map<llvm::PHINode*, llvm::PHINode*> HandlerPHIMap;
- HandlerPHIMap handler_phi;
- // Iterate every used landing pad basic block
- for (size_t i = 0, ei = basic_block_landing_pads_.size(); i != ei; ++i) {
- llvm::BasicBlock* lbb = basic_block_landing_pads_[i];
- if (lbb == NULL) {
- continue;
- }
-
- llvm::TerminatorInst* term_inst = lbb->getTerminator();
- std::vector<std::pair<llvm::BasicBlock*, llvm::BasicBlock*>>& rewrite_pair
- = landing_pad_phi_mapping_[lbb];
- irb_.SetInsertPoint(lbb->begin());
-
- // Iterate every succeeding basic block (catch block)
- for (unsigned succ_iter = 0, succ_end = term_inst->getNumSuccessors();
- succ_iter != succ_end; ++succ_iter) {
- llvm::BasicBlock* succ_basic_block = term_inst->getSuccessor(succ_iter);
-
- // Iterate every phi instructions in the succeeding basic block
- for (llvm::BasicBlock::iterator
- inst_iter = succ_basic_block->begin(),
- inst_end = succ_basic_block->end();
- inst_iter != inst_end; ++inst_iter) {
- llvm::PHINode *phi = llvm::dyn_cast<llvm::PHINode>(inst_iter);
-
- if (!phi) {
- break; // Meet non-phi instruction. Done.
- }
-
- if (handler_phi[phi] == NULL) {
- handler_phi[phi] = llvm::PHINode::Create(phi->getType(), 1);
- }
-
- // Create new_phi in landing pad
- llvm::PHINode* new_phi = irb_.CreatePHI(phi->getType(), rewrite_pair.size());
- // Insert all incoming value into new_phi by rewrite_pair
- for (size_t j = 0, ej = rewrite_pair.size(); j != ej; ++j) {
- llvm::BasicBlock* old_bb = rewrite_pair[j].first;
- llvm::BasicBlock* new_bb = rewrite_pair[j].second;
- new_phi->addIncoming(phi->getIncomingValueForBlock(old_bb), new_bb);
- }
- // Delete all incoming value from phi by rewrite_pair
- for (size_t j = 0, ej = rewrite_pair.size(); j != ej; ++j) {
- llvm::BasicBlock* old_bb = rewrite_pair[j].first;
- int old_bb_idx = phi->getBasicBlockIndex(old_bb);
- if (old_bb_idx >= 0) {
- phi->removeIncomingValue(old_bb_idx, false);
- }
- }
- // Insert new_phi into new handler phi
- handler_phi[phi]->addIncoming(new_phi, lbb);
- }
- }
- }
-
- // Replace all handler phi
- // We can't just use the old handler phi, because some exception edges will disappear after we
- // compute fast-path.
- for (HandlerPHIMap::iterator it = handler_phi.begin(); it != handler_phi.end(); ++it) {
- llvm::PHINode* old_phi = it->first;
- llvm::PHINode* new_phi = it->second;
- new_phi->insertBefore(old_phi);
- old_phi->replaceAllUsesWith(new_phi);
- old_phi->eraseFromParent();
- }
-}
-
-void GBCExpanderPass::UpdatePhiInstruction(llvm::BasicBlock* old_basic_block,
- llvm::BasicBlock* new_basic_block) {
- llvm::TerminatorInst* term_inst = new_basic_block->getTerminator();
-
- if (!term_inst) {
- return; // No terminating instruction in new_basic_block. Nothing to do.
- }
-
- // Iterate every succeeding basic block
- for (unsigned succ_iter = 0, succ_end = term_inst->getNumSuccessors();
- succ_iter != succ_end; ++succ_iter) {
- llvm::BasicBlock* succ_basic_block = term_inst->getSuccessor(succ_iter);
-
- // Iterate every phi instructions in the succeeding basic block
- for (llvm::BasicBlock::iterator
- inst_iter = succ_basic_block->begin(),
- inst_end = succ_basic_block->end();
- inst_iter != inst_end; ++inst_iter) {
- llvm::PHINode *phi = llvm::dyn_cast<llvm::PHINode>(inst_iter);
-
- if (!phi) {
- break; // Meet non-phi instruction. Done.
- }
-
- // Update the incoming block of this phi instruction
- for (llvm::PHINode::block_iterator
- ibb_iter = phi->block_begin(), ibb_end = phi->block_end();
- ibb_iter != ibb_end; ++ibb_iter) {
- if (*ibb_iter == old_basic_block) {
- *ibb_iter = new_basic_block;
- }
- }
- }
- }
-}
-
-llvm::Value* GBCExpanderPass::ExpandToRuntime(RuntimeId rt, llvm::CallInst& inst) {
- // Some GBC intrinsic can directly replace with IBC runtime. "Directly" means
- // the arguments passed to the GBC intrinsic are as the same as IBC runtime
- // function, therefore only called function is needed to change.
- unsigned num_args = inst.getNumArgOperands();
-
- if (num_args <= 0) {
- return irb_.CreateCall(irb_.GetRuntime(rt));
- } else {
- std::vector<llvm::Value*> args;
- for (unsigned i = 0; i < num_args; i++) {
- args.push_back(inst.getArgOperand(i));
- }
-
- return irb_.CreateCall(irb_.GetRuntime(rt), args);
- }
-}
-
-void
-GBCExpanderPass::EmitStackOverflowCheck(llvm::Instruction* first_non_alloca) {
- llvm::Function* func = first_non_alloca->getParent()->getParent();
- llvm::Module* module = func->getParent();
-
- // Call llvm intrinsic function to get frame address.
- llvm::Function* frameaddress =
- llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::frameaddress);
-
- // The type of llvm::frameaddress is: i8* @llvm.frameaddress(i32)
- llvm::Value* frame_address = irb_.CreateCall(frameaddress, irb_.getInt32(0));
-
- // Cast i8* to int
- frame_address = irb_.CreatePtrToInt(frame_address, irb_.getPtrEquivIntTy());
-
- // Get thread.stack_end_
- llvm::Value* stack_end =
- irb_.Runtime().EmitLoadFromThreadOffset(art::Thread::StackEndOffset().Int32Value(),
- irb_.getPtrEquivIntTy(),
- kTBAARuntimeInfo);
-
- // Check the frame address < thread.stack_end_ ?
- llvm::Value* is_stack_overflow = irb_.CreateICmpULT(frame_address, stack_end);
-
- llvm::BasicBlock* block_exception =
- llvm::BasicBlock::Create(context_, "stack_overflow", func);
-
- llvm::BasicBlock* block_continue =
- llvm::BasicBlock::Create(context_, "stack_overflow_cont", func);
-
- irb_.CreateCondBr(is_stack_overflow, block_exception, block_continue, kUnlikely);
-
- // If stack overflow, throw exception.
- irb_.SetInsertPoint(block_exception);
- irb_.CreateCall(irb_.GetRuntime(ThrowStackOverflowException));
-
- // Unwind.
- llvm::Type* ret_type = func->getReturnType();
- if (ret_type->isVoidTy()) {
- irb_.CreateRetVoid();
- } else {
- // The return value is ignored when there's an exception. MethodCompiler
- // returns zero value under the the corresponding return type in this case.
- // GBCExpander returns LLVM undef value here for brevity
- irb_.CreateRet(llvm::UndefValue::get(ret_type));
- }
-
- irb_.SetInsertPoint(block_continue);
-}
-
-llvm::Value* GBCExpanderPass::EmitLoadDexCacheAddr(art::MemberOffset offset) {
- llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
- return irb_.LoadFromObjectOffset(method_object_addr,
- offset.Int32Value(),
- irb_.getJObjectTy(),
- kTBAAConstJObject);
-}
-
-llvm::Value*
-GBCExpanderPass::EmitLoadDexCacheResolvedTypeFieldAddr(uint32_t type_idx) {
- llvm::Value* resolved_type_dex_cache_addr =
- EmitLoadDexCacheAddr(art::mirror::ArtMethod::DexCacheResolvedTypesOffset());
-
- llvm::Value* type_idx_value = irb_.getPtrEquivInt(type_idx);
-
- return EmitArrayGEP(resolved_type_dex_cache_addr, type_idx_value, kObject);
-}
-
-llvm::Value* GBCExpanderPass::
-EmitLoadDexCacheResolvedMethodFieldAddr(uint32_t method_idx) {
- llvm::Value* resolved_method_dex_cache_addr =
- EmitLoadDexCacheAddr(art::mirror::ArtMethod::DexCacheResolvedMethodsOffset());
-
- llvm::Value* method_idx_value = irb_.getPtrEquivInt(method_idx);
-
- return EmitArrayGEP(resolved_method_dex_cache_addr, method_idx_value, kObject);
-}
-
-llvm::Value* GBCExpanderPass::
-EmitLoadDexCacheStringFieldAddr(uint32_t string_idx) {
- llvm::Value* string_dex_cache_addr =
- EmitLoadDexCacheAddr(art::mirror::ArtMethod::DexCacheStringsOffset());
-
- llvm::Value* string_idx_value = irb_.getPtrEquivInt(string_idx);
-
- return EmitArrayGEP(string_dex_cache_addr, string_idx_value, kObject);
-}
-
-llvm::Value* GBCExpanderPass::EmitLoadMethodObjectAddr() {
- llvm::Function* parent_func = irb_.GetInsertBlock()->getParent();
- return parent_func->arg_begin();
-}
-
-llvm::Value* GBCExpanderPass::EmitLoadArrayLength(llvm::Value* array) {
- // Load array length
- return irb_.LoadFromObjectOffset(array,
- art::mirror::Array::LengthOffset().Int32Value(),
- irb_.getJIntTy(),
- kTBAAConstJObject);
-}
-
-llvm::Value*
-GBCExpanderPass::EmitLoadSDCalleeMethodObjectAddr(uint32_t callee_method_idx) {
- llvm::Value* callee_method_object_field_addr =
- EmitLoadDexCacheResolvedMethodFieldAddr(callee_method_idx);
-
- return irb_.CreateLoad(callee_method_object_field_addr, kTBAARuntimeInfo);
-}
-
-llvm::Value* GBCExpanderPass::
-EmitLoadVirtualCalleeMethodObjectAddr(int vtable_idx, llvm::Value* this_addr) {
- // Load class object of *this* pointer
- llvm::Value* class_object_addr =
- irb_.LoadFromObjectOffset(this_addr,
- art::mirror::Object::ClassOffset().Int32Value(),
- irb_.getJObjectTy(),
- kTBAAConstJObject);
-
- // Load vtable address
- llvm::Value* vtable_addr =
- irb_.LoadFromObjectOffset(class_object_addr,
- art::mirror::Class::VTableOffset().Int32Value(),
- irb_.getJObjectTy(),
- kTBAAConstJObject);
-
- // Load callee method object
- llvm::Value* vtable_idx_value =
- irb_.getPtrEquivInt(static_cast<uint64_t>(vtable_idx));
-
- llvm::Value* method_field_addr =
- EmitArrayGEP(vtable_addr, vtable_idx_value, kObject);
-
- return irb_.CreateLoad(method_field_addr, kTBAAConstJObject);
-}
-
-// Emit Array GetElementPtr
-llvm::Value* GBCExpanderPass::EmitArrayGEP(llvm::Value* array_addr,
- llvm::Value* index_value,
- JType elem_jty) {
- int data_offset;
- if (elem_jty == kLong || elem_jty == kDouble ||
- (elem_jty == kObject && sizeof(uint64_t) == sizeof(art::mirror::Object*))) {
- data_offset = art::mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
- } else {
- data_offset = art::mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
- }
-
- llvm::Constant* data_offset_value =
- irb_.getPtrEquivInt(data_offset);
-
- llvm::Type* elem_type = irb_.getJType(elem_jty);
-
- llvm::Value* array_data_addr =
- irb_.CreatePtrDisp(array_addr, data_offset_value,
- elem_type->getPointerTo());
-
- return irb_.CreateGEP(array_data_addr, index_value);
-}
-
-llvm::Value* GBCExpanderPass::EmitInvoke(llvm::CallInst& call_inst) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- art::InvokeType invoke_type =
- static_cast<art::InvokeType>(LV2UInt(call_inst.getArgOperand(0)));
- bool is_static = (invoke_type == art::kStatic);
- art::MethodReference target_method(dex_compilation_unit_->GetDexFile(),
- LV2UInt(call_inst.getArgOperand(1)));
-
- // Load *this* actual parameter
- llvm::Value* this_addr = (!is_static) ? call_inst.getArgOperand(3) : NULL;
-
- // Compute invoke related information for compiler decision
- int vtable_idx = -1;
- uintptr_t direct_code = 0;
- uintptr_t direct_method = 0;
- bool is_fast_path = driver_->ComputeInvokeInfo(dex_compilation_unit_, dex_pc,
- true, true,
- &invoke_type, &target_method,
- &vtable_idx,
- &direct_code, &direct_method);
- // Load the method object
- llvm::Value* callee_method_object_addr = NULL;
-
- if (!is_fast_path) {
- callee_method_object_addr =
- EmitCallRuntimeForCalleeMethodObjectAddr(target_method.dex_method_index, invoke_type,
- this_addr, dex_pc, is_fast_path);
- } else {
- switch (invoke_type) {
- case art::kStatic:
- case art::kDirect:
- if (direct_method != 0u &&
- direct_method != static_cast<uintptr_t>(-1)) {
- callee_method_object_addr =
- irb_.CreateIntToPtr(irb_.getPtrEquivInt(direct_method),
- irb_.getJObjectTy());
- } else {
- callee_method_object_addr =
- EmitLoadSDCalleeMethodObjectAddr(target_method.dex_method_index);
- }
- break;
-
- case art::kVirtual:
- DCHECK_NE(vtable_idx, -1);
- callee_method_object_addr =
- EmitLoadVirtualCalleeMethodObjectAddr(vtable_idx, this_addr);
- break;
-
- case art::kSuper:
- LOG(FATAL) << "invoke-super should be promoted to invoke-direct in "
- "the fast path.";
- break;
-
- case art::kInterface:
- callee_method_object_addr =
- EmitCallRuntimeForCalleeMethodObjectAddr(target_method.dex_method_index,
- invoke_type, this_addr,
- dex_pc, is_fast_path);
- break;
- }
- }
-
- // Load the actual parameter
- std::vector<llvm::Value*> args;
-
- args.push_back(callee_method_object_addr); // method object for callee
-
- for (uint32_t i = 3; i < call_inst.getNumArgOperands(); ++i) {
- args.push_back(call_inst.getArgOperand(i));
- }
-
- llvm::Value* code_addr;
- llvm::Type* func_type = GetFunctionType(call_inst.getType(),
- target_method.dex_method_index, is_static);
- if (direct_code != 0u && direct_code != static_cast<uintptr_t>(-1)) {
- code_addr =
- irb_.CreateIntToPtr(irb_.getPtrEquivInt(direct_code),
- func_type->getPointerTo());
- } else {
- code_addr =
- irb_.LoadFromObjectOffset(callee_method_object_addr,
- art::mirror::ArtMethod::EntryPointFromPortableCompiledCodeOffset().Int32Value(),
- func_type->getPointerTo(), kTBAARuntimeInfo);
- }
-
- // Invoke callee
- EmitUpdateDexPC(dex_pc);
- llvm::Value* retval = irb_.CreateCall(code_addr, args);
- EmitGuard_ExceptionLandingPad(dex_pc);
-
- return retval;
-}
-
-bool GBCExpanderPass::EmitIntrinsic(llvm::CallInst& call_inst,
- llvm::Value** result) {
- DCHECK(result != NULL);
-
- uint32_t callee_method_idx = LV2UInt(call_inst.getArgOperand(1));
- std::string callee_method_name(
- PrettyMethod(callee_method_idx, *dex_compilation_unit_->GetDexFile()));
-
- if (callee_method_name == "int java.lang.String.length()") {
- return EmitIntrinsicStringLengthOrIsEmpty(call_inst, result,
- false /* is_empty */);
- }
- if (callee_method_name == "boolean java.lang.String.isEmpty()") {
- return EmitIntrinsicStringLengthOrIsEmpty(call_inst, result,
- true /* is_empty */);
- }
-
- *result = NULL;
- return false;
-}
-
-bool GBCExpanderPass::EmitIntrinsicStringLengthOrIsEmpty(llvm::CallInst& call_inst,
- llvm::Value** result,
- bool is_empty) {
- art::InvokeType invoke_type =
- static_cast<art::InvokeType>(LV2UInt(call_inst.getArgOperand(0)));
- DCHECK_NE(invoke_type, art::kStatic);
- DCHECK_EQ(call_inst.getNumArgOperands(), 4U);
-
- llvm::Value* this_object = call_inst.getArgOperand(3);
- llvm::Value* string_count =
- irb_.LoadFromObjectOffset(this_object,
- art::mirror::String::CountOffset().Int32Value(),
- irb_.getJIntTy(),
- kTBAAConstJObject);
- if (is_empty) {
- llvm::Value* count_equals_zero = irb_.CreateICmpEQ(string_count,
- irb_.getJInt(0));
- llvm::Value* is_empty = irb_.CreateSelect(count_equals_zero,
- irb_.getJBoolean(true),
- irb_.getJBoolean(false));
- is_empty = SignOrZeroExtendCat1Types(is_empty, kBoolean);
- *result = is_empty;
- } else {
- *result = string_count;
- }
- return true;
-}
-
-void GBCExpanderPass::Expand_TestSuspend(llvm::CallInst& call_inst) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-
- llvm::Value* suspend_count =
- irb_.Runtime().EmitLoadFromThreadOffset(art::Thread::ThreadFlagsOffset().Int32Value(),
- irb_.getInt16Ty(),
- kTBAARuntimeInfo);
- llvm::Value* is_suspend = irb_.CreateICmpNE(suspend_count, irb_.getInt16(0));
-
- llvm::BasicBlock* basic_block_suspend = CreateBasicBlockWithDexPC(dex_pc, "suspend");
- llvm::BasicBlock* basic_block_cont = CreateBasicBlockWithDexPC(dex_pc, "suspend_cont");
-
- irb_.CreateCondBr(is_suspend, basic_block_suspend, basic_block_cont, kUnlikely);
-
- irb_.SetInsertPoint(basic_block_suspend);
- if (dex_pc != art::DexFile::kDexNoIndex) {
- EmitUpdateDexPC(dex_pc);
- }
- irb_.Runtime().EmitTestSuspend();
-
- llvm::BasicBlock* basic_block_exception = CreateBasicBlockWithDexPC(dex_pc, "exception");
- llvm::Value* exception_pending = irb_.Runtime().EmitIsExceptionPending();
- irb_.CreateCondBr(exception_pending, basic_block_exception, basic_block_cont, kUnlikely);
-
- irb_.SetInsertPoint(basic_block_exception);
- llvm::Type* ret_type = call_inst.getParent()->getParent()->getReturnType();
- if (ret_type->isVoidTy()) {
- irb_.CreateRetVoid();
- } else {
- // The return value is ignored when there's an exception.
- irb_.CreateRet(llvm::UndefValue::get(ret_type));
- }
-
- irb_.SetInsertPoint(basic_block_cont);
- return;
-}
-
-void GBCExpanderPass::Expand_MarkGCCard(llvm::CallInst& call_inst) {
- irb_.Runtime().EmitMarkGCCard(call_inst.getArgOperand(0), call_inst.getArgOperand(1));
- return;
-}
-
-llvm::Value*
-GBCExpanderPass::Expand_LoadStringFromDexCache(llvm::Value* string_idx_value) {
- uint32_t string_idx =
- llvm::cast<llvm::ConstantInt>(string_idx_value)->getZExtValue();
-
- llvm::Value* string_field_addr = EmitLoadDexCacheStringFieldAddr(string_idx);
-
- return irb_.CreateLoad(string_field_addr, kTBAARuntimeInfo);
-}
-
-llvm::Value*
-GBCExpanderPass::Expand_LoadTypeFromDexCache(llvm::Value* type_idx_value) {
- uint32_t type_idx =
- llvm::cast<llvm::ConstantInt>(type_idx_value)->getZExtValue();
-
- llvm::Value* type_field_addr =
- EmitLoadDexCacheResolvedTypeFieldAddr(type_idx);
-
- return irb_.CreateLoad(type_field_addr, kTBAARuntimeInfo);
-}
-
-void GBCExpanderPass::Expand_LockObject(llvm::Value* obj) {
- rtb_.EmitLockObject(obj);
- return;
-}
-
-void GBCExpanderPass::Expand_UnlockObject(llvm::Value* obj) {
- rtb_.EmitUnlockObject(obj);
- return;
-}
-
-llvm::Value* GBCExpanderPass::Expand_ArrayGet(llvm::Value* array_addr,
- llvm::Value* index_value,
- JType elem_jty) {
- llvm::Value* array_elem_addr =
- EmitArrayGEP(array_addr, index_value, elem_jty);
-
- return irb_.CreateLoad(array_elem_addr, kTBAAHeapArray, elem_jty);
-}
-
-void GBCExpanderPass::Expand_ArrayPut(llvm::Value* new_value,
- llvm::Value* array_addr,
- llvm::Value* index_value,
- JType elem_jty) {
- llvm::Value* array_elem_addr =
- EmitArrayGEP(array_addr, index_value, elem_jty);
-
- irb_.CreateStore(new_value, array_elem_addr, kTBAAHeapArray, elem_jty);
-
- return;
-}
-
-void GBCExpanderPass::Expand_FilledNewArray(llvm::CallInst& call_inst) {
- // Most of the codes refer to MethodCompiler::EmitInsn_FilledNewArray
- llvm::Value* array = call_inst.getArgOperand(0);
-
- uint32_t element_jty =
- llvm::cast<llvm::ConstantInt>(call_inst.getArgOperand(1))->getZExtValue();
-
- DCHECK_GT(call_inst.getNumArgOperands(), 2U);
- unsigned num_elements = (call_inst.getNumArgOperands() - 2);
-
- bool is_elem_int_ty = (static_cast<JType>(element_jty) == kInt);
-
- uint32_t alignment;
- llvm::Constant* elem_size;
- llvm::PointerType* field_type;
-
- // NOTE: Currently filled-new-array only supports 'L', '[', and 'I'
- // as the element, thus we are only checking 2 cases: primitive int and
- // non-primitive type.
- if (is_elem_int_ty) {
- alignment = sizeof(int32_t);
- elem_size = irb_.getPtrEquivInt(sizeof(int32_t));
- field_type = irb_.getJIntTy()->getPointerTo();
- } else {
- alignment = irb_.getSizeOfPtrEquivInt();
- elem_size = irb_.getSizeOfPtrEquivIntValue();
- field_type = irb_.getJObjectTy()->getPointerTo();
- }
-
- llvm::Value* data_field_offset =
- irb_.getPtrEquivInt(art::mirror::Array::DataOffset(alignment).Int32Value());
-
- llvm::Value* data_field_addr =
- irb_.CreatePtrDisp(array, data_field_offset, field_type);
-
- for (unsigned i = 0; i < num_elements; ++i) {
- // Values to fill the array begin at the 3rd argument
- llvm::Value* reg_value = call_inst.getArgOperand(2 + i);
-
- irb_.CreateStore(reg_value, data_field_addr, kTBAAHeapArray);
-
- data_field_addr =
- irb_.CreatePtrDisp(data_field_addr, elem_size, field_type);
- }
-
- return;
-}
-
-llvm::Value* GBCExpanderPass::Expand_IGetFast(llvm::Value* field_offset_value,
- llvm::Value* /*is_volatile_value*/,
- llvm::Value* object_addr,
- JType field_jty) {
- int field_offset =
- llvm::cast<llvm::ConstantInt>(field_offset_value)->getSExtValue();
-
- DCHECK_GE(field_offset, 0);
-
- llvm::PointerType* field_type =
- irb_.getJType(field_jty)->getPointerTo();
-
- field_offset_value = irb_.getPtrEquivInt(field_offset);
-
- llvm::Value* field_addr =
- irb_.CreatePtrDisp(object_addr, field_offset_value, field_type);
-
- // TODO: Check is_volatile. We need to generate atomic load instruction
- // when is_volatile is true.
- return irb_.CreateLoad(field_addr, kTBAAHeapInstance, field_jty);
-}
-
-void GBCExpanderPass::Expand_IPutFast(llvm::Value* field_offset_value,
- llvm::Value* /* is_volatile_value */,
- llvm::Value* object_addr,
- llvm::Value* new_value,
- JType field_jty) {
- int field_offset =
- llvm::cast<llvm::ConstantInt>(field_offset_value)->getSExtValue();
-
- DCHECK_GE(field_offset, 0);
-
- llvm::PointerType* field_type =
- irb_.getJType(field_jty)->getPointerTo();
-
- field_offset_value = irb_.getPtrEquivInt(field_offset);
-
- llvm::Value* field_addr =
- irb_.CreatePtrDisp(object_addr, field_offset_value, field_type);
-
- // TODO: Check is_volatile. We need to generate atomic store instruction
- // when is_volatile is true.
- irb_.CreateStore(new_value, field_addr, kTBAAHeapInstance, field_jty);
-
- return;
-}
-
-llvm::Value* GBCExpanderPass::Expand_SGetFast(llvm::Value* static_storage_addr,
- llvm::Value* field_offset_value,
- llvm::Value* /*is_volatile_value*/,
- JType field_jty) {
- int field_offset =
- llvm::cast<llvm::ConstantInt>(field_offset_value)->getSExtValue();
-
- DCHECK_GE(field_offset, 0);
-
- llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset);
-
- llvm::Value* static_field_addr =
- irb_.CreatePtrDisp(static_storage_addr, static_field_offset_value,
- irb_.getJType(field_jty)->getPointerTo());
-
- // TODO: Check is_volatile. We need to generate atomic store instruction
- // when is_volatile is true.
- return irb_.CreateLoad(static_field_addr, kTBAAHeapStatic, field_jty);
-}
-
-void GBCExpanderPass::Expand_SPutFast(llvm::Value* static_storage_addr,
- llvm::Value* field_offset_value,
- llvm::Value* /* is_volatile_value */,
- llvm::Value* new_value,
- JType field_jty) {
- int field_offset =
- llvm::cast<llvm::ConstantInt>(field_offset_value)->getSExtValue();
-
- DCHECK_GE(field_offset, 0);
-
- llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset);
-
- llvm::Value* static_field_addr =
- irb_.CreatePtrDisp(static_storage_addr, static_field_offset_value,
- irb_.getJType(field_jty)->getPointerTo());
-
- // TODO: Check is_volatile. We need to generate atomic store instruction
- // when is_volatile is true.
- irb_.CreateStore(new_value, static_field_addr, kTBAAHeapStatic, field_jty);
-
- return;
-}
-
-llvm::Value*
-GBCExpanderPass::Expand_LoadDeclaringClassSSB(llvm::Value* method_object_addr) {
- return irb_.LoadFromObjectOffset(method_object_addr,
- art::mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
- irb_.getJObjectTy(),
- kTBAAConstJObject);
-}
-
-llvm::Value*
-GBCExpanderPass::Expand_GetSDCalleeMethodObjAddrFast(llvm::Value* callee_method_idx_value) {
- uint32_t callee_method_idx =
- llvm::cast<llvm::ConstantInt>(callee_method_idx_value)->getZExtValue();
-
- return EmitLoadSDCalleeMethodObjectAddr(callee_method_idx);
-}
-
-llvm::Value* GBCExpanderPass::Expand_GetVirtualCalleeMethodObjAddrFast(
- llvm::Value* vtable_idx_value,
- llvm::Value* this_addr) {
- int vtable_idx =
- llvm::cast<llvm::ConstantInt>(vtable_idx_value)->getSExtValue();
-
- return EmitLoadVirtualCalleeMethodObjectAddr(vtable_idx, this_addr);
-}
-
-llvm::Value* GBCExpanderPass::Expand_Invoke(llvm::CallInst& call_inst) {
- // Most of the codes refer to MethodCompiler::EmitInsn_Invoke
- llvm::Value* callee_method_object_addr = call_inst.getArgOperand(0);
- unsigned num_args = call_inst.getNumArgOperands();
- llvm::Type* ret_type = call_inst.getType();
-
- // Determine the function type of the callee method
- std::vector<llvm::Type*> args_type;
- std::vector<llvm::Value*> args;
- for (unsigned i = 0; i < num_args; i++) {
- args.push_back(call_inst.getArgOperand(i));
- args_type.push_back(args[i]->getType());
- }
-
- llvm::FunctionType* callee_method_type =
- llvm::FunctionType::get(ret_type, args_type, false);
-
- llvm::Value* code_addr =
- irb_.LoadFromObjectOffset(callee_method_object_addr,
- art::mirror::ArtMethod::EntryPointFromPortableCompiledCodeOffset().Int32Value(),
- callee_method_type->getPointerTo(),
- kTBAARuntimeInfo);
-
- // Invoke callee
- llvm::Value* retval = irb_.CreateCall(code_addr, args);
-
- return retval;
-}
-
-llvm::Value* GBCExpanderPass::Expand_DivRem(llvm::CallInst& call_inst,
- bool is_div, JType op_jty) {
- llvm::Value* dividend = call_inst.getArgOperand(0);
- llvm::Value* divisor = call_inst.getArgOperand(1);
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- EmitGuard_DivZeroException(dex_pc, divisor, op_jty);
- // Most of the codes refer to MethodCompiler::EmitIntDivRemResultComputation
-
- // Check the special case: MININT / -1 = MININT
- // That case will cause overflow, which is undefined behavior in llvm.
- // So we check the divisor is -1 or not, if the divisor is -1, we do
- // the special path to avoid undefined behavior.
- llvm::Type* op_type = irb_.getJType(op_jty);
- llvm::Value* zero = irb_.getJZero(op_jty);
- llvm::Value* neg_one = llvm::ConstantInt::getSigned(op_type, -1);
-
- llvm::Function* parent = irb_.GetInsertBlock()->getParent();
- llvm::BasicBlock* eq_neg_one = llvm::BasicBlock::Create(context_, "", parent);
- llvm::BasicBlock* ne_neg_one = llvm::BasicBlock::Create(context_, "", parent);
- llvm::BasicBlock* neg_one_cont =
- llvm::BasicBlock::Create(context_, "", parent);
-
- llvm::Value* is_equal_neg_one = irb_.CreateICmpEQ(divisor, neg_one);
- irb_.CreateCondBr(is_equal_neg_one, eq_neg_one, ne_neg_one, kUnlikely);
-
- // If divisor == -1
- irb_.SetInsertPoint(eq_neg_one);
- llvm::Value* eq_result;
- if (is_div) {
- // We can just change from "dividend div -1" to "neg dividend". The sub
- // don't care the sign/unsigned because of two's complement representation.
- // And the behavior is what we want:
- // -(2^n) (2^n)-1
- // MININT < k <= MAXINT -> mul k -1 = -k
- // MININT == k -> mul k -1 = k
- //
- // LLVM use sub to represent 'neg'
- eq_result = irb_.CreateSub(zero, dividend);
- } else {
- // Everything modulo -1 will be 0.
- eq_result = zero;
- }
- irb_.CreateBr(neg_one_cont);
-
- // If divisor != -1, just do the division.
- irb_.SetInsertPoint(ne_neg_one);
- llvm::Value* ne_result;
- if (is_div) {
- ne_result = irb_.CreateSDiv(dividend, divisor);
- } else {
- ne_result = irb_.CreateSRem(dividend, divisor);
- }
- irb_.CreateBr(neg_one_cont);
-
- irb_.SetInsertPoint(neg_one_cont);
- llvm::PHINode* result = irb_.CreatePHI(op_type, 2);
- result->addIncoming(eq_result, eq_neg_one);
- result->addIncoming(ne_result, ne_neg_one);
-
- return result;
-}
-
-void GBCExpanderPass::Expand_AllocaShadowFrame(llvm::Value* num_vregs_value) {
- // Most of the codes refer to MethodCompiler::EmitPrologueAllocShadowFrame and
- // MethodCompiler::EmitPushShadowFrame
- uint16_t num_vregs =
- llvm::cast<llvm::ConstantInt>(num_vregs_value)->getZExtValue();
-
- llvm::StructType* shadow_frame_type =
- irb_.getShadowFrameTy(num_vregs);
-
- // Create allocas at the start of entry block.
- llvm::IRBuilderBase::InsertPoint irb_ip_original = irb_.saveIP();
- llvm::BasicBlock* entry_block = &func_->front();
- irb_.SetInsertPoint(&entry_block->front());
-
- shadow_frame_ = irb_.CreateAlloca(shadow_frame_type);
-
- // Alloca a pointer to old shadow frame
- old_shadow_frame_ =
- irb_.CreateAlloca(shadow_frame_type->getElementType(0)->getPointerTo());
-
- irb_.restoreIP(irb_ip_original);
-
- // Push the shadow frame
- llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
- llvm::Value* shadow_frame_upcast =
- irb_.CreateConstGEP2_32(shadow_frame_, 0, 0);
-
- llvm::Value* result = rtb_.EmitPushShadowFrame(shadow_frame_upcast,
- method_object_addr,
- num_vregs);
-
- irb_.CreateStore(result, old_shadow_frame_, kTBAARegister);
-
- return;
-}
-
-void GBCExpanderPass::Expand_SetVReg(llvm::Value* entry_idx,
- llvm::Value* value) {
- unsigned vreg_idx = LV2UInt(entry_idx);
- DCHECK_LT(vreg_idx, dex_compilation_unit_->GetCodeItem()->registers_size_);
-
- llvm::Value* vreg_addr = shadow_frame_vreg_addresses_[vreg_idx];
- if (UNLIKELY(vreg_addr == NULL)) {
- DCHECK(shadow_frame_ != NULL);
-
- llvm::Value* gep_index[] = {
- irb_.getInt32(0), // No pointer displacement
- irb_.getInt32(1), // VRegs
- entry_idx // Pointer field
- };
-
- // A shadow frame address must dominate every use in the function so we
- // place it in the entry block right after the allocas.
- llvm::BasicBlock::iterator first_non_alloca = func_->getEntryBlock().begin();
- while (llvm::isa<llvm::AllocaInst>(first_non_alloca)) {
- ++first_non_alloca;
- }
-
- llvm::IRBuilderBase::InsertPoint ip = irb_.saveIP();
- irb_.SetInsertPoint(static_cast<llvm::Instruction*>(first_non_alloca));
- vreg_addr = irb_.CreateGEP(shadow_frame_, gep_index);
- shadow_frame_vreg_addresses_[vreg_idx] = vreg_addr;
- irb_.restoreIP(ip);
- }
-
- irb_.CreateStore(value,
- irb_.CreateBitCast(vreg_addr, value->getType()->getPointerTo()),
- kTBAAShadowFrame);
- return;
-}
-
-void GBCExpanderPass::Expand_PopShadowFrame() {
- if (old_shadow_frame_ == NULL) {
- return;
- }
- rtb_.EmitPopShadowFrame(irb_.CreateLoad(old_shadow_frame_, kTBAARegister));
- return;
-}
-
-void GBCExpanderPass::Expand_UpdateDexPC(llvm::Value* dex_pc_value) {
- irb_.StoreToObjectOffset(shadow_frame_,
- art::ShadowFrame::DexPCOffset(),
- dex_pc_value,
- kTBAAShadowFrame);
- return;
-}
-
-void GBCExpanderPass::InsertStackOverflowCheck(llvm::Function& func) {
- // All alloca instructions are generated in the first basic block of the
- // function, and there are no alloca instructions after the first non-alloca
- // instruction.
-
- llvm::BasicBlock* first_basic_block = &func.front();
-
- // Look for first non-alloca instruction
- llvm::BasicBlock::iterator first_non_alloca = first_basic_block->begin();
- while (llvm::isa<llvm::AllocaInst>(first_non_alloca)) {
- ++first_non_alloca;
- }
-
- irb_.SetInsertPoint(first_non_alloca);
-
- // Insert stack overflow check codes before first_non_alloca (i.e., after all
- // alloca instructions)
- EmitStackOverflowCheck(&*first_non_alloca);
-
- irb_.Runtime().EmitTestSuspend();
-
- llvm::BasicBlock* next_basic_block = irb_.GetInsertBlock();
- if (next_basic_block != first_basic_block) {
- // Splice the rest of the instruction to the continuing basic block
- next_basic_block->getInstList().splice(
- irb_.GetInsertPoint(), first_basic_block->getInstList(),
- first_non_alloca, first_basic_block->end());
-
- // Rewrite the basic block
- RewriteBasicBlock(next_basic_block);
-
- // Update the phi-instructions in the successor basic block
- UpdatePhiInstruction(first_basic_block, irb_.GetInsertBlock());
- }
-
- // We have changed the basic block
- changed_ = true;
-}
-
-// ==== High-level intrinsic expander ==========================================
-
-llvm::Value* GBCExpanderPass::Expand_FPCompare(llvm::Value* src1_value,
- llvm::Value* src2_value,
- bool gt_bias) {
- llvm::Value* cmp_eq = irb_.CreateFCmpOEQ(src1_value, src2_value);
- llvm::Value* cmp_lt;
-
- if (gt_bias) {
- cmp_lt = irb_.CreateFCmpOLT(src1_value, src2_value);
- } else {
- cmp_lt = irb_.CreateFCmpULT(src1_value, src2_value);
- }
-
- return EmitCompareResultSelection(cmp_eq, cmp_lt);
-}
-
-llvm::Value* GBCExpanderPass::Expand_LongCompare(llvm::Value* src1_value, llvm::Value* src2_value) {
- llvm::Value* cmp_eq = irb_.CreateICmpEQ(src1_value, src2_value);
- llvm::Value* cmp_lt = irb_.CreateICmpSLT(src1_value, src2_value);
-
- return EmitCompareResultSelection(cmp_eq, cmp_lt);
-}
-
-llvm::Value* GBCExpanderPass::EmitCompareResultSelection(llvm::Value* cmp_eq,
- llvm::Value* cmp_lt) {
- llvm::Constant* zero = irb_.getJInt(0);
- llvm::Constant* pos1 = irb_.getJInt(1);
- llvm::Constant* neg1 = irb_.getJInt(-1);
-
- llvm::Value* result_lt = irb_.CreateSelect(cmp_lt, neg1, pos1);
- llvm::Value* result_eq = irb_.CreateSelect(cmp_eq, zero, result_lt);
-
- return result_eq;
-}
-
-llvm::Value* GBCExpanderPass::Expand_IntegerShift(llvm::Value* src1_value,
- llvm::Value* src2_value,
- IntegerShiftKind kind,
- JType op_jty) {
- DCHECK(op_jty == kInt || op_jty == kLong);
-
- // Mask and zero-extend RHS properly
- if (op_jty == kInt) {
- src2_value = irb_.CreateAnd(src2_value, 0x1f);
- } else {
- llvm::Value* masked_src2_value = irb_.CreateAnd(src2_value, 0x3f);
- src2_value = irb_.CreateZExt(masked_src2_value, irb_.getJLongTy());
- }
-
- // Create integer shift llvm instruction
- switch (kind) {
- case kIntegerSHL:
- return irb_.CreateShl(src1_value, src2_value);
-
- case kIntegerSHR:
- return irb_.CreateAShr(src1_value, src2_value);
-
- case kIntegerUSHR:
- return irb_.CreateLShr(src1_value, src2_value);
-
- default:
- LOG(FATAL) << "Unknown integer shift kind: " << kind;
- return NULL;
- }
-}
-
-llvm::Value* GBCExpanderPass::SignOrZeroExtendCat1Types(llvm::Value* value, JType jty) {
- switch (jty) {
- case kBoolean:
- case kChar:
- return irb_.CreateZExt(value, irb_.getJType(kInt));
- case kByte:
- case kShort:
- return irb_.CreateSExt(value, irb_.getJType(kInt));
- case kVoid:
- case kInt:
- case kLong:
- case kFloat:
- case kDouble:
- case kObject:
- return value; // Nothing to do.
- default:
- LOG(FATAL) << "Unknown java type: " << jty;
- return NULL;
- }
-}
-
-llvm::Value* GBCExpanderPass::TruncateCat1Types(llvm::Value* value, JType jty) {
- switch (jty) {
- case kBoolean:
- case kChar:
- case kByte:
- case kShort:
- return irb_.CreateTrunc(value, irb_.getJType(jty));
- case kVoid:
- case kInt:
- case kLong:
- case kFloat:
- case kDouble:
- case kObject:
- return value; // Nothing to do.
- default:
- LOG(FATAL) << "Unknown java type: " << jty;
- return NULL;
- }
-}
-
-llvm::Value* GBCExpanderPass::Expand_HLArrayGet(llvm::CallInst& call_inst,
- JType elem_jty) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- llvm::Value* array_addr = call_inst.getArgOperand(1);
- llvm::Value* index_value = call_inst.getArgOperand(2);
- int opt_flags = LV2UInt(call_inst.getArgOperand(0));
-
- EmitGuard_NullPointerException(dex_pc, array_addr, opt_flags);
- EmitGuard_ArrayIndexOutOfBoundsException(dex_pc, array_addr, index_value,
- opt_flags);
-
- llvm::Value* array_elem_addr = EmitArrayGEP(array_addr, index_value, elem_jty);
-
- llvm::Value* array_elem_value = irb_.CreateLoad(array_elem_addr, kTBAAHeapArray, elem_jty);
-
- return SignOrZeroExtendCat1Types(array_elem_value, elem_jty);
-}
-
-
-void GBCExpanderPass::Expand_HLArrayPut(llvm::CallInst& call_inst,
- JType elem_jty) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- llvm::Value* new_value = call_inst.getArgOperand(1);
- llvm::Value* array_addr = call_inst.getArgOperand(2);
- llvm::Value* index_value = call_inst.getArgOperand(3);
- int opt_flags = LV2UInt(call_inst.getArgOperand(0));
-
- EmitGuard_NullPointerException(dex_pc, array_addr, opt_flags);
- EmitGuard_ArrayIndexOutOfBoundsException(dex_pc, array_addr, index_value,
- opt_flags);
-
- new_value = TruncateCat1Types(new_value, elem_jty);
-
- llvm::Value* array_elem_addr = EmitArrayGEP(array_addr, index_value, elem_jty);
-
- if (elem_jty == kObject) { // If put an object, check the type, and mark GC card table.
- llvm::Function* runtime_func = irb_.GetRuntime(CheckPutArrayElement);
-
- irb_.CreateCall2(runtime_func, new_value, array_addr);
-
- EmitGuard_ExceptionLandingPad(dex_pc);
-
- EmitMarkGCCard(new_value, array_addr);
- }
-
- irb_.CreateStore(new_value, array_elem_addr, kTBAAHeapArray, elem_jty);
-
- return;
-}
-
-llvm::Value* GBCExpanderPass::Expand_HLIGet(llvm::CallInst& call_inst,
- JType field_jty) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- llvm::Value* object_addr = call_inst.getArgOperand(1);
- uint32_t field_idx = LV2UInt(call_inst.getArgOperand(2));
- int opt_flags = LV2UInt(call_inst.getArgOperand(0));
-
- EmitGuard_NullPointerException(dex_pc, object_addr, opt_flags);
-
- llvm::Value* field_value;
-
- art::MemberOffset field_offset(0u);
- bool is_volatile;
- bool is_fast_path = driver_->ComputeInstanceFieldInfo(
- field_idx, dex_compilation_unit_, false, &field_offset, &is_volatile);
-
- if (!is_fast_path) {
- llvm::Function* runtime_func;
-
- if (field_jty == kObject) {
- runtime_func = irb_.GetRuntime(GetObjectInstance);
- } else if (field_jty == kLong || field_jty == kDouble) {
- runtime_func = irb_.GetRuntime(Get64Instance);
- } else {
- runtime_func = irb_.GetRuntime(Get32Instance);
- }
-
- llvm::ConstantInt* field_idx_value = irb_.getInt32(field_idx);
-
- llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
- EmitUpdateDexPC(dex_pc);
-
- field_value = irb_.CreateCall3(runtime_func, field_idx_value,
- method_object_addr, object_addr);
-
- EmitGuard_ExceptionLandingPad(dex_pc);
-
- if (field_jty == kFloat || field_jty == kDouble) {
- field_value = irb_.CreateBitCast(field_value, irb_.getJType(field_jty));
- }
- } else {
- DCHECK_GE(field_offset.Int32Value(), 0);
-
- llvm::PointerType* field_type =
- irb_.getJType(field_jty)->getPointerTo();
-
- llvm::ConstantInt* field_offset_value = irb_.getPtrEquivInt(field_offset.Int32Value());
-
- llvm::Value* field_addr =
- irb_.CreatePtrDisp(object_addr, field_offset_value, field_type);
-
- field_value = irb_.CreateLoad(field_addr, kTBAAHeapInstance, field_jty);
- field_value = SignOrZeroExtendCat1Types(field_value, field_jty);
-
- if (is_volatile) {
- irb_.CreateMemoryBarrier(art::kLoadAny);
- }
- }
-
- return field_value;
-}
-
-void GBCExpanderPass::Expand_HLIPut(llvm::CallInst& call_inst,
- JType field_jty) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- llvm::Value* new_value = call_inst.getArgOperand(1);
- llvm::Value* object_addr = call_inst.getArgOperand(2);
- uint32_t field_idx = LV2UInt(call_inst.getArgOperand(3));
- int opt_flags = LV2UInt(call_inst.getArgOperand(0));
-
- EmitGuard_NullPointerException(dex_pc, object_addr, opt_flags);
-
- art::MemberOffset field_offset(0u);
- bool is_volatile;
- bool is_fast_path = driver_->ComputeInstanceFieldInfo(
- field_idx, dex_compilation_unit_, true, &field_offset, &is_volatile);
-
- if (!is_fast_path) {
- llvm::Function* runtime_func;
-
- if (field_jty == kFloat) {
- new_value = irb_.CreateBitCast(new_value, irb_.getJType(kInt));
- } else if (field_jty == kDouble) {
- new_value = irb_.CreateBitCast(new_value, irb_.getJType(kLong));
- }
-
- if (field_jty == kObject) {
- runtime_func = irb_.GetRuntime(SetObjectInstance);
- } else if (field_jty == kLong || field_jty == kDouble) {
- runtime_func = irb_.GetRuntime(Set64Instance);
- } else {
- runtime_func = irb_.GetRuntime(Set32Instance);
- }
-
- llvm::Value* field_idx_value = irb_.getInt32(field_idx);
-
- llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
- EmitUpdateDexPC(dex_pc);
-
- irb_.CreateCall4(runtime_func, field_idx_value,
- method_object_addr, object_addr, new_value);
-
- EmitGuard_ExceptionLandingPad(dex_pc);
-
- } else {
- DCHECK_GE(field_offset.Int32Value(), 0);
-
- if (is_volatile) {
- irb_.CreateMemoryBarrier(art::kAnyStore);
- }
-
- llvm::PointerType* field_type =
- irb_.getJType(field_jty)->getPointerTo();
-
- llvm::Value* field_offset_value = irb_.getPtrEquivInt(field_offset.Int32Value());
-
- llvm::Value* field_addr =
- irb_.CreatePtrDisp(object_addr, field_offset_value, field_type);
-
- new_value = TruncateCat1Types(new_value, field_jty);
- irb_.CreateStore(new_value, field_addr, kTBAAHeapInstance, field_jty);
-
- if (is_volatile) {
- irb_.CreateMemoryBarrier(art::kAnyAny);
- }
-
- if (field_jty == kObject) { // If put an object, mark the GC card table.
- EmitMarkGCCard(new_value, object_addr);
- }
- }
-
- return;
-}
-
-llvm::Value* GBCExpanderPass::EmitLoadConstantClass(uint32_t dex_pc,
- uint32_t type_idx) {
- if (!driver_->CanAccessTypeWithoutChecks(dex_compilation_unit_->GetDexMethodIndex(),
- *dex_compilation_unit_->GetDexFile(), type_idx)) {
- llvm::Value* type_idx_value = irb_.getInt32(type_idx);
-
- llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
- llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread();
-
- llvm::Function* runtime_func = irb_.GetRuntime(InitializeTypeAndVerifyAccess);
-
- EmitUpdateDexPC(dex_pc);
-
- llvm::Value* type_object_addr =
- irb_.CreateCall3(runtime_func, type_idx_value, method_object_addr, thread_object_addr);
-
- EmitGuard_ExceptionLandingPad(dex_pc);
-
- return type_object_addr;
-
- } else {
- // Try to load the class (type) object from the test cache.
- llvm::Value* type_field_addr =
- EmitLoadDexCacheResolvedTypeFieldAddr(type_idx);
-
- llvm::Value* type_object_addr = irb_.CreateLoad(type_field_addr, kTBAARuntimeInfo);
-
- if (driver_->CanAssumeTypeIsPresentInDexCache(*dex_compilation_unit_->GetDexFile(), type_idx)) {
- return type_object_addr;
- }
-
- llvm::BasicBlock* block_original = irb_.GetInsertBlock();
-
- // Test whether class (type) object is in the dex cache or not
- llvm::Value* equal_null =
- irb_.CreateICmpEQ(type_object_addr, irb_.getJNull());
-
- llvm::BasicBlock* block_cont =
- CreateBasicBlockWithDexPC(dex_pc, "cont");
-
- llvm::BasicBlock* block_load_class =
- CreateBasicBlockWithDexPC(dex_pc, "load_class");
-
- irb_.CreateCondBr(equal_null, block_load_class, block_cont, kUnlikely);
-
- // Failback routine to load the class object
- irb_.SetInsertPoint(block_load_class);
-
- llvm::Function* runtime_func = irb_.GetRuntime(InitializeType);
-
- llvm::Constant* type_idx_value = irb_.getInt32(type_idx);
-
- llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
- llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread();
-
- EmitUpdateDexPC(dex_pc);
-
- llvm::Value* loaded_type_object_addr =
- irb_.CreateCall3(runtime_func, type_idx_value, method_object_addr, thread_object_addr);
-
- EmitGuard_ExceptionLandingPad(dex_pc);
-
- llvm::BasicBlock* block_after_load_class = irb_.GetInsertBlock();
-
- irb_.CreateBr(block_cont);
-
- // Now the class object must be loaded
- irb_.SetInsertPoint(block_cont);
-
- llvm::PHINode* phi = irb_.CreatePHI(irb_.getJObjectTy(), 2);
-
- phi->addIncoming(type_object_addr, block_original);
- phi->addIncoming(loaded_type_object_addr, block_after_load_class);
-
- return phi;
- }
-}
-
-llvm::Value* GBCExpanderPass::EmitLoadStaticStorage(uint32_t dex_pc,
- uint32_t type_idx) {
- llvm::BasicBlock* block_load_static =
- CreateBasicBlockWithDexPC(dex_pc, "load_static");
-
- llvm::BasicBlock* block_check_init = CreateBasicBlockWithDexPC(dex_pc, "init");
- llvm::BasicBlock* block_cont = CreateBasicBlockWithDexPC(dex_pc, "cont");
-
- // Load static storage from dex cache
- llvm::Value* storage_field_addr = EmitLoadDexCacheResolvedTypeFieldAddr(type_idx);
-
- llvm::Value* storage_object_addr = irb_.CreateLoad(storage_field_addr, kTBAARuntimeInfo);
-
- // Test: Is the class resolved?
- llvm::Value* equal_null = irb_.CreateICmpEQ(storage_object_addr, irb_.getJNull());
-
- irb_.CreateCondBr(equal_null, block_load_static, block_check_init, kUnlikely);
-
- // storage_object_addr != null, so check if its initialized.
- irb_.SetInsertPoint(block_check_init);
-
- llvm::Value* class_status =
- irb_.LoadFromObjectOffset(storage_object_addr,
- art::mirror::Class::StatusOffset().Int32Value(),
- irb_.getJIntTy(), kTBAAHeapInstance);
-
- llvm::Value* is_not_initialized =
- irb_.CreateICmpULT(class_status, irb_.getInt32(art::mirror::Class::kStatusInitialized));
-
- irb_.CreateCondBr(is_not_initialized, block_load_static, block_cont, kUnlikely);
-
- // Failback routine to load the class object
- irb_.SetInsertPoint(block_load_static);
-
- llvm::Function* runtime_func = irb_.GetRuntime(InitializeStaticStorage);
-
- llvm::Constant* type_idx_value = irb_.getInt32(type_idx);
-
- llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
- llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread();
-
- EmitUpdateDexPC(dex_pc);
-
- llvm::Value* loaded_storage_object_addr =
- irb_.CreateCall3(runtime_func, type_idx_value, method_object_addr, thread_object_addr);
-
- EmitGuard_ExceptionLandingPad(dex_pc);
-
- llvm::BasicBlock* block_after_load_static = irb_.GetInsertBlock();
-
- irb_.CreateBr(block_cont);
-
- // Now the class object must be loaded
- irb_.SetInsertPoint(block_cont);
-
- llvm::PHINode* phi = irb_.CreatePHI(irb_.getJObjectTy(), 2);
-
- phi->addIncoming(storage_object_addr, block_check_init);
- phi->addIncoming(loaded_storage_object_addr, block_after_load_static);
-
- // Ensure load of status and load of value don't re-order.
- irb_.CreateMemoryBarrier(art::kLoadAny);
-
- return phi;
-}
-
-llvm::Value* GBCExpanderPass::Expand_HLSget(llvm::CallInst& call_inst,
- JType field_jty) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- uint32_t field_idx = LV2UInt(call_inst.getArgOperand(0));
-
- art::MemberOffset field_offset(0u);
- uint32_t ssb_index;
- bool is_referrers_class;
- bool is_volatile;
- bool is_initialized;
-
- bool is_fast_path = driver_->ComputeStaticFieldInfo(
- field_idx, dex_compilation_unit_, false,
- &field_offset, &ssb_index, &is_referrers_class, &is_volatile, &is_initialized);
-
- llvm::Value* static_field_value;
-
- if (!is_fast_path) {
- llvm::Function* runtime_func;
-
- if (field_jty == kObject) {
- runtime_func = irb_.GetRuntime(GetObjectStatic);
- } else if (field_jty == kLong || field_jty == kDouble) {
- runtime_func = irb_.GetRuntime(Get64Static);
- } else {
- runtime_func = irb_.GetRuntime(Get32Static);
- }
-
- llvm::Constant* field_idx_value = irb_.getInt32(field_idx);
-
- llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
- EmitUpdateDexPC(dex_pc);
-
- static_field_value =
- irb_.CreateCall2(runtime_func, field_idx_value, method_object_addr);
-
- EmitGuard_ExceptionLandingPad(dex_pc);
-
- if (field_jty == kFloat || field_jty == kDouble) {
- static_field_value = irb_.CreateBitCast(static_field_value, irb_.getJType(field_jty));
- }
- } else {
- DCHECK_GE(field_offset.Int32Value(), 0);
-
- llvm::Value* static_storage_addr = NULL;
-
- if (is_referrers_class) {
- // Fast path, static storage base is this method's class
- llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
- static_storage_addr =
- irb_.LoadFromObjectOffset(method_object_addr,
- art::mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
- irb_.getJObjectTy(),
- kTBAAConstJObject);
- } else {
- // Medium path, static storage base in a different class which
- // requires checks that the other class is initialized
- DCHECK_NE(ssb_index, art::DexFile::kDexNoIndex);
- static_storage_addr = EmitLoadStaticStorage(dex_pc, ssb_index);
- }
-
- llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset.Int32Value());
-
- llvm::Value* static_field_addr =
- irb_.CreatePtrDisp(static_storage_addr, static_field_offset_value,
- irb_.getJType(field_jty)->getPointerTo());
-
- static_field_value = irb_.CreateLoad(static_field_addr, kTBAAHeapStatic, field_jty);
- static_field_value = SignOrZeroExtendCat1Types(static_field_value, field_jty);
-
- if (is_volatile) {
- irb_.CreateMemoryBarrier(art::kLoadAny);
- }
- }
-
- return static_field_value;
-}
-
-void GBCExpanderPass::Expand_HLSput(llvm::CallInst& call_inst,
- JType field_jty) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- uint32_t field_idx = LV2UInt(call_inst.getArgOperand(0));
- llvm::Value* new_value = call_inst.getArgOperand(1);
-
- if (field_jty == kFloat || field_jty == kDouble) {
- new_value = irb_.CreateBitCast(new_value, irb_.getJType(field_jty));
- }
-
- art::MemberOffset field_offset(0u);
- uint32_t ssb_index;
- bool is_referrers_class;
- bool is_volatile;
- bool is_initialized;
-
- bool is_fast_path = driver_->ComputeStaticFieldInfo(
- field_idx, dex_compilation_unit_, true,
- &field_offset, &ssb_index, &is_referrers_class, &is_volatile, &is_initialized);
-
- if (!is_fast_path) {
- llvm::Function* runtime_func;
-
- if (field_jty == kObject) {
- runtime_func = irb_.GetRuntime(SetObjectStatic);
- } else if (field_jty == kLong || field_jty == kDouble) {
- runtime_func = irb_.GetRuntime(Set64Static);
- } else {
- runtime_func = irb_.GetRuntime(Set32Static);
- }
-
- if (field_jty == kFloat) {
- new_value = irb_.CreateBitCast(new_value, irb_.getJType(kInt));
- } else if (field_jty == kDouble) {
- new_value = irb_.CreateBitCast(new_value, irb_.getJType(kLong));
- }
-
- llvm::Constant* field_idx_value = irb_.getInt32(field_idx);
-
- llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
- EmitUpdateDexPC(dex_pc);
-
- irb_.CreateCall3(runtime_func, field_idx_value,
- method_object_addr, new_value);
-
- EmitGuard_ExceptionLandingPad(dex_pc);
-
- } else {
- DCHECK_GE(field_offset.Int32Value(), 0);
-
- llvm::Value* static_storage_addr = NULL;
-
- if (is_referrers_class) {
- // Fast path, static storage base is this method's class
- llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
- static_storage_addr =
- irb_.LoadFromObjectOffset(method_object_addr,
- art::mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
- irb_.getJObjectTy(),
- kTBAAConstJObject);
- } else {
- // Medium path, static storage base in a different class which
- // requires checks that the other class is initialized
- DCHECK_NE(ssb_index, art::DexFile::kDexNoIndex);
- static_storage_addr = EmitLoadStaticStorage(dex_pc, ssb_index);
- }
-
- if (is_volatile) {
- irb_.CreateMemoryBarrier(art::kAnyStore);
- }
-
- llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset.Int32Value());
-
- llvm::Value* static_field_addr =
- irb_.CreatePtrDisp(static_storage_addr, static_field_offset_value,
- irb_.getJType(field_jty)->getPointerTo());
-
- new_value = TruncateCat1Types(new_value, field_jty);
- irb_.CreateStore(new_value, static_field_addr, kTBAAHeapStatic, field_jty);
-
- if (is_volatile) {
- irb_.CreateMemoryBarrier(art::kAnyAny);
- }
-
- if (field_jty == kObject) { // If put an object, mark the GC card table.
- EmitMarkGCCard(new_value, static_storage_addr);
- }
- }
-
- return;
-}
-
-llvm::Value* GBCExpanderPass::Expand_ConstString(llvm::CallInst& call_inst) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- uint32_t string_idx = LV2UInt(call_inst.getArgOperand(0));
-
- llvm::Value* string_field_addr = EmitLoadDexCacheStringFieldAddr(string_idx);
-
- llvm::Value* string_addr = irb_.CreateLoad(string_field_addr, kTBAARuntimeInfo);
-
- if (!driver_->CanAssumeStringIsPresentInDexCache(*dex_compilation_unit_->GetDexFile(),
- string_idx)) {
- llvm::BasicBlock* block_str_exist =
- CreateBasicBlockWithDexPC(dex_pc, "str_exist");
-
- llvm::BasicBlock* block_str_resolve =
- CreateBasicBlockWithDexPC(dex_pc, "str_resolve");
-
- llvm::BasicBlock* block_cont =
- CreateBasicBlockWithDexPC(dex_pc, "str_cont");
-
- // Test: Is the string resolved and in the dex cache?
- llvm::Value* equal_null = irb_.CreateICmpEQ(string_addr, irb_.getJNull());
-
- irb_.CreateCondBr(equal_null, block_str_resolve, block_str_exist, kUnlikely);
-
- // String is resolved, go to next basic block.
- irb_.SetInsertPoint(block_str_exist);
- irb_.CreateBr(block_cont);
-
- // String is not resolved yet, resolve it now.
- irb_.SetInsertPoint(block_str_resolve);
-
- llvm::Function* runtime_func = irb_.GetRuntime(ResolveString);
-
- llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
- llvm::Value* string_idx_value = irb_.getInt32(string_idx);
-
- EmitUpdateDexPC(dex_pc);
-
- llvm::Value* result = irb_.CreateCall2(runtime_func, method_object_addr,
- string_idx_value);
-
- EmitGuard_ExceptionLandingPad(dex_pc);
-
- irb_.CreateBr(block_cont);
-
-
- llvm::BasicBlock* block_pre_cont = irb_.GetInsertBlock();
-
- irb_.SetInsertPoint(block_cont);
-
- llvm::PHINode* phi = irb_.CreatePHI(irb_.getJObjectTy(), 2);
-
- phi->addIncoming(string_addr, block_str_exist);
- phi->addIncoming(result, block_pre_cont);
-
- string_addr = phi;
- }
-
- return string_addr;
-}
-
-llvm::Value* GBCExpanderPass::Expand_ConstClass(llvm::CallInst& call_inst) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- uint32_t type_idx = LV2UInt(call_inst.getArgOperand(0));
-
- llvm::Value* type_object_addr = EmitLoadConstantClass(dex_pc, type_idx);
-
- return type_object_addr;
-}
-
-void GBCExpanderPass::Expand_MonitorEnter(llvm::CallInst& call_inst) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- llvm::Value* object_addr = call_inst.getArgOperand(1);
- int opt_flags = LV2UInt(call_inst.getArgOperand(0));
-
- EmitGuard_NullPointerException(dex_pc, object_addr, opt_flags);
-
- EmitUpdateDexPC(dex_pc);
-
- irb_.Runtime().EmitLockObject(object_addr);
-
- return;
-}
-
-void GBCExpanderPass::Expand_MonitorExit(llvm::CallInst& call_inst) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- llvm::Value* object_addr = call_inst.getArgOperand(1);
- int opt_flags = LV2UInt(call_inst.getArgOperand(0));
-
- EmitGuard_NullPointerException(dex_pc, object_addr, opt_flags);
-
- EmitUpdateDexPC(dex_pc);
-
- irb_.Runtime().EmitUnlockObject(object_addr);
-
- EmitGuard_ExceptionLandingPad(dex_pc);
-
- return;
-}
-
-void GBCExpanderPass::Expand_HLCheckCast(llvm::CallInst& call_inst) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- uint32_t type_idx = LV2UInt(call_inst.getArgOperand(0));
- llvm::Value* object_addr = call_inst.getArgOperand(1);
-
- llvm::BasicBlock* block_test_class =
- CreateBasicBlockWithDexPC(dex_pc, "test_class");
-
- llvm::BasicBlock* block_test_sub_class =
- CreateBasicBlockWithDexPC(dex_pc, "test_sub_class");
-
- llvm::BasicBlock* block_cont =
- CreateBasicBlockWithDexPC(dex_pc, "checkcast_cont");
-
- // Test: Is the reference equal to null? Act as no-op when it is null.
- llvm::Value* equal_null = irb_.CreateICmpEQ(object_addr, irb_.getJNull());
-
- irb_.CreateCondBr(equal_null, block_cont, block_test_class, kUnlikely);
-
- // Test: Is the object instantiated from the given class?
- irb_.SetInsertPoint(block_test_class);
- llvm::Value* type_object_addr = EmitLoadConstantClass(dex_pc, type_idx);
- DCHECK_EQ(art::mirror::Object::ClassOffset().Int32Value(), 0);
-
- llvm::PointerType* jobject_ptr_ty = irb_.getJObjectTy();
-
- llvm::Value* object_type_field_addr =
- irb_.CreateBitCast(object_addr, jobject_ptr_ty->getPointerTo());
-
- llvm::Value* object_type_object_addr =
- irb_.CreateLoad(object_type_field_addr, kTBAAConstJObject);
-
- llvm::Value* equal_class =
- irb_.CreateICmpEQ(type_object_addr, object_type_object_addr);
-
- irb_.CreateCondBr(equal_class, block_cont, block_test_sub_class, kLikely);
-
- // Test: Is the object instantiated from the subclass of the given class?
- irb_.SetInsertPoint(block_test_sub_class);
-
- EmitUpdateDexPC(dex_pc);
-
- irb_.CreateCall2(irb_.GetRuntime(CheckCast),
- type_object_addr, object_type_object_addr);
-
- EmitGuard_ExceptionLandingPad(dex_pc);
-
- irb_.CreateBr(block_cont);
-
- irb_.SetInsertPoint(block_cont);
-
- return;
-}
-
-llvm::Value* GBCExpanderPass::Expand_InstanceOf(llvm::CallInst& call_inst) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- uint32_t type_idx = LV2UInt(call_inst.getArgOperand(0));
- llvm::Value* object_addr = call_inst.getArgOperand(1);
-
- llvm::BasicBlock* block_nullp =
- CreateBasicBlockWithDexPC(dex_pc, "nullp");
-
- llvm::BasicBlock* block_test_class =
- CreateBasicBlockWithDexPC(dex_pc, "test_class");
-
- llvm::BasicBlock* block_class_equals =
- CreateBasicBlockWithDexPC(dex_pc, "class_eq");
-
- llvm::BasicBlock* block_test_sub_class =
- CreateBasicBlockWithDexPC(dex_pc, "test_sub_class");
-
- llvm::BasicBlock* block_cont =
- CreateBasicBlockWithDexPC(dex_pc, "instance_of_cont");
-
- // Overview of the following code :
- // We check for null, if so, then false, otherwise check for class == . If so
- // then true, otherwise do callout slowpath.
- //
- // Test: Is the reference equal to null? Set 0 when it is null.
- llvm::Value* equal_null = irb_.CreateICmpEQ(object_addr, irb_.getJNull());
-
- irb_.CreateCondBr(equal_null, block_nullp, block_test_class, kUnlikely);
-
- irb_.SetInsertPoint(block_nullp);
- irb_.CreateBr(block_cont);
-
- // Test: Is the object instantiated from the given class?
- irb_.SetInsertPoint(block_test_class);
- llvm::Value* type_object_addr = EmitLoadConstantClass(dex_pc, type_idx);
- DCHECK_EQ(art::mirror::Object::ClassOffset().Int32Value(), 0);
-
- llvm::PointerType* jobject_ptr_ty = irb_.getJObjectTy();
-
- llvm::Value* object_type_field_addr =
- irb_.CreateBitCast(object_addr, jobject_ptr_ty->getPointerTo());
-
- llvm::Value* object_type_object_addr =
- irb_.CreateLoad(object_type_field_addr, kTBAAConstJObject);
-
- llvm::Value* equal_class =
- irb_.CreateICmpEQ(type_object_addr, object_type_object_addr);
-
- irb_.CreateCondBr(equal_class, block_class_equals, block_test_sub_class, kLikely);
-
- irb_.SetInsertPoint(block_class_equals);
- irb_.CreateBr(block_cont);
-
- // Test: Is the object instantiated from the subclass of the given class?
- irb_.SetInsertPoint(block_test_sub_class);
- llvm::Value* result =
- irb_.CreateCall2(irb_.GetRuntime(IsAssignable),
- type_object_addr, object_type_object_addr);
- irb_.CreateBr(block_cont);
-
- irb_.SetInsertPoint(block_cont);
-
- llvm::PHINode* phi = irb_.CreatePHI(irb_.getJIntTy(), 3);
-
- phi->addIncoming(irb_.getJInt(0), block_nullp);
- phi->addIncoming(irb_.getJInt(1), block_class_equals);
- phi->addIncoming(result, block_test_sub_class);
-
- return phi;
-}
-
-llvm::Value* GBCExpanderPass::Expand_NewInstance(llvm::CallInst& call_inst) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- uint32_t type_idx = LV2UInt(call_inst.getArgOperand(0));
-
- llvm::Function* runtime_func;
- if (driver_->CanAccessInstantiableTypeWithoutChecks(dex_compilation_unit_->GetDexMethodIndex(),
- *dex_compilation_unit_->GetDexFile(),
- type_idx)) {
- runtime_func = irb_.GetRuntime(AllocObject);
- } else {
- runtime_func = irb_.GetRuntime(AllocObjectWithAccessCheck);
- }
-
- llvm::Constant* type_index_value = irb_.getInt32(type_idx);
-
- llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
- llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread();
-
- EmitUpdateDexPC(dex_pc);
-
- llvm::Value* object_addr =
- irb_.CreateCall3(runtime_func, type_index_value, method_object_addr, thread_object_addr);
-
- EmitGuard_ExceptionLandingPad(dex_pc);
-
- return object_addr;
-}
-
-llvm::Value* GBCExpanderPass::Expand_HLInvoke(llvm::CallInst& call_inst) {
- art::InvokeType invoke_type = static_cast<art::InvokeType>(LV2UInt(call_inst.getArgOperand(0)));
- bool is_static = (invoke_type == art::kStatic);
-
- if (!is_static) {
- // Test: Is *this* parameter equal to null?
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- llvm::Value* this_addr = call_inst.getArgOperand(3);
- int opt_flags = LV2UInt(call_inst.getArgOperand(2));
-
- EmitGuard_NullPointerException(dex_pc, this_addr, opt_flags);
- }
-
- llvm::Value* result = NULL;
- if (EmitIntrinsic(call_inst, &result)) {
- return result;
- }
-
- return EmitInvoke(call_inst);
-}
-
-llvm::Value* GBCExpanderPass::Expand_OptArrayLength(llvm::CallInst& call_inst) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- // Get the array object address
- llvm::Value* array_addr = call_inst.getArgOperand(1);
- int opt_flags = LV2UInt(call_inst.getArgOperand(0));
-
- EmitGuard_NullPointerException(dex_pc, array_addr, opt_flags);
-
- // Get the array length and store it to the register
- return EmitLoadArrayLength(array_addr);
-}
-
-llvm::Value* GBCExpanderPass::Expand_NewArray(llvm::CallInst& call_inst) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- uint32_t type_idx = LV2UInt(call_inst.getArgOperand(0));
- llvm::Value* length = call_inst.getArgOperand(1);
-
- return EmitAllocNewArray(dex_pc, length, type_idx, false);
-}
-
-llvm::Value* GBCExpanderPass::Expand_HLFilledNewArray(llvm::CallInst& call_inst) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- uint32_t type_idx = LV2UInt(call_inst.getArgOperand(1));
- uint32_t length = call_inst.getNumArgOperands() - 3;
-
- llvm::Value* object_addr =
- EmitAllocNewArray(dex_pc, irb_.getInt32(length), type_idx, true);
-
- if (length > 0) {
- // Check for the element type
- uint32_t type_desc_len = 0;
- const char* type_desc =
- dex_compilation_unit_->GetDexFile()->StringByTypeIdx(type_idx, &type_desc_len);
-
- DCHECK_GE(type_desc_len, 2u); // should be guaranteed by verifier
- DCHECK_EQ(type_desc[0], '['); // should be guaranteed by verifier
- bool is_elem_int_ty = (type_desc[1] == 'I');
-
- uint32_t alignment;
- llvm::Constant* elem_size;
- llvm::PointerType* field_type;
-
- // NOTE: Currently filled-new-array only supports 'L', '[', and 'I'
- // as the element, thus we are only checking 2 cases: primitive int and
- // non-primitive type.
- if (is_elem_int_ty) {
- alignment = sizeof(int32_t);
- elem_size = irb_.getPtrEquivInt(sizeof(int32_t));
- field_type = irb_.getJIntTy()->getPointerTo();
- } else {
- alignment = irb_.getSizeOfPtrEquivInt();
- elem_size = irb_.getSizeOfPtrEquivIntValue();
- field_type = irb_.getJObjectTy()->getPointerTo();
- }
-
- llvm::Value* data_field_offset =
- irb_.getPtrEquivInt(art::mirror::Array::DataOffset(alignment).Int32Value());
-
- llvm::Value* data_field_addr =
- irb_.CreatePtrDisp(object_addr, data_field_offset, field_type);
-
- // TODO: Tune this code. Currently we are generating one instruction for
- // one element which may be very space consuming. Maybe changing to use
- // memcpy may help; however, since we can't guarantee that the alloca of
- // dalvik register are continuous, we can't perform such optimization yet.
- for (uint32_t i = 0; i < length; ++i) {
- llvm::Value* reg_value = call_inst.getArgOperand(i+3);
-
- irb_.CreateStore(reg_value, data_field_addr, kTBAAHeapArray);
-
- data_field_addr =
- irb_.CreatePtrDisp(data_field_addr, elem_size, field_type);
- }
- }
-
- return object_addr;
-}
-
-void GBCExpanderPass::Expand_HLFillArrayData(llvm::CallInst& call_inst) {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
- int32_t payload_offset = static_cast<int32_t>(dex_pc) +
- LV2SInt(call_inst.getArgOperand(0));
- llvm::Value* array_addr = call_inst.getArgOperand(1);
-
- const art::Instruction::ArrayDataPayload* payload =
- reinterpret_cast<const art::Instruction::ArrayDataPayload*>(
- dex_compilation_unit_->GetCodeItem()->insns_ + payload_offset);
-
- if (payload->element_count == 0) {
- // When the number of the elements in the payload is zero, we don't have
- // to copy any numbers. However, we should check whether the array object
- // address is equal to null or not.
- EmitGuard_NullPointerException(dex_pc, array_addr, 0);
- } else {
- // To save the code size, we are going to call the runtime function to
- // copy the content from DexFile.
-
- // NOTE: We will check for the NullPointerException in the runtime.
-
- llvm::Function* runtime_func = irb_.GetRuntime(FillArrayData);
-
- llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
- EmitUpdateDexPC(dex_pc);
-
- irb_.CreateCall4(runtime_func,
- method_object_addr, irb_.getInt32(dex_pc),
- array_addr, irb_.getInt32(payload_offset));
-
- EmitGuard_ExceptionLandingPad(dex_pc);
- }
-
- return;
-}
-
-llvm::Value* GBCExpanderPass::EmitAllocNewArray(uint32_t dex_pc,
- llvm::Value* array_length_value,
- uint32_t type_idx,
- bool is_filled_new_array) {
- llvm::Function* runtime_func;
-
- bool skip_access_check =
- driver_->CanAccessTypeWithoutChecks(dex_compilation_unit_->GetDexMethodIndex(),
- *dex_compilation_unit_->GetDexFile(), type_idx);
-
-
- if (is_filled_new_array) {
- runtime_func = skip_access_check ?
- irb_.GetRuntime(CheckAndAllocArray) :
- irb_.GetRuntime(CheckAndAllocArrayWithAccessCheck);
- } else {
- runtime_func = skip_access_check ?
- irb_.GetRuntime(AllocArray) :
- irb_.GetRuntime(AllocArrayWithAccessCheck);
- }
-
- llvm::Constant* type_index_value = irb_.getInt32(type_idx);
-
- llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
- llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread();
-
- EmitUpdateDexPC(dex_pc);
-
- llvm::Value* object_addr =
- irb_.CreateCall4(runtime_func, type_index_value, method_object_addr,
- array_length_value, thread_object_addr);
-
- EmitGuard_ExceptionLandingPad(dex_pc);
-
- return object_addr;
-}
-
-llvm::Value* GBCExpanderPass::
-EmitCallRuntimeForCalleeMethodObjectAddr(uint32_t callee_method_idx,
- art::InvokeType invoke_type,
- llvm::Value* this_addr,
- uint32_t dex_pc,
- bool is_fast_path) {
- llvm::Function* runtime_func = NULL;
-
- switch (invoke_type) {
- case art::kStatic:
- runtime_func = irb_.GetRuntime(FindStaticMethodWithAccessCheck);
- break;
-
- case art::kDirect:
- runtime_func = irb_.GetRuntime(FindDirectMethodWithAccessCheck);
- break;
-
- case art::kVirtual:
- runtime_func = irb_.GetRuntime(FindVirtualMethodWithAccessCheck);
- break;
-
- case art::kSuper:
- runtime_func = irb_.GetRuntime(FindSuperMethodWithAccessCheck);
- break;
-
- case art::kInterface:
- if (is_fast_path) {
- runtime_func = irb_.GetRuntime(FindInterfaceMethod);
- } else {
- runtime_func = irb_.GetRuntime(FindInterfaceMethodWithAccessCheck);
- }
- break;
- }
-
- llvm::Value* callee_method_idx_value = irb_.getInt32(callee_method_idx);
-
- if (this_addr == NULL) {
- DCHECK_EQ(invoke_type, art::kStatic);
- this_addr = irb_.getJNull();
- }
-
- llvm::Value* caller_method_object_addr = EmitLoadMethodObjectAddr();
-
- llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread();
-
- EmitUpdateDexPC(dex_pc);
-
- llvm::Value* callee_method_object_addr =
- irb_.CreateCall4(runtime_func,
- callee_method_idx_value,
- this_addr,
- caller_method_object_addr,
- thread_object_addr);
-
- EmitGuard_ExceptionLandingPad(dex_pc);
-
- return callee_method_object_addr;
-}
-
-void GBCExpanderPass::EmitMarkGCCard(llvm::Value* value, llvm::Value* target_addr) {
- // Using runtime support, let the target can override by InlineAssembly.
- irb_.Runtime().EmitMarkGCCard(value, target_addr);
-}
-
-void GBCExpanderPass::EmitUpdateDexPC(uint32_t dex_pc) {
- if (shadow_frame_ == NULL) {
- return;
- }
- irb_.StoreToObjectOffset(shadow_frame_,
- art::ShadowFrame::DexPCOffset(),
- irb_.getInt32(dex_pc),
- kTBAAShadowFrame);
-}
-
-void GBCExpanderPass::EmitGuard_DivZeroException(uint32_t dex_pc,
- llvm::Value* denominator,
- JType op_jty) {
- DCHECK(op_jty == kInt || op_jty == kLong) << op_jty;
-
- llvm::Constant* zero = irb_.getJZero(op_jty);
-
- llvm::Value* equal_zero = irb_.CreateICmpEQ(denominator, zero);
-
- llvm::BasicBlock* block_exception = CreateBasicBlockWithDexPC(dex_pc, "div0");
-
- llvm::BasicBlock* block_continue = CreateBasicBlockWithDexPC(dex_pc, "cont");
-
- irb_.CreateCondBr(equal_zero, block_exception, block_continue, kUnlikely);
-
- irb_.SetInsertPoint(block_exception);
- EmitUpdateDexPC(dex_pc);
- irb_.CreateCall(irb_.GetRuntime(ThrowDivZeroException));
- EmitBranchExceptionLandingPad(dex_pc);
-
- irb_.SetInsertPoint(block_continue);
-}
-
-void GBCExpanderPass::EmitGuard_NullPointerException(uint32_t dex_pc,
- llvm::Value* object,
- int opt_flags) {
- bool ignore_null_check = ((opt_flags & MIR_IGNORE_NULL_CHECK) != 0);
- if (ignore_null_check) {
- llvm::BasicBlock* lpad = GetLandingPadBasicBlock(dex_pc);
- if (lpad) {
- // There is at least one catch: create a "fake" conditional branch to
- // keep the exception edge to the catch block.
- landing_pad_phi_mapping_[lpad].push_back(
- std::make_pair(current_bb_->getUniquePredecessor(),
- irb_.GetInsertBlock()));
-
- llvm::BasicBlock* block_continue =
- CreateBasicBlockWithDexPC(dex_pc, "cont");
-
- irb_.CreateCondBr(irb_.getFalse(), lpad, block_continue, kUnlikely);
-
- irb_.SetInsertPoint(block_continue);
- }
- } else {
- llvm::Value* equal_null = irb_.CreateICmpEQ(object, irb_.getJNull());
-
- llvm::BasicBlock* block_exception =
- CreateBasicBlockWithDexPC(dex_pc, "nullp");
-
- llvm::BasicBlock* block_continue =
- CreateBasicBlockWithDexPC(dex_pc, "cont");
-
- irb_.CreateCondBr(equal_null, block_exception, block_continue, kUnlikely);
-
- irb_.SetInsertPoint(block_exception);
- EmitUpdateDexPC(dex_pc);
- irb_.CreateCall(irb_.GetRuntime(ThrowNullPointerException),
- irb_.getInt32(dex_pc));
- EmitBranchExceptionLandingPad(dex_pc);
-
- irb_.SetInsertPoint(block_continue);
- }
-}
-
-void
-GBCExpanderPass::EmitGuard_ArrayIndexOutOfBoundsException(uint32_t dex_pc,
- llvm::Value* array,
- llvm::Value* index,
- int opt_flags) {
- bool ignore_range_check = ((opt_flags & MIR_IGNORE_RANGE_CHECK) != 0);
- if (ignore_range_check) {
- llvm::BasicBlock* lpad = GetLandingPadBasicBlock(dex_pc);
- if (lpad) {
- // There is at least one catch: create a "fake" conditional branch to
- // keep the exception edge to the catch block.
- landing_pad_phi_mapping_[lpad].push_back(
- std::make_pair(current_bb_->getUniquePredecessor(),
- irb_.GetInsertBlock()));
-
- llvm::BasicBlock* block_continue =
- CreateBasicBlockWithDexPC(dex_pc, "cont");
-
- irb_.CreateCondBr(irb_.getFalse(), lpad, block_continue, kUnlikely);
-
- irb_.SetInsertPoint(block_continue);
- }
- } else {
- llvm::Value* array_len = EmitLoadArrayLength(array);
-
- llvm::Value* cmp = irb_.CreateICmpUGE(index, array_len);
-
- llvm::BasicBlock* block_exception =
- CreateBasicBlockWithDexPC(dex_pc, "overflow");
-
- llvm::BasicBlock* block_continue =
- CreateBasicBlockWithDexPC(dex_pc, "cont");
-
- irb_.CreateCondBr(cmp, block_exception, block_continue, kUnlikely);
-
- irb_.SetInsertPoint(block_exception);
-
- EmitUpdateDexPC(dex_pc);
- irb_.CreateCall2(irb_.GetRuntime(ThrowIndexOutOfBounds), index, array_len);
- EmitBranchExceptionLandingPad(dex_pc);
-
- irb_.SetInsertPoint(block_continue);
- }
-}
-
-llvm::FunctionType* GBCExpanderPass::GetFunctionType(llvm::Type* ret_type, uint32_t method_idx,
- bool is_static) {
- // Get method signature
- art::DexFile::MethodId const& method_id =
- dex_compilation_unit_->GetDexFile()->GetMethodId(method_idx);
-
- uint32_t shorty_size;
- const char* shorty = dex_compilation_unit_->GetDexFile()->GetMethodShorty(method_id, &shorty_size);
- CHECK_GE(shorty_size, 1u);
-
- // Get argument type
- std::vector<llvm::Type*> args_type;
-
- args_type.push_back(irb_.getJObjectTy()); // method object pointer
-
- if (!is_static) {
- args_type.push_back(irb_.getJType('L')); // "this" object pointer
- }
-
- for (uint32_t i = 1; i < shorty_size; ++i) {
- char shorty_type = art::RemapShorty(shorty[i]);
- args_type.push_back(irb_.getJType(shorty_type));
- }
-
- return llvm::FunctionType::get(ret_type, args_type, false);
-}
-
-
-llvm::BasicBlock* GBCExpanderPass::
-CreateBasicBlockWithDexPC(uint32_t dex_pc, const char* postfix) {
- std::string name;
-
-#if !defined(NDEBUG)
- art::StringAppendF(&name, "B%04x.%s", dex_pc, postfix);
-#endif
-
- return llvm::BasicBlock::Create(context_, name, func_);
-}
-
-llvm::BasicBlock* GBCExpanderPass::GetBasicBlock(uint32_t dex_pc) {
- DCHECK(dex_pc < dex_compilation_unit_->GetCodeItem()->insns_size_in_code_units_);
- CHECK(basic_blocks_[dex_pc] != NULL);
- return basic_blocks_[dex_pc];
-}
-
-int32_t GBCExpanderPass::GetTryItemOffset(uint32_t dex_pc) {
- int32_t min = 0;
- int32_t max = dex_compilation_unit_->GetCodeItem()->tries_size_ - 1;
-
- while (min <= max) {
- int32_t mid = min + (max - min) / 2;
-
- const art::DexFile::TryItem* ti =
- art::DexFile::GetTryItems(*dex_compilation_unit_->GetCodeItem(), mid);
- uint32_t start = ti->start_addr_;
- uint32_t end = start + ti->insn_count_;
-
- if (dex_pc < start) {
- max = mid - 1;
- } else if (dex_pc >= end) {
- min = mid + 1;
- } else {
- return mid; // found
- }
- }
-
- return -1; // not found
-}
-
-llvm::BasicBlock* GBCExpanderPass::GetLandingPadBasicBlock(uint32_t dex_pc) {
- // Find the try item for this address in this method
- int32_t ti_offset = GetTryItemOffset(dex_pc);
-
- if (ti_offset == -1) {
- return NULL; // No landing pad is available for this address.
- }
-
- // Check for the existing landing pad basic block
- DCHECK_GT(basic_block_landing_pads_.size(), static_cast<size_t>(ti_offset));
- llvm::BasicBlock* block_lpad = basic_block_landing_pads_[ti_offset];
-
- if (block_lpad) {
- // We have generated landing pad for this try item already. Return the
- // same basic block.
- return block_lpad;
- }
-
- // Get try item from code item
- const art::DexFile::TryItem* ti = art::DexFile::GetTryItems(*dex_compilation_unit_->GetCodeItem(),
- ti_offset);
-
- std::string lpadname;
-
-#if !defined(NDEBUG)
- art::StringAppendF(&lpadname, "lpad%d_%04x_to_%04x", ti_offset, ti->start_addr_, ti->handler_off_);
-#endif
-
- // Create landing pad basic block
- block_lpad = llvm::BasicBlock::Create(context_, lpadname, func_);
-
- // Change IRBuilder insert point
- llvm::IRBuilderBase::InsertPoint irb_ip_original = irb_.saveIP();
- irb_.SetInsertPoint(block_lpad);
-
- // Find catch block with matching type
- llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
- llvm::Value* ti_offset_value = irb_.getInt32(ti_offset);
-
- llvm::Value* catch_handler_index_value =
- irb_.CreateCall2(irb_.GetRuntime(FindCatchBlock),
- method_object_addr, ti_offset_value);
-
- // Switch instruction (Go to unwind basic block by default)
- llvm::SwitchInst* sw =
- irb_.CreateSwitch(catch_handler_index_value, GetUnwindBasicBlock());
-
- // Cases with matched catch block
- art::CatchHandlerIterator iter(*dex_compilation_unit_->GetCodeItem(), ti->start_addr_);
-
- for (uint32_t c = 0; iter.HasNext(); iter.Next(), ++c) {
- sw->addCase(irb_.getInt32(c), GetBasicBlock(iter.GetHandlerAddress()));
- }
-
- // Restore the orignal insert point for IRBuilder
- irb_.restoreIP(irb_ip_original);
-
- // Cache this landing pad
- DCHECK_GT(basic_block_landing_pads_.size(), static_cast<size_t>(ti_offset));
- basic_block_landing_pads_[ti_offset] = block_lpad;
-
- return block_lpad;
-}
-
-llvm::BasicBlock* GBCExpanderPass::GetUnwindBasicBlock() {
- // Check the existing unwinding baisc block block
- if (basic_block_unwind_ != NULL) {
- return basic_block_unwind_;
- }
-
- // Create new basic block for unwinding
- basic_block_unwind_ =
- llvm::BasicBlock::Create(context_, "exception_unwind", func_);
-
- // Change IRBuilder insert point
- llvm::IRBuilderBase::InsertPoint irb_ip_original = irb_.saveIP();
- irb_.SetInsertPoint(basic_block_unwind_);
-
- // Pop the shadow frame
- Expand_PopShadowFrame();
-
- // Emit the code to return default value (zero) for the given return type.
- char ret_shorty = dex_compilation_unit_->GetShorty()[0];
- ret_shorty = art::RemapShorty(ret_shorty);
- if (ret_shorty == 'V') {
- irb_.CreateRetVoid();
- } else {
- irb_.CreateRet(irb_.getJZero(ret_shorty));
- }
-
- // Restore the orignal insert point for IRBuilder
- irb_.restoreIP(irb_ip_original);
-
- return basic_block_unwind_;
-}
-
-void GBCExpanderPass::EmitBranchExceptionLandingPad(uint32_t dex_pc) {
- if (llvm::BasicBlock* lpad = GetLandingPadBasicBlock(dex_pc)) {
- landing_pad_phi_mapping_[lpad].push_back(std::make_pair(current_bb_->getUniquePredecessor(),
- irb_.GetInsertBlock()));
- irb_.CreateBr(lpad);
- } else {
- irb_.CreateBr(GetUnwindBasicBlock());
- }
-}
-
-void GBCExpanderPass::EmitGuard_ExceptionLandingPad(uint32_t dex_pc) {
- llvm::Value* exception_pending = irb_.Runtime().EmitIsExceptionPending();
-
- llvm::BasicBlock* block_cont = CreateBasicBlockWithDexPC(dex_pc, "cont");
-
- if (llvm::BasicBlock* lpad = GetLandingPadBasicBlock(dex_pc)) {
- landing_pad_phi_mapping_[lpad].push_back(std::make_pair(current_bb_->getUniquePredecessor(),
- irb_.GetInsertBlock()));
- irb_.CreateCondBr(exception_pending, lpad, block_cont, kUnlikely);
- } else {
- irb_.CreateCondBr(exception_pending, GetUnwindBasicBlock(), block_cont, kUnlikely);
- }
-
- irb_.SetInsertPoint(block_cont);
-}
-
-llvm::Value*
-GBCExpanderPass::ExpandIntrinsic(IntrinsicHelper::IntrinsicId intr_id,
- llvm::CallInst& call_inst) {
- switch (intr_id) {
- //==- Thread -----------------------------------------------------------==//
- case IntrinsicHelper::GetCurrentThread: {
- return irb_.Runtime().EmitGetCurrentThread();
- }
- case IntrinsicHelper::CheckSuspend: {
- Expand_TestSuspend(call_inst);
- return NULL;
- }
- case IntrinsicHelper::TestSuspend: {
- Expand_TestSuspend(call_inst);
- return NULL;
- }
- case IntrinsicHelper::MarkGCCard: {
- Expand_MarkGCCard(call_inst);
- return NULL;
- }
-
- //==- Exception --------------------------------------------------------==//
- case IntrinsicHelper::ThrowException: {
- return ExpandToRuntime(ThrowException, call_inst);
- }
- case IntrinsicHelper::HLThrowException: {
- uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-
- EmitUpdateDexPC(dex_pc);
-
- irb_.CreateCall(irb_.GetRuntime(ThrowException),
- call_inst.getArgOperand(0));
-
- EmitGuard_ExceptionLandingPad(dex_pc);
- return NULL;
- }
- case IntrinsicHelper::GetException: {
- return irb_.Runtime().EmitGetAndClearException();
- }
- case IntrinsicHelper::IsExceptionPending: {
- return irb_.Runtime().EmitIsExceptionPending();
- }
- case IntrinsicHelper::FindCatchBlock: {
- return ExpandToRuntime(FindCatchBlock, call_inst);
- }
- case IntrinsicHelper::ThrowDivZeroException: {
- return ExpandToRuntime(ThrowDivZeroException, call_inst);
- }
- case IntrinsicHelper::ThrowNullPointerException: {
- return ExpandToRuntime(ThrowNullPointerException, call_inst);
- }
- case IntrinsicHelper::ThrowIndexOutOfBounds: {
- return ExpandToRuntime(ThrowIndexOutOfBounds, call_inst);
- }
-
- //==- Const String -----------------------------------------------------==//
- case IntrinsicHelper::ConstString: {
- return Expand_ConstString(call_inst);
- }
- case IntrinsicHelper::LoadStringFromDexCache: {
- return Expand_LoadStringFromDexCache(call_inst.getArgOperand(0));
- }
- case IntrinsicHelper::ResolveString: {
- return ExpandToRuntime(ResolveString, call_inst);
- }
-
- //==- Const Class ------------------------------------------------------==//
- case IntrinsicHelper::ConstClass: {
- return Expand_ConstClass(call_inst);
- }
- case IntrinsicHelper::InitializeTypeAndVerifyAccess: {
- return ExpandToRuntime(InitializeTypeAndVerifyAccess, call_inst);
- }
- case IntrinsicHelper::LoadTypeFromDexCache: {
- return Expand_LoadTypeFromDexCache(call_inst.getArgOperand(0));
- }
- case IntrinsicHelper::InitializeType: {
- return ExpandToRuntime(InitializeType, call_inst);
- }
-
- //==- Lock -------------------------------------------------------------==//
- case IntrinsicHelper::LockObject: {
- Expand_LockObject(call_inst.getArgOperand(0));
- return NULL;
- }
- case IntrinsicHelper::UnlockObject: {
- Expand_UnlockObject(call_inst.getArgOperand(0));
- return NULL;
- }
-
- //==- Cast -------------------------------------------------------------==//
- case IntrinsicHelper::CheckCast: {
- return ExpandToRuntime(CheckCast, call_inst);
- }
- case IntrinsicHelper::HLCheckCast: {
- Expand_HLCheckCast(call_inst);
- return NULL;
- }
- case IntrinsicHelper::IsAssignable: {
- return ExpandToRuntime(IsAssignable, call_inst);
- }
-
- //==- Alloc ------------------------------------------------------------==//
- case IntrinsicHelper::AllocObject: {
- return ExpandToRuntime(AllocObject, call_inst);
- }
- case IntrinsicHelper::AllocObjectWithAccessCheck: {
- return ExpandToRuntime(AllocObjectWithAccessCheck, call_inst);
- }
-
- //==- Instance ---------------------------------------------------------==//
- case IntrinsicHelper::NewInstance: {
- return Expand_NewInstance(call_inst);
- }
- case IntrinsicHelper::InstanceOf: {
- return Expand_InstanceOf(call_inst);
- }
-
- //==- Array ------------------------------------------------------------==//
- case IntrinsicHelper::NewArray: {
- return Expand_NewArray(call_inst);
- }
- case IntrinsicHelper::OptArrayLength: {
- return Expand_OptArrayLength(call_inst);
- }
- case IntrinsicHelper::ArrayLength: {
- return EmitLoadArrayLength(call_inst.getArgOperand(0));
- }
- case IntrinsicHelper::AllocArray: {
- return ExpandToRuntime(AllocArray, call_inst);
- }
- case IntrinsicHelper::AllocArrayWithAccessCheck: {
- return ExpandToRuntime(AllocArrayWithAccessCheck,
- call_inst);
- }
- case IntrinsicHelper::CheckAndAllocArray: {
- return ExpandToRuntime(CheckAndAllocArray, call_inst);
- }
- case IntrinsicHelper::CheckAndAllocArrayWithAccessCheck: {
- return ExpandToRuntime(CheckAndAllocArrayWithAccessCheck,
- call_inst);
- }
- case IntrinsicHelper::ArrayGet: {
- return Expand_ArrayGet(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- kInt);
- }
- case IntrinsicHelper::ArrayGetWide: {
- return Expand_ArrayGet(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- kLong);
- }
- case IntrinsicHelper::ArrayGetObject: {
- return Expand_ArrayGet(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- kObject);
- }
- case IntrinsicHelper::ArrayGetBoolean: {
- return Expand_ArrayGet(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- kBoolean);
- }
- case IntrinsicHelper::ArrayGetByte: {
- return Expand_ArrayGet(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- kByte);
- }
- case IntrinsicHelper::ArrayGetChar: {
- return Expand_ArrayGet(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- kChar);
- }
- case IntrinsicHelper::ArrayGetShort: {
- return Expand_ArrayGet(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- kShort);
- }
- case IntrinsicHelper::ArrayPut: {
- Expand_ArrayPut(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kInt);
- return NULL;
- }
- case IntrinsicHelper::ArrayPutWide: {
- Expand_ArrayPut(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kLong);
- return NULL;
- }
- case IntrinsicHelper::ArrayPutObject: {
- Expand_ArrayPut(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kObject);
- return NULL;
- }
- case IntrinsicHelper::ArrayPutBoolean: {
- Expand_ArrayPut(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kBoolean);
- return NULL;
- }
- case IntrinsicHelper::ArrayPutByte: {
- Expand_ArrayPut(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kByte);
- return NULL;
- }
- case IntrinsicHelper::ArrayPutChar: {
- Expand_ArrayPut(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kChar);
- return NULL;
- }
- case IntrinsicHelper::ArrayPutShort: {
- Expand_ArrayPut(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kShort);
- return NULL;
- }
- case IntrinsicHelper::CheckPutArrayElement: {
- return ExpandToRuntime(CheckPutArrayElement, call_inst);
- }
- case IntrinsicHelper::FilledNewArray: {
- Expand_FilledNewArray(call_inst);
- return NULL;
- }
- case IntrinsicHelper::FillArrayData: {
- return ExpandToRuntime(FillArrayData, call_inst);
- }
- case IntrinsicHelper::HLFillArrayData: {
- Expand_HLFillArrayData(call_inst);
- return NULL;
- }
- case IntrinsicHelper::HLFilledNewArray: {
- return Expand_HLFilledNewArray(call_inst);
- }
-
- //==- Instance Field ---------------------------------------------------==//
- case IntrinsicHelper::InstanceFieldGet:
- case IntrinsicHelper::InstanceFieldGetBoolean:
- case IntrinsicHelper::InstanceFieldGetByte:
- case IntrinsicHelper::InstanceFieldGetChar:
- case IntrinsicHelper::InstanceFieldGetShort: {
- return ExpandToRuntime(Get32Instance, call_inst);
- }
- case IntrinsicHelper::InstanceFieldGetWide: {
- return ExpandToRuntime(Get64Instance, call_inst);
- }
- case IntrinsicHelper::InstanceFieldGetObject: {
- return ExpandToRuntime(GetObjectInstance, call_inst);
- }
- case IntrinsicHelper::InstanceFieldGetFast: {
- return Expand_IGetFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kInt);
- }
- case IntrinsicHelper::InstanceFieldGetWideFast: {
- return Expand_IGetFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kLong);
- }
- case IntrinsicHelper::InstanceFieldGetObjectFast: {
- return Expand_IGetFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kObject);
- }
- case IntrinsicHelper::InstanceFieldGetBooleanFast: {
- return Expand_IGetFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kBoolean);
- }
- case IntrinsicHelper::InstanceFieldGetByteFast: {
- return Expand_IGetFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kByte);
- }
- case IntrinsicHelper::InstanceFieldGetCharFast: {
- return Expand_IGetFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kChar);
- }
- case IntrinsicHelper::InstanceFieldGetShortFast: {
- return Expand_IGetFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kShort);
- }
- case IntrinsicHelper::InstanceFieldPut:
- case IntrinsicHelper::InstanceFieldPutBoolean:
- case IntrinsicHelper::InstanceFieldPutByte:
- case IntrinsicHelper::InstanceFieldPutChar:
- case IntrinsicHelper::InstanceFieldPutShort: {
- return ExpandToRuntime(Set32Instance, call_inst);
- }
- case IntrinsicHelper::InstanceFieldPutWide: {
- return ExpandToRuntime(Set64Instance, call_inst);
- }
- case IntrinsicHelper::InstanceFieldPutObject: {
- return ExpandToRuntime(SetObjectInstance, call_inst);
- }
- case IntrinsicHelper::InstanceFieldPutFast: {
- Expand_IPutFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- call_inst.getArgOperand(3),
- kInt);
- return NULL;
- }
- case IntrinsicHelper::InstanceFieldPutWideFast: {
- Expand_IPutFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- call_inst.getArgOperand(3),
- kLong);
- return NULL;
- }
- case IntrinsicHelper::InstanceFieldPutObjectFast: {
- Expand_IPutFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- call_inst.getArgOperand(3),
- kObject);
- return NULL;
- }
- case IntrinsicHelper::InstanceFieldPutBooleanFast: {
- Expand_IPutFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- call_inst.getArgOperand(3),
- kBoolean);
- return NULL;
- }
- case IntrinsicHelper::InstanceFieldPutByteFast: {
- Expand_IPutFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- call_inst.getArgOperand(3),
- kByte);
- return NULL;
- }
- case IntrinsicHelper::InstanceFieldPutCharFast: {
- Expand_IPutFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- call_inst.getArgOperand(3),
- kChar);
- return NULL;
- }
- case IntrinsicHelper::InstanceFieldPutShortFast: {
- Expand_IPutFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- call_inst.getArgOperand(3),
- kShort);
- return NULL;
- }
-
- //==- Static Field -----------------------------------------------------==//
- case IntrinsicHelper::StaticFieldGet:
- case IntrinsicHelper::StaticFieldGetBoolean:
- case IntrinsicHelper::StaticFieldGetByte:
- case IntrinsicHelper::StaticFieldGetChar:
- case IntrinsicHelper::StaticFieldGetShort: {
- return ExpandToRuntime(Get32Static, call_inst);
- }
- case IntrinsicHelper::StaticFieldGetWide: {
- return ExpandToRuntime(Get64Static, call_inst);
- }
- case IntrinsicHelper::StaticFieldGetObject: {
- return ExpandToRuntime(GetObjectStatic, call_inst);
- }
- case IntrinsicHelper::StaticFieldGetFast: {
- return Expand_SGetFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kInt);
- }
- case IntrinsicHelper::StaticFieldGetWideFast: {
- return Expand_SGetFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kLong);
- }
- case IntrinsicHelper::StaticFieldGetObjectFast: {
- return Expand_SGetFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kObject);
- }
- case IntrinsicHelper::StaticFieldGetBooleanFast: {
- return Expand_SGetFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kBoolean);
- }
- case IntrinsicHelper::StaticFieldGetByteFast: {
- return Expand_SGetFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kByte);
- }
- case IntrinsicHelper::StaticFieldGetCharFast: {
- return Expand_SGetFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kChar);
- }
- case IntrinsicHelper::StaticFieldGetShortFast: {
- return Expand_SGetFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- kShort);
- }
- case IntrinsicHelper::StaticFieldPut:
- case IntrinsicHelper::StaticFieldPutBoolean:
- case IntrinsicHelper::StaticFieldPutByte:
- case IntrinsicHelper::StaticFieldPutChar:
- case IntrinsicHelper::StaticFieldPutShort: {
- return ExpandToRuntime(Set32Static, call_inst);
- }
- case IntrinsicHelper::StaticFieldPutWide: {
- return ExpandToRuntime(Set64Static, call_inst);
- }
- case IntrinsicHelper::StaticFieldPutObject: {
- return ExpandToRuntime(SetObjectStatic, call_inst);
- }
- case IntrinsicHelper::StaticFieldPutFast: {
- Expand_SPutFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- call_inst.getArgOperand(3),
- kInt);
- return NULL;
- }
- case IntrinsicHelper::StaticFieldPutWideFast: {
- Expand_SPutFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- call_inst.getArgOperand(3),
- kLong);
- return NULL;
- }
- case IntrinsicHelper::StaticFieldPutObjectFast: {
- Expand_SPutFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- call_inst.getArgOperand(3),
- kObject);
- return NULL;
- }
- case IntrinsicHelper::StaticFieldPutBooleanFast: {
- Expand_SPutFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- call_inst.getArgOperand(3),
- kBoolean);
- return NULL;
- }
- case IntrinsicHelper::StaticFieldPutByteFast: {
- Expand_SPutFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- call_inst.getArgOperand(3),
- kByte);
- return NULL;
- }
- case IntrinsicHelper::StaticFieldPutCharFast: {
- Expand_SPutFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- call_inst.getArgOperand(3),
- kChar);
- return NULL;
- }
- case IntrinsicHelper::StaticFieldPutShortFast: {
- Expand_SPutFast(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- call_inst.getArgOperand(2),
- call_inst.getArgOperand(3),
- kShort);
- return NULL;
- }
- case IntrinsicHelper::LoadDeclaringClassSSB: {
- return Expand_LoadDeclaringClassSSB(call_inst.getArgOperand(0));
- }
- case IntrinsicHelper::InitializeAndLoadClassSSB: {
- return ExpandToRuntime(InitializeStaticStorage, call_inst);
- }
-
- //==- High-level Array -------------------------------------------------==//
- case IntrinsicHelper::HLArrayGet: {
- return Expand_HLArrayGet(call_inst, kInt);
- }
- case IntrinsicHelper::HLArrayGetBoolean: {
- return Expand_HLArrayGet(call_inst, kBoolean);
- }
- case IntrinsicHelper::HLArrayGetByte: {
- return Expand_HLArrayGet(call_inst, kByte);
- }
- case IntrinsicHelper::HLArrayGetChar: {
- return Expand_HLArrayGet(call_inst, kChar);
- }
- case IntrinsicHelper::HLArrayGetShort: {
- return Expand_HLArrayGet(call_inst, kShort);
- }
- case IntrinsicHelper::HLArrayGetFloat: {
- return Expand_HLArrayGet(call_inst, kFloat);
- }
- case IntrinsicHelper::HLArrayGetWide: {
- return Expand_HLArrayGet(call_inst, kLong);
- }
- case IntrinsicHelper::HLArrayGetDouble: {
- return Expand_HLArrayGet(call_inst, kDouble);
- }
- case IntrinsicHelper::HLArrayGetObject: {
- return Expand_HLArrayGet(call_inst, kObject);
- }
- case IntrinsicHelper::HLArrayPut: {
- Expand_HLArrayPut(call_inst, kInt);
- return NULL;
- }
- case IntrinsicHelper::HLArrayPutBoolean: {
- Expand_HLArrayPut(call_inst, kBoolean);
- return NULL;
- }
- case IntrinsicHelper::HLArrayPutByte: {
- Expand_HLArrayPut(call_inst, kByte);
- return NULL;
- }
- case IntrinsicHelper::HLArrayPutChar: {
- Expand_HLArrayPut(call_inst, kChar);
- return NULL;
- }
- case IntrinsicHelper::HLArrayPutShort: {
- Expand_HLArrayPut(call_inst, kShort);
- return NULL;
- }
- case IntrinsicHelper::HLArrayPutFloat: {
- Expand_HLArrayPut(call_inst, kFloat);
- return NULL;
- }
- case IntrinsicHelper::HLArrayPutWide: {
- Expand_HLArrayPut(call_inst, kLong);
- return NULL;
- }
- case IntrinsicHelper::HLArrayPutDouble: {
- Expand_HLArrayPut(call_inst, kDouble);
- return NULL;
- }
- case IntrinsicHelper::HLArrayPutObject: {
- Expand_HLArrayPut(call_inst, kObject);
- return NULL;
- }
-
- //==- High-level Instance ----------------------------------------------==//
- case IntrinsicHelper::HLIGet: {
- return Expand_HLIGet(call_inst, kInt);
- }
- case IntrinsicHelper::HLIGetBoolean: {
- return Expand_HLIGet(call_inst, kBoolean);
- }
- case IntrinsicHelper::HLIGetByte: {
- return Expand_HLIGet(call_inst, kByte);
- }
- case IntrinsicHelper::HLIGetChar: {
- return Expand_HLIGet(call_inst, kChar);
- }
- case IntrinsicHelper::HLIGetShort: {
- return Expand_HLIGet(call_inst, kShort);
- }
- case IntrinsicHelper::HLIGetFloat: {
- return Expand_HLIGet(call_inst, kFloat);
- }
- case IntrinsicHelper::HLIGetWide: {
- return Expand_HLIGet(call_inst, kLong);
- }
- case IntrinsicHelper::HLIGetDouble: {
- return Expand_HLIGet(call_inst, kDouble);
- }
- case IntrinsicHelper::HLIGetObject: {
- return Expand_HLIGet(call_inst, kObject);
- }
- case IntrinsicHelper::HLIPut: {
- Expand_HLIPut(call_inst, kInt);
- return NULL;
- }
- case IntrinsicHelper::HLIPutBoolean: {
- Expand_HLIPut(call_inst, kBoolean);
- return NULL;
- }
- case IntrinsicHelper::HLIPutByte: {
- Expand_HLIPut(call_inst, kByte);
- return NULL;
- }
- case IntrinsicHelper::HLIPutChar: {
- Expand_HLIPut(call_inst, kChar);
- return NULL;
- }
- case IntrinsicHelper::HLIPutShort: {
- Expand_HLIPut(call_inst, kShort);
- return NULL;
- }
- case IntrinsicHelper::HLIPutFloat: {
- Expand_HLIPut(call_inst, kFloat);
- return NULL;
- }
- case IntrinsicHelper::HLIPutWide: {
- Expand_HLIPut(call_inst, kLong);
- return NULL;
- }
- case IntrinsicHelper::HLIPutDouble: {
- Expand_HLIPut(call_inst, kDouble);
- return NULL;
- }
- case IntrinsicHelper::HLIPutObject: {
- Expand_HLIPut(call_inst, kObject);
- return NULL;
- }
-
- //==- High-level Invoke ------------------------------------------------==//
- case IntrinsicHelper::HLInvokeVoid:
- case IntrinsicHelper::HLInvokeObj:
- case IntrinsicHelper::HLInvokeInt:
- case IntrinsicHelper::HLInvokeFloat:
- case IntrinsicHelper::HLInvokeLong:
- case IntrinsicHelper::HLInvokeDouble: {
- return Expand_HLInvoke(call_inst);
- }
-
- //==- Invoke -----------------------------------------------------------==//
- case IntrinsicHelper::FindStaticMethodWithAccessCheck: {
- return ExpandToRuntime(FindStaticMethodWithAccessCheck, call_inst);
- }
- case IntrinsicHelper::FindDirectMethodWithAccessCheck: {
- return ExpandToRuntime(FindDirectMethodWithAccessCheck, call_inst);
- }
- case IntrinsicHelper::FindVirtualMethodWithAccessCheck: {
- return ExpandToRuntime(FindVirtualMethodWithAccessCheck, call_inst);
- }
- case IntrinsicHelper::FindSuperMethodWithAccessCheck: {
- return ExpandToRuntime(FindSuperMethodWithAccessCheck, call_inst);
- }
- case IntrinsicHelper::FindInterfaceMethodWithAccessCheck: {
- return ExpandToRuntime(FindInterfaceMethodWithAccessCheck, call_inst);
- }
- case IntrinsicHelper::GetSDCalleeMethodObjAddrFast: {
- return Expand_GetSDCalleeMethodObjAddrFast(call_inst.getArgOperand(0));
- }
- case IntrinsicHelper::GetVirtualCalleeMethodObjAddrFast: {
- return Expand_GetVirtualCalleeMethodObjAddrFast(
- call_inst.getArgOperand(0), call_inst.getArgOperand(1));
- }
- case IntrinsicHelper::GetInterfaceCalleeMethodObjAddrFast: {
- return ExpandToRuntime(FindInterfaceMethod, call_inst);
- }
- case IntrinsicHelper::InvokeRetVoid:
- case IntrinsicHelper::InvokeRetBoolean:
- case IntrinsicHelper::InvokeRetByte:
- case IntrinsicHelper::InvokeRetChar:
- case IntrinsicHelper::InvokeRetShort:
- case IntrinsicHelper::InvokeRetInt:
- case IntrinsicHelper::InvokeRetLong:
- case IntrinsicHelper::InvokeRetFloat:
- case IntrinsicHelper::InvokeRetDouble:
- case IntrinsicHelper::InvokeRetObject: {
- return Expand_Invoke(call_inst);
- }
-
- //==- Math -------------------------------------------------------------==//
- case IntrinsicHelper::DivInt: {
- return Expand_DivRem(call_inst, /* is_div */true, kInt);
- }
- case IntrinsicHelper::RemInt: {
- return Expand_DivRem(call_inst, /* is_div */false, kInt);
- }
- case IntrinsicHelper::DivLong: {
- return Expand_DivRem(call_inst, /* is_div */true, kLong);
- }
- case IntrinsicHelper::RemLong: {
- return Expand_DivRem(call_inst, /* is_div */false, kLong);
- }
- case IntrinsicHelper::D2L: {
- return ExpandToRuntime(art_d2l, call_inst);
- }
- case IntrinsicHelper::D2I: {
- return ExpandToRuntime(art_d2i, call_inst);
- }
- case IntrinsicHelper::F2L: {
- return ExpandToRuntime(art_f2l, call_inst);
- }
- case IntrinsicHelper::F2I: {
- return ExpandToRuntime(art_f2i, call_inst);
- }
-
- //==- High-level Static ------------------------------------------------==//
- case IntrinsicHelper::HLSget: {
- return Expand_HLSget(call_inst, kInt);
- }
- case IntrinsicHelper::HLSgetBoolean: {
- return Expand_HLSget(call_inst, kBoolean);
- }
- case IntrinsicHelper::HLSgetByte: {
- return Expand_HLSget(call_inst, kByte);
- }
- case IntrinsicHelper::HLSgetChar: {
- return Expand_HLSget(call_inst, kChar);
- }
- case IntrinsicHelper::HLSgetShort: {
- return Expand_HLSget(call_inst, kShort);
- }
- case IntrinsicHelper::HLSgetFloat: {
- return Expand_HLSget(call_inst, kFloat);
- }
- case IntrinsicHelper::HLSgetWide: {
- return Expand_HLSget(call_inst, kLong);
- }
- case IntrinsicHelper::HLSgetDouble: {
- return Expand_HLSget(call_inst, kDouble);
- }
- case IntrinsicHelper::HLSgetObject: {
- return Expand_HLSget(call_inst, kObject);
- }
- case IntrinsicHelper::HLSput: {
- Expand_HLSput(call_inst, kInt);
- return NULL;
- }
- case IntrinsicHelper::HLSputBoolean: {
- Expand_HLSput(call_inst, kBoolean);
- return NULL;
- }
- case IntrinsicHelper::HLSputByte: {
- Expand_HLSput(call_inst, kByte);
- return NULL;
- }
- case IntrinsicHelper::HLSputChar: {
- Expand_HLSput(call_inst, kChar);
- return NULL;
- }
- case IntrinsicHelper::HLSputShort: {
- Expand_HLSput(call_inst, kShort);
- return NULL;
- }
- case IntrinsicHelper::HLSputFloat: {
- Expand_HLSput(call_inst, kFloat);
- return NULL;
- }
- case IntrinsicHelper::HLSputWide: {
- Expand_HLSput(call_inst, kLong);
- return NULL;
- }
- case IntrinsicHelper::HLSputDouble: {
- Expand_HLSput(call_inst, kDouble);
- return NULL;
- }
- case IntrinsicHelper::HLSputObject: {
- Expand_HLSput(call_inst, kObject);
- return NULL;
- }
-
- //==- High-level Monitor -----------------------------------------------==//
- case IntrinsicHelper::MonitorEnter: {
- Expand_MonitorEnter(call_inst);
- return NULL;
- }
- case IntrinsicHelper::MonitorExit: {
- Expand_MonitorExit(call_inst);
- return NULL;
- }
-
- //==- Shadow Frame -----------------------------------------------------==//
- case IntrinsicHelper::AllocaShadowFrame: {
- Expand_AllocaShadowFrame(call_inst.getArgOperand(0));
- return NULL;
- }
- case IntrinsicHelper::SetVReg: {
- Expand_SetVReg(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1));
- return NULL;
- }
- case IntrinsicHelper::PopShadowFrame: {
- Expand_PopShadowFrame();
- return NULL;
- }
- case IntrinsicHelper::UpdateDexPC: {
- Expand_UpdateDexPC(call_inst.getArgOperand(0));
- return NULL;
- }
-
- //==- Comparison -------------------------------------------------------==//
- case IntrinsicHelper::CmplFloat:
- case IntrinsicHelper::CmplDouble: {
- return Expand_FPCompare(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- false);
- }
- case IntrinsicHelper::CmpgFloat:
- case IntrinsicHelper::CmpgDouble: {
- return Expand_FPCompare(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- true);
- }
- case IntrinsicHelper::CmpLong: {
- return Expand_LongCompare(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1));
- }
-
- //==- Const ------------------------------------------------------------==//
- case IntrinsicHelper::ConstInt:
- case IntrinsicHelper::ConstLong: {
- return call_inst.getArgOperand(0);
- }
- case IntrinsicHelper::ConstFloat: {
- return irb_.CreateBitCast(call_inst.getArgOperand(0),
- irb_.getJFloatTy());
- }
- case IntrinsicHelper::ConstDouble: {
- return irb_.CreateBitCast(call_inst.getArgOperand(0),
- irb_.getJDoubleTy());
- }
- case IntrinsicHelper::ConstObj: {
- CHECK_EQ(LV2UInt(call_inst.getArgOperand(0)), 0U);
- return irb_.getJNull();
- }
-
- //==- Method Info ------------------------------------------------------==//
- case IntrinsicHelper::MethodInfo: {
- // Nothing to be done, because MethodInfo carries optional hints that are
- // not needed by the portable path.
- return NULL;
- }
-
- //==- Copy -------------------------------------------------------------==//
- case IntrinsicHelper::CopyInt:
- case IntrinsicHelper::CopyFloat:
- case IntrinsicHelper::CopyLong:
- case IntrinsicHelper::CopyDouble:
- case IntrinsicHelper::CopyObj: {
- return call_inst.getArgOperand(0);
- }
-
- //==- Shift ------------------------------------------------------------==//
- case IntrinsicHelper::SHLLong: {
- return Expand_IntegerShift(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- kIntegerSHL, kLong);
- }
- case IntrinsicHelper::SHRLong: {
- return Expand_IntegerShift(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- kIntegerSHR, kLong);
- }
- case IntrinsicHelper::USHRLong: {
- return Expand_IntegerShift(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- kIntegerUSHR, kLong);
- }
- case IntrinsicHelper::SHLInt: {
- return Expand_IntegerShift(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- kIntegerSHL, kInt);
- }
- case IntrinsicHelper::SHRInt: {
- return Expand_IntegerShift(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- kIntegerSHR, kInt);
- }
- case IntrinsicHelper::USHRInt: {
- return Expand_IntegerShift(call_inst.getArgOperand(0),
- call_inst.getArgOperand(1),
- kIntegerUSHR, kInt);
- }
-
- //==- Conversion -------------------------------------------------------==//
- case IntrinsicHelper::IntToChar: {
- return irb_.CreateZExt(irb_.CreateTrunc(call_inst.getArgOperand(0), irb_.getJCharTy()),
- irb_.getJIntTy());
- }
- case IntrinsicHelper::IntToShort: {
- return irb_.CreateSExt(irb_.CreateTrunc(call_inst.getArgOperand(0), irb_.getJShortTy()),
- irb_.getJIntTy());
- }
- case IntrinsicHelper::IntToByte: {
- return irb_.CreateSExt(irb_.CreateTrunc(call_inst.getArgOperand(0), irb_.getJByteTy()),
- irb_.getJIntTy());
- }
-
- //==- Exception --------------------------------------------------------==//
- case IntrinsicHelper::CatchTargets: {
- UpdatePhiInstruction(current_bb_, irb_.GetInsertBlock());
- llvm::SwitchInst* si = llvm::dyn_cast<llvm::SwitchInst>(call_inst.getNextNode());
- CHECK(si != NULL);
- irb_.CreateBr(si->getDefaultDest());
- si->eraseFromParent();
- return call_inst.getArgOperand(0);
- }
-
- //==- Constructor barrier-----------------------------------------------==//
- case IntrinsicHelper::ConstructorBarrier: {
- irb_.CreateMemoryBarrier(art::kStoreStore);
- return NULL;
- }
-
- //==- Unknown Cases ----------------------------------------------------==//
- case IntrinsicHelper::MaxIntrinsicId:
- case IntrinsicHelper::UnknownId:
- // default:
- // NOTE: "default" is intentionally commented so that C/C++ compiler will
- // give some warning on unmatched cases.
- // NOTE: We should not implement these cases.
- break;
- }
- UNIMPLEMENTED(FATAL) << "Unexpected GBC intrinsic: " << static_cast<int>(intr_id);
- return NULL;
-} // NOLINT(readability/fn_size)
-
-} // anonymous namespace
-
-namespace art {
-namespace llvm {
-
-::llvm::FunctionPass*
-CreateGBCExpanderPass(const IntrinsicHelper& intrinsic_helper, IRBuilder& irb,
- CompilerDriver* driver, const DexCompilationUnit* dex_compilation_unit) {
- return new GBCExpanderPass(intrinsic_helper, irb, driver, dex_compilation_unit);
-}
-
-} // namespace llvm
-} // namespace art
diff --git a/compiler/llvm/generated/art_module.cc b/compiler/llvm/generated/art_module.cc
deleted file mode 100644
index f3c5a5a..0000000
--- a/compiler/llvm/generated/art_module.cc
+++ /dev/null
@@ -1,1096 +0,0 @@
-// Generated with ./gen_art_module_cc.sh
-
-
-#pragma GCC diagnostic ignored "-Wframe-larger-than="
-// TODO: Remove this pragma after llc can generate makeLLVMModuleContents()
-// with smaller frame size.
-
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/Function.h>
-#include <llvm/IR/Module.h>
-#include <llvm/IR/Type.h>
-
-#include <vector>
-
-using namespace llvm;
-
-namespace art {
-namespace llvm {
-
-
-// Generated by llvm2cpp - DO NOT MODIFY!
-
-
-Module* makeLLVMModuleContents(Module *mod) {
-
-mod->setModuleIdentifier("art_module.ll");
-
-// Type Definitions
-std::vector<Type*>FuncTy_0_args;
-StructType *StructTy_JavaObject = mod->getTypeByName("JavaObject");
-if (!StructTy_JavaObject) {
-StructTy_JavaObject = StructType::create(mod->getContext(), "JavaObject");
-}
-std::vector<Type*>StructTy_JavaObject_fields;
-if (StructTy_JavaObject->isOpaque()) {
-StructTy_JavaObject->setBody(StructTy_JavaObject_fields, /*isPacked=*/false);
-}
-
-PointerType* PointerTy_1 = PointerType::get(StructTy_JavaObject, 0);
-
-FuncTy_0_args.push_back(PointerTy_1);
-StructType *StructTy_ShadowFrame = mod->getTypeByName("ShadowFrame");
-if (!StructTy_ShadowFrame) {
-StructTy_ShadowFrame = StructType::create(mod->getContext(), "ShadowFrame");
-}
-std::vector<Type*>StructTy_ShadowFrame_fields;
-StructTy_ShadowFrame_fields.push_back(IntegerType::get(mod->getContext(), 32));
-PointerType* PointerTy_2 = PointerType::get(StructTy_ShadowFrame, 0);
-
-StructTy_ShadowFrame_fields.push_back(PointerTy_2);
-StructTy_ShadowFrame_fields.push_back(PointerTy_1);
-StructTy_ShadowFrame_fields.push_back(IntegerType::get(mod->getContext(), 32));
-if (StructTy_ShadowFrame->isOpaque()) {
-StructTy_ShadowFrame->setBody(StructTy_ShadowFrame_fields, /*isPacked=*/false);
-}
-
-
-FuncTy_0_args.push_back(PointerTy_2);
-FunctionType* FuncTy_0 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_0_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_3_args;
-FunctionType* FuncTy_3 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_3_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_4_args;
-FuncTy_4_args.push_back(PointerTy_1);
-FunctionType* FuncTy_4 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_4_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_5_args;
-FuncTy_5_args.push_back(PointerTy_1);
-FuncTy_5_args.push_back(PointerTy_1);
-FunctionType* FuncTy_5 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_5_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_6_args;
-FuncTy_6_args.push_back(PointerTy_1);
-FunctionType* FuncTy_6 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_6_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_7_args;
-FuncTy_7_args.push_back(PointerTy_1);
-FuncTy_7_args.push_back(PointerTy_2);
-FuncTy_7_args.push_back(PointerTy_1);
-FuncTy_7_args.push_back(IntegerType::get(mod->getContext(), 32));
-FunctionType* FuncTy_7 = FunctionType::get(
- /*Result=*/PointerTy_2,
- /*Params=*/FuncTy_7_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_8_args;
-FuncTy_8_args.push_back(PointerTy_2);
-FunctionType* FuncTy_8 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_8_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_9_args;
-FunctionType* FuncTy_9 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_9_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_10_args;
-FuncTy_10_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_10_args.push_back(IntegerType::get(mod->getContext(), 32));
-FunctionType* FuncTy_10 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_10_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_11_args;
-FuncTy_11_args.push_back(IntegerType::get(mod->getContext(), 32));
-FunctionType* FuncTy_11 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_11_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_12_args;
-FuncTy_12_args.push_back(PointerTy_1);
-FuncTy_12_args.push_back(IntegerType::get(mod->getContext(), 32));
-FunctionType* FuncTy_12 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_12_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_13_args;
-FuncTy_13_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_13_args.push_back(PointerTy_1);
-FuncTy_13_args.push_back(PointerTy_1);
-FunctionType* FuncTy_13 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_13_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_14_args;
-FuncTy_14_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_14_args.push_back(PointerTy_1);
-FuncTy_14_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_14_args.push_back(PointerTy_1);
-FunctionType* FuncTy_14 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_14_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_15_args;
-FuncTy_15_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_15_args.push_back(PointerTy_1);
-FunctionType* FuncTy_15 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_15_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_16_args;
-FuncTy_16_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_16_args.push_back(PointerTy_1);
-FuncTy_16_args.push_back(PointerTy_1);
-FuncTy_16_args.push_back(PointerTy_1);
-FunctionType* FuncTy_16 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_16_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_17_args;
-FuncTy_17_args.push_back(PointerTy_1);
-FuncTy_17_args.push_back(IntegerType::get(mod->getContext(), 32));
-FunctionType* FuncTy_17 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_17_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_18_args;
-FuncTy_18_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_18_args.push_back(PointerTy_1);
-FuncTy_18_args.push_back(IntegerType::get(mod->getContext(), 32));
-FunctionType* FuncTy_18 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_18_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_19_args;
-FuncTy_19_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_19_args.push_back(PointerTy_1);
-FuncTy_19_args.push_back(IntegerType::get(mod->getContext(), 64));
-FunctionType* FuncTy_19 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_19_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_20_args;
-FuncTy_20_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_20_args.push_back(PointerTy_1);
-FuncTy_20_args.push_back(PointerTy_1);
-FunctionType* FuncTy_20 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_20_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_21_args;
-FuncTy_21_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_21_args.push_back(PointerTy_1);
-FunctionType* FuncTy_21 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_21_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_22_args;
-FuncTy_22_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_22_args.push_back(PointerTy_1);
-FunctionType* FuncTy_22 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 64),
- /*Params=*/FuncTy_22_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_23_args;
-FuncTy_23_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_23_args.push_back(PointerTy_1);
-FunctionType* FuncTy_23 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_23_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_24_args;
-FuncTy_24_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_24_args.push_back(PointerTy_1);
-FuncTy_24_args.push_back(PointerTy_1);
-FuncTy_24_args.push_back(IntegerType::get(mod->getContext(), 32));
-FunctionType* FuncTy_24 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_24_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_25_args;
-FuncTy_25_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_25_args.push_back(PointerTy_1);
-FuncTy_25_args.push_back(PointerTy_1);
-FuncTy_25_args.push_back(IntegerType::get(mod->getContext(), 64));
-FunctionType* FuncTy_25 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_25_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_26_args;
-FuncTy_26_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_26_args.push_back(PointerTy_1);
-FuncTy_26_args.push_back(PointerTy_1);
-FuncTy_26_args.push_back(PointerTy_1);
-FunctionType* FuncTy_26 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_26_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_27_args;
-FuncTy_27_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_27_args.push_back(PointerTy_1);
-FuncTy_27_args.push_back(PointerTy_1);
-FunctionType* FuncTy_27 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 64),
- /*Params=*/FuncTy_27_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_28_args;
-FuncTy_28_args.push_back(PointerTy_1);
-FuncTy_28_args.push_back(PointerTy_1);
-FunctionType* FuncTy_28 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_28_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_29_args;
-FuncTy_29_args.push_back(PointerTy_1);
-FuncTy_29_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_29_args.push_back(PointerTy_1);
-FuncTy_29_args.push_back(IntegerType::get(mod->getContext(), 32));
-FunctionType* FuncTy_29 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_29_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_30_args;
-FuncTy_30_args.push_back(PointerTy_1);
-FuncTy_30_args.push_back(PointerTy_1);
-FunctionType* FuncTy_30 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_30_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_31_args;
-FuncTy_31_args.push_back(Type::getDoubleTy(mod->getContext()));
-FunctionType* FuncTy_31 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 64),
- /*Params=*/FuncTy_31_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_32_args;
-FuncTy_32_args.push_back(Type::getDoubleTy(mod->getContext()));
-FunctionType* FuncTy_32 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_32_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_33_args;
-FuncTy_33_args.push_back(Type::getFloatTy(mod->getContext()));
-FunctionType* FuncTy_33 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 64),
- /*Params=*/FuncTy_33_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_34_args;
-FuncTy_34_args.push_back(Type::getFloatTy(mod->getContext()));
-FunctionType* FuncTy_34 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_34_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_35_args;
-FuncTy_35_args.push_back(PointerTy_1);
-FunctionType* FuncTy_35 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_35_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_36_args;
-FuncTy_36_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_36_args.push_back(PointerTy_1);
-FuncTy_36_args.push_back(PointerTy_1);
-FunctionType* FuncTy_36 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_36_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_37_args;
-FuncTy_37_args.push_back(PointerTy_1);
-FuncTy_37_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_37_args.push_back(PointerTy_1);
-FunctionType* FuncTy_37 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_37_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_38_args;
-FuncTy_38_args.push_back(PointerTy_1);
-FuncTy_38_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_38_args.push_back(PointerTy_1);
-FuncTy_38_args.push_back(PointerTy_1);
-FunctionType* FuncTy_38 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_38_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_39_args;
-FunctionType* FuncTy_39 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 1),
- /*Params=*/FuncTy_39_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_40_args;
-FuncTy_40_args.push_back(PointerTy_1);
-FunctionType* FuncTy_40 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_40_args,
- /*isVarArg=*/true);
-
-
-// Function Declarations
-
-Function* func___art_type_list = mod->getFunction("__art_type_list");
-if (!func___art_type_list) {
-func___art_type_list = Function::Create(
- /*Type=*/FuncTy_0,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"__art_type_list", mod); // (external, no body)
-func___art_type_list->setCallingConv(CallingConv::C);
-}
-AttributeSet func___art_type_list_PAL;
-func___art_type_list->setAttributes(func___art_type_list_PAL);
-
-Function* func_art_portable_get_current_thread_from_code = mod->getFunction("art_portable_get_current_thread_from_code");
-if (!func_art_portable_get_current_thread_from_code) {
-func_art_portable_get_current_thread_from_code = Function::Create(
- /*Type=*/FuncTy_3,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_get_current_thread_from_code", mod); // (external, no body)
-func_art_portable_get_current_thread_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_get_current_thread_from_code_PAL;
-func_art_portable_get_current_thread_from_code->setAttributes(func_art_portable_get_current_thread_from_code_PAL);
-
-Function* func_art_portable_set_current_thread_from_code = mod->getFunction("art_portable_set_current_thread_from_code");
-if (!func_art_portable_set_current_thread_from_code) {
-func_art_portable_set_current_thread_from_code = Function::Create(
- /*Type=*/FuncTy_4,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_set_current_thread_from_code", mod); // (external, no body)
-func_art_portable_set_current_thread_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_set_current_thread_from_code_PAL;
-func_art_portable_set_current_thread_from_code->setAttributes(func_art_portable_set_current_thread_from_code_PAL);
-
-Function* func_art_portable_lock_object_from_code = mod->getFunction("art_portable_lock_object_from_code");
-if (!func_art_portable_lock_object_from_code) {
-func_art_portable_lock_object_from_code = Function::Create(
- /*Type=*/FuncTy_5,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_lock_object_from_code", mod); // (external, no body)
-func_art_portable_lock_object_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_lock_object_from_code_PAL;
-func_art_portable_lock_object_from_code->setAttributes(func_art_portable_lock_object_from_code_PAL);
-
-Function* func_art_portable_unlock_object_from_code = mod->getFunction("art_portable_unlock_object_from_code");
-if (!func_art_portable_unlock_object_from_code) {
-func_art_portable_unlock_object_from_code = Function::Create(
- /*Type=*/FuncTy_5,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_unlock_object_from_code", mod); // (external, no body)
-func_art_portable_unlock_object_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_unlock_object_from_code_PAL;
-func_art_portable_unlock_object_from_code->setAttributes(func_art_portable_unlock_object_from_code_PAL);
-
-Function* func_art_portable_test_suspend_from_code = mod->getFunction("art_portable_test_suspend_from_code");
-if (!func_art_portable_test_suspend_from_code) {
-func_art_portable_test_suspend_from_code = Function::Create(
- /*Type=*/FuncTy_6,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_test_suspend_from_code", mod); // (external, no body)
-func_art_portable_test_suspend_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_test_suspend_from_code_PAL;
-func_art_portable_test_suspend_from_code->setAttributes(func_art_portable_test_suspend_from_code_PAL);
-
-Function* func_art_portable_push_shadow_frame_from_code = mod->getFunction("art_portable_push_shadow_frame_from_code");
-if (!func_art_portable_push_shadow_frame_from_code) {
-func_art_portable_push_shadow_frame_from_code = Function::Create(
- /*Type=*/FuncTy_7,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_push_shadow_frame_from_code", mod); // (external, no body)
-func_art_portable_push_shadow_frame_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_push_shadow_frame_from_code_PAL;
-func_art_portable_push_shadow_frame_from_code->setAttributes(func_art_portable_push_shadow_frame_from_code_PAL);
-
-Function* func_art_portable_pop_shadow_frame_from_code = mod->getFunction("art_portable_pop_shadow_frame_from_code");
-if (!func_art_portable_pop_shadow_frame_from_code) {
-func_art_portable_pop_shadow_frame_from_code = Function::Create(
- /*Type=*/FuncTy_8,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_pop_shadow_frame_from_code", mod); // (external, no body)
-func_art_portable_pop_shadow_frame_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_pop_shadow_frame_from_code_PAL;
-func_art_portable_pop_shadow_frame_from_code->setAttributes(func_art_portable_pop_shadow_frame_from_code_PAL);
-
-Function* func_art_portable_get_and_clear_exception = mod->getFunction("art_portable_get_and_clear_exception");
-if (!func_art_portable_get_and_clear_exception) {
-func_art_portable_get_and_clear_exception = Function::Create(
- /*Type=*/FuncTy_4,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_get_and_clear_exception", mod); // (external, no body)
-func_art_portable_get_and_clear_exception->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_get_and_clear_exception_PAL;
-func_art_portable_get_and_clear_exception->setAttributes(func_art_portable_get_and_clear_exception_PAL);
-
-Function* func_art_portable_throw_div_zero_from_code = mod->getFunction("art_portable_throw_div_zero_from_code");
-if (!func_art_portable_throw_div_zero_from_code) {
-func_art_portable_throw_div_zero_from_code = Function::Create(
- /*Type=*/FuncTy_9,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_throw_div_zero_from_code", mod); // (external, no body)
-func_art_portable_throw_div_zero_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_throw_div_zero_from_code_PAL;
-func_art_portable_throw_div_zero_from_code->setAttributes(func_art_portable_throw_div_zero_from_code_PAL);
-
-Function* func_art_portable_throw_array_bounds_from_code = mod->getFunction("art_portable_throw_array_bounds_from_code");
-if (!func_art_portable_throw_array_bounds_from_code) {
-func_art_portable_throw_array_bounds_from_code = Function::Create(
- /*Type=*/FuncTy_10,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_throw_array_bounds_from_code", mod); // (external, no body)
-func_art_portable_throw_array_bounds_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_throw_array_bounds_from_code_PAL;
-func_art_portable_throw_array_bounds_from_code->setAttributes(func_art_portable_throw_array_bounds_from_code_PAL);
-
-Function* func_art_portable_throw_no_such_method_from_code = mod->getFunction("art_portable_throw_no_such_method_from_code");
-if (!func_art_portable_throw_no_such_method_from_code) {
-func_art_portable_throw_no_such_method_from_code = Function::Create(
- /*Type=*/FuncTy_11,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_throw_no_such_method_from_code", mod); // (external, no body)
-func_art_portable_throw_no_such_method_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_throw_no_such_method_from_code_PAL;
-func_art_portable_throw_no_such_method_from_code->setAttributes(func_art_portable_throw_no_such_method_from_code_PAL);
-
-Function* func_art_portable_throw_null_pointer_exception_from_code = mod->getFunction("art_portable_throw_null_pointer_exception_from_code");
-if (!func_art_portable_throw_null_pointer_exception_from_code) {
-func_art_portable_throw_null_pointer_exception_from_code = Function::Create(
- /*Type=*/FuncTy_11,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_throw_null_pointer_exception_from_code", mod); // (external, no body)
-func_art_portable_throw_null_pointer_exception_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_throw_null_pointer_exception_from_code_PAL;
-func_art_portable_throw_null_pointer_exception_from_code->setAttributes(func_art_portable_throw_null_pointer_exception_from_code_PAL);
-
-Function* func_art_portable_throw_stack_overflow_from_code = mod->getFunction("art_portable_throw_stack_overflow_from_code");
-if (!func_art_portable_throw_stack_overflow_from_code) {
-func_art_portable_throw_stack_overflow_from_code = Function::Create(
- /*Type=*/FuncTy_9,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_throw_stack_overflow_from_code", mod); // (external, no body)
-func_art_portable_throw_stack_overflow_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_throw_stack_overflow_from_code_PAL;
-func_art_portable_throw_stack_overflow_from_code->setAttributes(func_art_portable_throw_stack_overflow_from_code_PAL);
-
-Function* func_art_portable_throw_exception_from_code = mod->getFunction("art_portable_throw_exception_from_code");
-if (!func_art_portable_throw_exception_from_code) {
-func_art_portable_throw_exception_from_code = Function::Create(
- /*Type=*/FuncTy_6,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_throw_exception_from_code", mod); // (external, no body)
-func_art_portable_throw_exception_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_throw_exception_from_code_PAL;
-func_art_portable_throw_exception_from_code->setAttributes(func_art_portable_throw_exception_from_code_PAL);
-
-Function* func_art_portable_find_catch_block_from_code = mod->getFunction("art_portable_find_catch_block_from_code");
-if (!func_art_portable_find_catch_block_from_code) {
-func_art_portable_find_catch_block_from_code = Function::Create(
- /*Type=*/FuncTy_12,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_catch_block_from_code", mod); // (external, no body)
-func_art_portable_find_catch_block_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_catch_block_from_code_PAL;
-func_art_portable_find_catch_block_from_code->setAttributes(func_art_portable_find_catch_block_from_code_PAL);
-
-Function* func_art_portable_alloc_object_from_code = mod->getFunction("art_portable_alloc_object_from_code");
-if (!func_art_portable_alloc_object_from_code) {
-func_art_portable_alloc_object_from_code = Function::Create(
- /*Type=*/FuncTy_13,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_alloc_object_from_code", mod); // (external, no body)
-func_art_portable_alloc_object_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_alloc_object_from_code_PAL;
-func_art_portable_alloc_object_from_code->setAttributes(func_art_portable_alloc_object_from_code_PAL);
-
-Function* func_art_portable_alloc_object_from_code_with_access_check = mod->getFunction("art_portable_alloc_object_from_code_with_access_check");
-if (!func_art_portable_alloc_object_from_code_with_access_check) {
-func_art_portable_alloc_object_from_code_with_access_check = Function::Create(
- /*Type=*/FuncTy_13,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_alloc_object_from_code_with_access_check", mod); // (external, no body)
-func_art_portable_alloc_object_from_code_with_access_check->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_alloc_object_from_code_with_access_check_PAL;
-func_art_portable_alloc_object_from_code_with_access_check->setAttributes(func_art_portable_alloc_object_from_code_with_access_check_PAL);
-
-Function* func_art_portable_alloc_array_from_code = mod->getFunction("art_portable_alloc_array_from_code");
-if (!func_art_portable_alloc_array_from_code) {
-func_art_portable_alloc_array_from_code = Function::Create(
- /*Type=*/FuncTy_14,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_alloc_array_from_code", mod); // (external, no body)
-func_art_portable_alloc_array_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_alloc_array_from_code_PAL;
-func_art_portable_alloc_array_from_code->setAttributes(func_art_portable_alloc_array_from_code_PAL);
-
-Function* func_art_portable_alloc_array_from_code_with_access_check = mod->getFunction("art_portable_alloc_array_from_code_with_access_check");
-if (!func_art_portable_alloc_array_from_code_with_access_check) {
-func_art_portable_alloc_array_from_code_with_access_check = Function::Create(
- /*Type=*/FuncTy_14,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_alloc_array_from_code_with_access_check", mod); // (external, no body)
-func_art_portable_alloc_array_from_code_with_access_check->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_alloc_array_from_code_with_access_check_PAL;
-func_art_portable_alloc_array_from_code_with_access_check->setAttributes(func_art_portable_alloc_array_from_code_with_access_check_PAL);
-
-Function* func_art_portable_check_and_alloc_array_from_code = mod->getFunction("art_portable_check_and_alloc_array_from_code");
-if (!func_art_portable_check_and_alloc_array_from_code) {
-func_art_portable_check_and_alloc_array_from_code = Function::Create(
- /*Type=*/FuncTy_14,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_check_and_alloc_array_from_code", mod); // (external, no body)
-func_art_portable_check_and_alloc_array_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_check_and_alloc_array_from_code_PAL;
-func_art_portable_check_and_alloc_array_from_code->setAttributes(func_art_portable_check_and_alloc_array_from_code_PAL);
-
-Function* func_art_portable_check_and_alloc_array_from_code_with_access_check = mod->getFunction("art_portable_check_and_alloc_array_from_code_with_access_check");
-if (!func_art_portable_check_and_alloc_array_from_code_with_access_check) {
-func_art_portable_check_and_alloc_array_from_code_with_access_check = Function::Create(
- /*Type=*/FuncTy_14,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_check_and_alloc_array_from_code_with_access_check", mod); // (external, no body)
-func_art_portable_check_and_alloc_array_from_code_with_access_check->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_check_and_alloc_array_from_code_with_access_check_PAL;
-func_art_portable_check_and_alloc_array_from_code_with_access_check->setAttributes(func_art_portable_check_and_alloc_array_from_code_with_access_check_PAL);
-
-Function* func_art_portable_find_instance_field_from_code = mod->getFunction("art_portable_find_instance_field_from_code");
-if (!func_art_portable_find_instance_field_from_code) {
-func_art_portable_find_instance_field_from_code = Function::Create(
- /*Type=*/FuncTy_15,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_instance_field_from_code", mod); // (external, no body)
-func_art_portable_find_instance_field_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_instance_field_from_code_PAL;
-func_art_portable_find_instance_field_from_code->setAttributes(func_art_portable_find_instance_field_from_code_PAL);
-
-Function* func_art_portable_find_static_field_from_code = mod->getFunction("art_portable_find_static_field_from_code");
-if (!func_art_portable_find_static_field_from_code) {
-func_art_portable_find_static_field_from_code = Function::Create(
- /*Type=*/FuncTy_15,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_static_field_from_code", mod); // (external, no body)
-func_art_portable_find_static_field_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_static_field_from_code_PAL;
-func_art_portable_find_static_field_from_code->setAttributes(func_art_portable_find_static_field_from_code_PAL);
-
-Function* func_art_portable_find_static_method_from_code_with_access_check = mod->getFunction("art_portable_find_static_method_from_code_with_access_check");
-if (!func_art_portable_find_static_method_from_code_with_access_check) {
-func_art_portable_find_static_method_from_code_with_access_check = Function::Create(
- /*Type=*/FuncTy_16,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_static_method_from_code_with_access_check", mod); // (external, no body)
-func_art_portable_find_static_method_from_code_with_access_check->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_static_method_from_code_with_access_check_PAL;
-func_art_portable_find_static_method_from_code_with_access_check->setAttributes(func_art_portable_find_static_method_from_code_with_access_check_PAL);
-
-Function* func_art_portable_find_direct_method_from_code_with_access_check = mod->getFunction("art_portable_find_direct_method_from_code_with_access_check");
-if (!func_art_portable_find_direct_method_from_code_with_access_check) {
-func_art_portable_find_direct_method_from_code_with_access_check = Function::Create(
- /*Type=*/FuncTy_16,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_direct_method_from_code_with_access_check", mod); // (external, no body)
-func_art_portable_find_direct_method_from_code_with_access_check->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_direct_method_from_code_with_access_check_PAL;
-func_art_portable_find_direct_method_from_code_with_access_check->setAttributes(func_art_portable_find_direct_method_from_code_with_access_check_PAL);
-
-Function* func_art_portable_find_virtual_method_from_code_with_access_check = mod->getFunction("art_portable_find_virtual_method_from_code_with_access_check");
-if (!func_art_portable_find_virtual_method_from_code_with_access_check) {
-func_art_portable_find_virtual_method_from_code_with_access_check = Function::Create(
- /*Type=*/FuncTy_16,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_virtual_method_from_code_with_access_check", mod); // (external, no body)
-func_art_portable_find_virtual_method_from_code_with_access_check->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_virtual_method_from_code_with_access_check_PAL;
-func_art_portable_find_virtual_method_from_code_with_access_check->setAttributes(func_art_portable_find_virtual_method_from_code_with_access_check_PAL);
-
-Function* func_art_portable_find_super_method_from_code_with_access_check = mod->getFunction("art_portable_find_super_method_from_code_with_access_check");
-if (!func_art_portable_find_super_method_from_code_with_access_check) {
-func_art_portable_find_super_method_from_code_with_access_check = Function::Create(
- /*Type=*/FuncTy_16,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_super_method_from_code_with_access_check", mod); // (external, no body)
-func_art_portable_find_super_method_from_code_with_access_check->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_super_method_from_code_with_access_check_PAL;
-func_art_portable_find_super_method_from_code_with_access_check->setAttributes(func_art_portable_find_super_method_from_code_with_access_check_PAL);
-
-Function* func_art_portable_find_interface_method_from_code_with_access_check = mod->getFunction("art_portable_find_interface_method_from_code_with_access_check");
-if (!func_art_portable_find_interface_method_from_code_with_access_check) {
-func_art_portable_find_interface_method_from_code_with_access_check = Function::Create(
- /*Type=*/FuncTy_16,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_interface_method_from_code_with_access_check", mod); // (external, no body)
-func_art_portable_find_interface_method_from_code_with_access_check->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_interface_method_from_code_with_access_check_PAL;
-func_art_portable_find_interface_method_from_code_with_access_check->setAttributes(func_art_portable_find_interface_method_from_code_with_access_check_PAL);
-
-Function* func_art_portable_find_interface_method_from_code = mod->getFunction("art_portable_find_interface_method_from_code");
-if (!func_art_portable_find_interface_method_from_code) {
-func_art_portable_find_interface_method_from_code = Function::Create(
- /*Type=*/FuncTy_16,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_interface_method_from_code", mod); // (external, no body)
-func_art_portable_find_interface_method_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_interface_method_from_code_PAL;
-func_art_portable_find_interface_method_from_code->setAttributes(func_art_portable_find_interface_method_from_code_PAL);
-
-Function* func_art_portable_initialize_static_storage_from_code = mod->getFunction("art_portable_initialize_static_storage_from_code");
-if (!func_art_portable_initialize_static_storage_from_code) {
-func_art_portable_initialize_static_storage_from_code = Function::Create(
- /*Type=*/FuncTy_13,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_initialize_static_storage_from_code", mod); // (external, no body)
-func_art_portable_initialize_static_storage_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_initialize_static_storage_from_code_PAL;
-func_art_portable_initialize_static_storage_from_code->setAttributes(func_art_portable_initialize_static_storage_from_code_PAL);
-
-Function* func_art_portable_initialize_type_from_code = mod->getFunction("art_portable_initialize_type_from_code");
-if (!func_art_portable_initialize_type_from_code) {
-func_art_portable_initialize_type_from_code = Function::Create(
- /*Type=*/FuncTy_13,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_initialize_type_from_code", mod); // (external, no body)
-func_art_portable_initialize_type_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_initialize_type_from_code_PAL;
-func_art_portable_initialize_type_from_code->setAttributes(func_art_portable_initialize_type_from_code_PAL);
-
-Function* func_art_portable_initialize_type_and_verify_access_from_code = mod->getFunction("art_portable_initialize_type_and_verify_access_from_code");
-if (!func_art_portable_initialize_type_and_verify_access_from_code) {
-func_art_portable_initialize_type_and_verify_access_from_code = Function::Create(
- /*Type=*/FuncTy_13,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_initialize_type_and_verify_access_from_code", mod); // (external, no body)
-func_art_portable_initialize_type_and_verify_access_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_initialize_type_and_verify_access_from_code_PAL;
-func_art_portable_initialize_type_and_verify_access_from_code->setAttributes(func_art_portable_initialize_type_and_verify_access_from_code_PAL);
-
-Function* func_art_portable_resolve_string_from_code = mod->getFunction("art_portable_resolve_string_from_code");
-if (!func_art_portable_resolve_string_from_code) {
-func_art_portable_resolve_string_from_code = Function::Create(
- /*Type=*/FuncTy_17,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_resolve_string_from_code", mod); // (external, no body)
-func_art_portable_resolve_string_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_resolve_string_from_code_PAL;
-func_art_portable_resolve_string_from_code->setAttributes(func_art_portable_resolve_string_from_code_PAL);
-
-Function* func_art_portable_set32_static_from_code = mod->getFunction("art_portable_set32_static_from_code");
-if (!func_art_portable_set32_static_from_code) {
-func_art_portable_set32_static_from_code = Function::Create(
- /*Type=*/FuncTy_18,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_set32_static_from_code", mod); // (external, no body)
-func_art_portable_set32_static_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_set32_static_from_code_PAL;
-func_art_portable_set32_static_from_code->setAttributes(func_art_portable_set32_static_from_code_PAL);
-
-Function* func_art_portable_set64_static_from_code = mod->getFunction("art_portable_set64_static_from_code");
-if (!func_art_portable_set64_static_from_code) {
-func_art_portable_set64_static_from_code = Function::Create(
- /*Type=*/FuncTy_19,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_set64_static_from_code", mod); // (external, no body)
-func_art_portable_set64_static_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_set64_static_from_code_PAL;
-func_art_portable_set64_static_from_code->setAttributes(func_art_portable_set64_static_from_code_PAL);
-
-Function* func_art_portable_set_obj_static_from_code = mod->getFunction("art_portable_set_obj_static_from_code");
-if (!func_art_portable_set_obj_static_from_code) {
-func_art_portable_set_obj_static_from_code = Function::Create(
- /*Type=*/FuncTy_20,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_set_obj_static_from_code", mod); // (external, no body)
-func_art_portable_set_obj_static_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_set_obj_static_from_code_PAL;
-func_art_portable_set_obj_static_from_code->setAttributes(func_art_portable_set_obj_static_from_code_PAL);
-
-Function* func_art_portable_get32_static_from_code = mod->getFunction("art_portable_get32_static_from_code");
-if (!func_art_portable_get32_static_from_code) {
-func_art_portable_get32_static_from_code = Function::Create(
- /*Type=*/FuncTy_21,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_get32_static_from_code", mod); // (external, no body)
-func_art_portable_get32_static_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_get32_static_from_code_PAL;
-func_art_portable_get32_static_from_code->setAttributes(func_art_portable_get32_static_from_code_PAL);
-
-Function* func_art_portable_get64_static_from_code = mod->getFunction("art_portable_get64_static_from_code");
-if (!func_art_portable_get64_static_from_code) {
-func_art_portable_get64_static_from_code = Function::Create(
- /*Type=*/FuncTy_22,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_get64_static_from_code", mod); // (external, no body)
-func_art_portable_get64_static_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_get64_static_from_code_PAL;
-func_art_portable_get64_static_from_code->setAttributes(func_art_portable_get64_static_from_code_PAL);
-
-Function* func_art_portable_get_obj_static_from_code = mod->getFunction("art_portable_get_obj_static_from_code");
-if (!func_art_portable_get_obj_static_from_code) {
-func_art_portable_get_obj_static_from_code = Function::Create(
- /*Type=*/FuncTy_23,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_get_obj_static_from_code", mod); // (external, no body)
-func_art_portable_get_obj_static_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_get_obj_static_from_code_PAL;
-func_art_portable_get_obj_static_from_code->setAttributes(func_art_portable_get_obj_static_from_code_PAL);
-
-Function* func_art_portable_set32_instance_from_code = mod->getFunction("art_portable_set32_instance_from_code");
-if (!func_art_portable_set32_instance_from_code) {
-func_art_portable_set32_instance_from_code = Function::Create(
- /*Type=*/FuncTy_24,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_set32_instance_from_code", mod); // (external, no body)
-func_art_portable_set32_instance_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_set32_instance_from_code_PAL;
-func_art_portable_set32_instance_from_code->setAttributes(func_art_portable_set32_instance_from_code_PAL);
-
-Function* func_art_portable_set64_instance_from_code = mod->getFunction("art_portable_set64_instance_from_code");
-if (!func_art_portable_set64_instance_from_code) {
-func_art_portable_set64_instance_from_code = Function::Create(
- /*Type=*/FuncTy_25,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_set64_instance_from_code", mod); // (external, no body)
-func_art_portable_set64_instance_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_set64_instance_from_code_PAL;
-func_art_portable_set64_instance_from_code->setAttributes(func_art_portable_set64_instance_from_code_PAL);
-
-Function* func_art_portable_set_obj_instance_from_code = mod->getFunction("art_portable_set_obj_instance_from_code");
-if (!func_art_portable_set_obj_instance_from_code) {
-func_art_portable_set_obj_instance_from_code = Function::Create(
- /*Type=*/FuncTy_26,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_set_obj_instance_from_code", mod); // (external, no body)
-func_art_portable_set_obj_instance_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_set_obj_instance_from_code_PAL;
-func_art_portable_set_obj_instance_from_code->setAttributes(func_art_portable_set_obj_instance_from_code_PAL);
-
-Function* func_art_portable_get32_instance_from_code = mod->getFunction("art_portable_get32_instance_from_code");
-if (!func_art_portable_get32_instance_from_code) {
-func_art_portable_get32_instance_from_code = Function::Create(
- /*Type=*/FuncTy_20,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_get32_instance_from_code", mod); // (external, no body)
-func_art_portable_get32_instance_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_get32_instance_from_code_PAL;
-func_art_portable_get32_instance_from_code->setAttributes(func_art_portable_get32_instance_from_code_PAL);
-
-Function* func_art_portable_get64_instance_from_code = mod->getFunction("art_portable_get64_instance_from_code");
-if (!func_art_portable_get64_instance_from_code) {
-func_art_portable_get64_instance_from_code = Function::Create(
- /*Type=*/FuncTy_27,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_get64_instance_from_code", mod); // (external, no body)
-func_art_portable_get64_instance_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_get64_instance_from_code_PAL;
-func_art_portable_get64_instance_from_code->setAttributes(func_art_portable_get64_instance_from_code_PAL);
-
-Function* func_art_portable_get_obj_instance_from_code = mod->getFunction("art_portable_get_obj_instance_from_code");
-if (!func_art_portable_get_obj_instance_from_code) {
-func_art_portable_get_obj_instance_from_code = Function::Create(
- /*Type=*/FuncTy_13,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_get_obj_instance_from_code", mod); // (external, no body)
-func_art_portable_get_obj_instance_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_get_obj_instance_from_code_PAL;
-func_art_portable_get_obj_instance_from_code->setAttributes(func_art_portable_get_obj_instance_from_code_PAL);
-
-Function* func_art_portable_decode_jobject_in_thread = mod->getFunction("art_portable_decode_jobject_in_thread");
-if (!func_art_portable_decode_jobject_in_thread) {
-func_art_portable_decode_jobject_in_thread = Function::Create(
- /*Type=*/FuncTy_28,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_decode_jobject_in_thread", mod); // (external, no body)
-func_art_portable_decode_jobject_in_thread->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_decode_jobject_in_thread_PAL;
-func_art_portable_decode_jobject_in_thread->setAttributes(func_art_portable_decode_jobject_in_thread_PAL);
-
-Function* func_art_portable_fill_array_data_from_code = mod->getFunction("art_portable_fill_array_data_from_code");
-if (!func_art_portable_fill_array_data_from_code) {
-func_art_portable_fill_array_data_from_code = Function::Create(
- /*Type=*/FuncTy_29,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_fill_array_data_from_code", mod); // (external, no body)
-func_art_portable_fill_array_data_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_fill_array_data_from_code_PAL;
-func_art_portable_fill_array_data_from_code->setAttributes(func_art_portable_fill_array_data_from_code_PAL);
-
-Function* func_art_portable_is_assignable_from_code = mod->getFunction("art_portable_is_assignable_from_code");
-if (!func_art_portable_is_assignable_from_code) {
-func_art_portable_is_assignable_from_code = Function::Create(
- /*Type=*/FuncTy_30,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_is_assignable_from_code", mod); // (external, no body)
-func_art_portable_is_assignable_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_is_assignable_from_code_PAL;
-func_art_portable_is_assignable_from_code->setAttributes(func_art_portable_is_assignable_from_code_PAL);
-
-Function* func_art_portable_check_cast_from_code = mod->getFunction("art_portable_check_cast_from_code");
-if (!func_art_portable_check_cast_from_code) {
-func_art_portable_check_cast_from_code = Function::Create(
- /*Type=*/FuncTy_5,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_check_cast_from_code", mod); // (external, no body)
-func_art_portable_check_cast_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_check_cast_from_code_PAL;
-func_art_portable_check_cast_from_code->setAttributes(func_art_portable_check_cast_from_code_PAL);
-
-Function* func_art_portable_check_put_array_element_from_code = mod->getFunction("art_portable_check_put_array_element_from_code");
-if (!func_art_portable_check_put_array_element_from_code) {
-func_art_portable_check_put_array_element_from_code = Function::Create(
- /*Type=*/FuncTy_5,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_check_put_array_element_from_code", mod); // (external, no body)
-func_art_portable_check_put_array_element_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_check_put_array_element_from_code_PAL;
-func_art_portable_check_put_array_element_from_code->setAttributes(func_art_portable_check_put_array_element_from_code_PAL);
-
-Function* func_art_d2l = mod->getFunction("art_d2l");
-if (!func_art_d2l) {
-func_art_d2l = Function::Create(
- /*Type=*/FuncTy_31,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_d2l", mod); // (external, no body)
-func_art_d2l->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_d2l_PAL;
-func_art_d2l->setAttributes(func_art_d2l_PAL);
-
-Function* func_art_d2i = mod->getFunction("art_d2i");
-if (!func_art_d2i) {
-func_art_d2i = Function::Create(
- /*Type=*/FuncTy_32,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_d2i", mod); // (external, no body)
-func_art_d2i->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_d2i_PAL;
-func_art_d2i->setAttributes(func_art_d2i_PAL);
-
-Function* func_art_f2l = mod->getFunction("art_f2l");
-if (!func_art_f2l) {
-func_art_f2l = Function::Create(
- /*Type=*/FuncTy_33,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_f2l", mod); // (external, no body)
-func_art_f2l->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_f2l_PAL;
-func_art_f2l->setAttributes(func_art_f2l_PAL);
-
-Function* func_art_f2i = mod->getFunction("art_f2i");
-if (!func_art_f2i) {
-func_art_f2i = Function::Create(
- /*Type=*/FuncTy_34,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_f2i", mod); // (external, no body)
-func_art_f2i->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_f2i_PAL;
-func_art_f2i->setAttributes(func_art_f2i_PAL);
-
-Function* func_art_portable_jni_method_start = mod->getFunction("art_portable_jni_method_start");
-if (!func_art_portable_jni_method_start) {
-func_art_portable_jni_method_start = Function::Create(
- /*Type=*/FuncTy_35,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_jni_method_start", mod); // (external, no body)
-func_art_portable_jni_method_start->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_jni_method_start_PAL;
-func_art_portable_jni_method_start->setAttributes(func_art_portable_jni_method_start_PAL);
-
-Function* func_art_portable_jni_method_start_synchronized = mod->getFunction("art_portable_jni_method_start_synchronized");
-if (!func_art_portable_jni_method_start_synchronized) {
-func_art_portable_jni_method_start_synchronized = Function::Create(
- /*Type=*/FuncTy_30,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_jni_method_start_synchronized", mod); // (external, no body)
-func_art_portable_jni_method_start_synchronized->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_jni_method_start_synchronized_PAL;
-func_art_portable_jni_method_start_synchronized->setAttributes(func_art_portable_jni_method_start_synchronized_PAL);
-
-Function* func_art_portable_jni_method_end = mod->getFunction("art_portable_jni_method_end");
-if (!func_art_portable_jni_method_end) {
-func_art_portable_jni_method_end = Function::Create(
- /*Type=*/FuncTy_15,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_jni_method_end", mod); // (external, no body)
-func_art_portable_jni_method_end->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_jni_method_end_PAL;
-func_art_portable_jni_method_end->setAttributes(func_art_portable_jni_method_end_PAL);
-
-Function* func_art_portable_jni_method_end_synchronized = mod->getFunction("art_portable_jni_method_end_synchronized");
-if (!func_art_portable_jni_method_end_synchronized) {
-func_art_portable_jni_method_end_synchronized = Function::Create(
- /*Type=*/FuncTy_36,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_jni_method_end_synchronized", mod); // (external, no body)
-func_art_portable_jni_method_end_synchronized->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_jni_method_end_synchronized_PAL;
-func_art_portable_jni_method_end_synchronized->setAttributes(func_art_portable_jni_method_end_synchronized_PAL);
-
-Function* func_art_portable_jni_method_end_with_reference = mod->getFunction("art_portable_jni_method_end_with_reference");
-if (!func_art_portable_jni_method_end_with_reference) {
-func_art_portable_jni_method_end_with_reference = Function::Create(
- /*Type=*/FuncTy_37,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_jni_method_end_with_reference", mod); // (external, no body)
-func_art_portable_jni_method_end_with_reference->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_jni_method_end_with_reference_PAL;
-func_art_portable_jni_method_end_with_reference->setAttributes(func_art_portable_jni_method_end_with_reference_PAL);
-
-Function* func_art_portable_jni_method_end_with_reference_synchronized = mod->getFunction("art_portable_jni_method_end_with_reference_synchronized");
-if (!func_art_portable_jni_method_end_with_reference_synchronized) {
-func_art_portable_jni_method_end_with_reference_synchronized = Function::Create(
- /*Type=*/FuncTy_38,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_jni_method_end_with_reference_synchronized", mod); // (external, no body)
-func_art_portable_jni_method_end_with_reference_synchronized->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_jni_method_end_with_reference_synchronized_PAL;
-func_art_portable_jni_method_end_with_reference_synchronized->setAttributes(func_art_portable_jni_method_end_with_reference_synchronized_PAL);
-
-Function* func_art_portable_is_exception_pending_from_code = mod->getFunction("art_portable_is_exception_pending_from_code");
-if (!func_art_portable_is_exception_pending_from_code) {
-func_art_portable_is_exception_pending_from_code = Function::Create(
- /*Type=*/FuncTy_39,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_is_exception_pending_from_code", mod); // (external, no body)
-func_art_portable_is_exception_pending_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_is_exception_pending_from_code_PAL;
-func_art_portable_is_exception_pending_from_code->setAttributes(func_art_portable_is_exception_pending_from_code_PAL);
-
-Function* func_art_portable_mark_gc_card_from_code = mod->getFunction("art_portable_mark_gc_card_from_code");
-if (!func_art_portable_mark_gc_card_from_code) {
-func_art_portable_mark_gc_card_from_code = Function::Create(
- /*Type=*/FuncTy_5,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_mark_gc_card_from_code", mod); // (external, no body)
-func_art_portable_mark_gc_card_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_mark_gc_card_from_code_PAL;
-func_art_portable_mark_gc_card_from_code->setAttributes(func_art_portable_mark_gc_card_from_code_PAL);
-
-Function* func_art_portable_proxy_invoke_handler_from_code = mod->getFunction("art_portable_proxy_invoke_handler_from_code");
-if (!func_art_portable_proxy_invoke_handler_from_code) {
-func_art_portable_proxy_invoke_handler_from_code = Function::Create(
- /*Type=*/FuncTy_40,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_proxy_invoke_handler_from_code", mod); // (external, no body)
-func_art_portable_proxy_invoke_handler_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_proxy_invoke_handler_from_code_PAL;
-func_art_portable_proxy_invoke_handler_from_code->setAttributes(func_art_portable_proxy_invoke_handler_from_code_PAL);
-
-// Global Variable Declarations
-
-
-// Constant Definitions
-
-// Global Variable Definitions
-
-// Function Definitions
-
-return mod;
-
-}
-
-} // namespace llvm
-} // namespace art
diff --git a/compiler/llvm/intrinsic_func_list.def b/compiler/llvm/intrinsic_func_list.def
deleted file mode 100644
index 887a626..0000000
--- a/compiler/llvm/intrinsic_func_list.def
+++ /dev/null
@@ -1,1796 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// DEF_INTRINSICS_FUNC(ID, NAME, ATTR, RET_TYPE,
-// ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE, ARG5_TYPE)
-#ifndef DEF_INTRINSICS_FUNC
-# error "missing DEF_INTRINSICS_FUNC definition!"
-#endif
-
-#define _EVAL_DEF_INTRINSICS_FUNC(ID, NAME, ATTR, RET_TYPE, ...) \
- DEF_INTRINSICS_FUNC(ID, NAME, ATTR, RET_TYPE, __VA_ARGS__)
-
-#define _EXPAND_ARG0() kNone, kNone, kNone, kNone, kNone
-#define _EXPAND_ARG1(ARG1) ARG1, kNone, kNone, kNone, kNone
-#define _EXPAND_ARG2(ARG1, ARG2) ARG1, ARG2, kNone, kNone, kNone
-#define _EXPAND_ARG3(ARG1, ARG2, ARG3) ARG1, ARG2, ARG3, kNone, kNone
-#define _EXPAND_ARG4(ARG1, ARG2, ARG3, ARG4) ARG1, ARG2, ARG3, ARG4, kNone
-#define _EXPAND_ARG5(ARG1, ARG2, ARG3, ARG4, ARG5) \
- ARG1, ARG2, ARG3, ARG4, ARG5
-
-#define _JTYPE(TYPE, SPACE) _JTYPE_OF_ ## TYPE ## _UNDER_ ## SPACE
-
-// Note: These should be consistent with the type return from
-// IRBuilder::GetJType([type], kArray).
-#define _JTYPE_OF_kInt1Ty_UNDER_kArray kInt8Ty
-#define _JTYPE_OF_kInt8Ty_UNDER_kArray kInt8Ty
-#define _JTYPE_OF_kInt16Ty_UNDER_kArray kInt16Ty
-#define _JTYPE_OF_kInt32Ty_UNDER_kArray kInt32Ty
-#define _JTYPE_OF_kInt64Ty_UNDER_kArray kInt64Ty
-#define _JTYPE_OF_kJavaObjectTy_UNDER_kArray kJavaObjectTy
-
-// Note: These should be consistent with the type return from
-// IRBuilder::GetJType([type], kField).
-#define _JTYPE_OF_kInt1Ty_UNDER_kField kInt32Ty
-#define _JTYPE_OF_kInt8Ty_UNDER_kField kInt32Ty
-#define _JTYPE_OF_kInt16Ty_UNDER_kField kInt32Ty
-#define _JTYPE_OF_kInt32Ty_UNDER_kField kInt32Ty
-#define _JTYPE_OF_kInt64Ty_UNDER_kField kInt64Ty
-#define _JTYPE_OF_kJavaObjectTy_UNDER_kField kJavaObjectTy
-
-//----------------------------------------------------------------------------
-// Thread
-//----------------------------------------------------------------------------
-
-// Thread* art_portable_get_current_thread()
-_EVAL_DEF_INTRINSICS_FUNC(GetCurrentThread,
- art_portable_get_current_thread,
- kAttrReadNone | kAttrNoThrow,
- kJavaThreadTy,
- _EXPAND_ARG0())
-
-// void art_portable_test_suspend(Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(TestSuspend,
- art_portable_test_suspend,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG1(kJavaThreadTy))
-
-// void art_portable_check_suspend() /* Expands to GetCurrentThread/TestSuspend */
-_EVAL_DEF_INTRINSICS_FUNC(CheckSuspend,
- art_portable_check_suspend,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG0())
-
-// void art_portable_mark_gc_card(Object* new_value, Object* object)
-_EVAL_DEF_INTRINSICS_FUNC(MarkGCCard,
- art_portable_mark_gc_card,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG2(kJavaObjectTy, kJavaObjectTy))
-
-//----------------------------------------------------------------------------
-// Exception
-//----------------------------------------------------------------------------
-
-// Should not expand - introduces the catch targets for a potentially
-// throwing instruction. The result is a switch key and this
-// instruction will be followed by a switch statement. The catch
-// targets will be enumerated as cases of the switch, with the fallthrough
-// designating the block containing the potentially throwing instruction.
-// bool art_portable_catch_targets(int dex_pc)
-_EVAL_DEF_INTRINSICS_FUNC(CatchTargets,
- art_portable_catch_targets,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG1(kInt32ConstantTy))
-
-// void art_portable_throw_exception(JavaObject* exception)
-_EVAL_DEF_INTRINSICS_FUNC(ThrowException,
- art_portable_throw_exception,
- kAttrDoThrow,
- kVoidTy,
- _EXPAND_ARG1(kJavaObjectTy))
-
-// void art_portable_hl_throw_exception(JavaObject* exception)
-_EVAL_DEF_INTRINSICS_FUNC(HLThrowException,
- art_portable_hl_throw_exception,
- kAttrDoThrow,
- kVoidTy,
- _EXPAND_ARG1(kJavaObjectTy))
-
-// JavaObject* art_portable_get_current_exception()
-_EVAL_DEF_INTRINSICS_FUNC(GetException,
- art_portable_get_current_exception,
- kAttrReadOnly | kAttrNoThrow,
- kJavaObjectTy,
- _EXPAND_ARG0())
-
-// bool art_portable_is_exception_pending()
-_EVAL_DEF_INTRINSICS_FUNC(IsExceptionPending,
- art_portable_is_exception_pending,
- kAttrReadOnly | kAttrNoThrow,
- kInt1Ty,
- _EXPAND_ARG0())
-
-// int art_portable_find_catch_block(Method* method, int try_item_offset)
-_EVAL_DEF_INTRINSICS_FUNC(FindCatchBlock,
- art_portable_find_catch_block,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG2(kJavaMethodTy, kInt32ConstantTy))
-
-// void art_portable_throw_div_zero()
-_EVAL_DEF_INTRINSICS_FUNC(ThrowDivZeroException,
- art_portable_throw_div_zero,
- kAttrDoThrow,
- kVoidTy,
- _EXPAND_ARG0())
-
-// void art_portable_throw_null_pointer_exception(uint32_t dex_pc)
-_EVAL_DEF_INTRINSICS_FUNC(ThrowNullPointerException,
- art_portable_throw_null_pointer_exception,
- kAttrDoThrow,
- kVoidTy,
- _EXPAND_ARG1(kInt32ConstantTy))
-
-// void art_portable_throw_array_bounds(int index, int array_len)
-_EVAL_DEF_INTRINSICS_FUNC(ThrowIndexOutOfBounds,
- art_portable_throw_array_bounds,
- kAttrDoThrow,
- kVoidTy,
- _EXPAND_ARG2(kInt32Ty, kInt32Ty))
-
-//----------------------------------------------------------------------------
-// ConstString
-//----------------------------------------------------------------------------
-
-// JavaObject* art_portable_const_string(uint32_t string_idx)
-_EVAL_DEF_INTRINSICS_FUNC(ConstString,
- art_portable_const_string,
- kAttrReadOnly | kAttrNoThrow,
- kJavaObjectTy,
- _EXPAND_ARG1(kInt32ConstantTy))
-
-// JavaObject* art_portable_load_string_from_dex_cache(Method* method, uint32_t string_idx)
-_EVAL_DEF_INTRINSICS_FUNC(LoadStringFromDexCache,
- art_portable_load_string_from_dex_cache,
- kAttrReadOnly | kAttrNoThrow,
- kJavaObjectTy,
- _EXPAND_ARG1(kInt32ConstantTy))
-
-// JavaObject* art_portable_resolve_string(Method* method, uint32_t string_idx)
-_EVAL_DEF_INTRINSICS_FUNC(ResolveString,
- art_portable_resolve_string,
- kAttrNone,
- kJavaObjectTy,
- _EXPAND_ARG2(kJavaMethodTy, kInt32ConstantTy))
-
-//----------------------------------------------------------------------------
-// ConstClass
-//----------------------------------------------------------------------------
-
-// JavaObject* art_portable_const_class(uint32_t type_idx)
-_EVAL_DEF_INTRINSICS_FUNC(ConstClass,
- art_portable_const_class,
- kAttrReadOnly | kAttrNoThrow,
- kJavaObjectTy,
- _EXPAND_ARG1(kInt32ConstantTy))
-
-// JavaObject* art_portable_initialize_type_and_verify_access(uint32_t type_idx,
-// Method* referrer,
-// Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(InitializeTypeAndVerifyAccess,
- art_portable_initialize_type_and_verify_access,
- kAttrNone,
- kJavaObjectTy,
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaThreadTy))
-
-// JavaObject* art_portable_load_type_from_dex_cache(uint32_t type_idx)
-_EVAL_DEF_INTRINSICS_FUNC(LoadTypeFromDexCache,
- art_portable_load_type_from_dex_cache,
- kAttrReadOnly | kAttrNoThrow,
- kJavaObjectTy,
- _EXPAND_ARG1(kInt32ConstantTy))
-
-// JavaObject* art_portable_initialize_type(uint32_t type_idx,
-// Method* referrer,
-// Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(InitializeType,
- art_portable_initialize_type,
- kAttrNone,
- kJavaObjectTy,
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaThreadTy))
-
-//----------------------------------------------------------------------------
-// Lock
-//----------------------------------------------------------------------------
-
-// void art_portable_lock_object(JavaObject* obj, Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(LockObject,
- art_portable_lock_object,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG2(kJavaObjectTy, kJavaThreadTy))
-
-// void art_portable_unlock_object(JavaObject* obj, Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(UnlockObject,
- art_portable_unlock_object,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG2(kJavaObjectTy, kJavaThreadTy))
-
-//----------------------------------------------------------------------------
-// Cast
-//----------------------------------------------------------------------------
-
-// void art_portable_check_cast(JavaObject* dest_type, JavaObject* src_type)
-_EVAL_DEF_INTRINSICS_FUNC(CheckCast,
- art_portable_check_cast,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG2(kJavaObjectTy, kJavaObjectTy))
-
-// void art_portable_hl_check_cast(uint32_t type_idx, JavaObject* obj)
-_EVAL_DEF_INTRINSICS_FUNC(HLCheckCast,
- art_portable_hl_check_cast,
- kAttrReadOnly | kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG2(kInt32ConstantTy, kJavaObjectTy))
-
-// int art_portable_is_assignable(JavaObject* dest_type, JavaObject* src_type)
-_EVAL_DEF_INTRINSICS_FUNC(IsAssignable,
- art_portable_is_assignable,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG2(kJavaObjectTy, kJavaObjectTy))
-
-//----------------------------------------------------------------------------
-// Allocation
-//----------------------------------------------------------------------------
-
-// JavaObject* art_portable_alloc_object(uint32_t type_idx,
-// Method* referrer,
-// Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(AllocObject,
- art_portable_alloc_object,
- kAttrNone,
- kJavaObjectTy,
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaThreadTy))
-
-// JavaObject* art_portable_alloc_object_with_access_check(uint32_t type_idx,
-// Method* referrer,
-// Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(AllocObjectWithAccessCheck,
- art_portable_alloc_object_with_access_check,
- kAttrNone,
- kJavaObjectTy,
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaThreadTy))
-
-//----------------------------------------------------------------------------
-// Instance
-//----------------------------------------------------------------------------
-
-// JavaObject* art_portable_new_instance(uint32_t type_idx)
-_EVAL_DEF_INTRINSICS_FUNC(NewInstance,
- art_portable_new_instance,
- kAttrNone,
- kJavaObjectTy,
- _EXPAND_ARG1(kInt32Ty))
-
-// bool art_portable_instance_of(uint32_t type_idx, JavaObject* ref)
-_EVAL_DEF_INTRINSICS_FUNC(InstanceOf,
- art_portable_instance_of,
- kAttrNone,
- kInt32Ty,
- _EXPAND_ARG2(kInt32Ty, kJavaObjectTy))
-
-//----------------------------------------------------------------------------
-// Array
-//----------------------------------------------------------------------------
-
-// JavaObject* art_portable_new_array(uint32_t type_idx, uint32_t array_size)
-_EVAL_DEF_INTRINSICS_FUNC(NewArray,
- art_portable_new_array,
- kAttrNone,
- kJavaObjectTy,
- _EXPAND_ARG2(kInt32ConstantTy, kInt32Ty))
-
-// uint32_t art_portable_opt_array_length(int32_t opt_flags, JavaObject* array)
-_EVAL_DEF_INTRINSICS_FUNC(OptArrayLength,
- art_portable_opt_array_length,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG2(kInt32Ty, kJavaObjectTy))
-
-// uint32_t art_portable_array_length(JavaObject* array)
-_EVAL_DEF_INTRINSICS_FUNC(ArrayLength,
- art_portable_array_length,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG1(kJavaObjectTy))
-
-// JavaObject* art_portable_alloc_array(uint32_t type_idx,
-// Method* referrer,
-// uint32_t length,
-// Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(AllocArray,
- art_portable_alloc_array,
- kAttrNone,
- kJavaObjectTy,
- _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kInt32Ty, kJavaThreadTy))
-
-// JavaObject* art_portable_alloc_array_with_access_check(uint32_t type_idx,
-// Method* referrer,
-// uint32_t length,
-// Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(AllocArrayWithAccessCheck,
- art_portable_alloc_array_with_access_check,
- kAttrNone,
- kJavaObjectTy,
- _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kInt32Ty, kJavaThreadTy))
-
-// JavaObject* art_portable_check_and_alloc_array(uint32_t type_idx,
-// Method* referrer,
-// uint32_t length,
-// Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(CheckAndAllocArray,
- art_portable_check_and_alloc_array,
- kAttrNone,
- kJavaObjectTy,
- _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kInt32ConstantTy, kJavaThreadTy))
-
-// JavaObject* art_portable_check_and_alloc_array_with_access_check(uint32_t type_idx,
-// Method* referrer,
-// uint32_t length,
-// Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(CheckAndAllocArrayWithAccessCheck,
- art_portable_check_and_alloc_array_with_access_check,
- kAttrNone,
- kJavaObjectTy,
- _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kInt32ConstantTy, kJavaThreadTy))
-
-// art_portable_aget_* and art_portable_aput_* never generate exception since the
-// necessary checking on arguments (e.g., array and index) has already done
-// before invocation of these intrinsics.
-//
-// [type] void art_portable_aget_[type](JavaObject* array, uint32_t index)
-_EVAL_DEF_INTRINSICS_FUNC(ArrayGet,
- art_portable_aget,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt32Ty, kArray),
- _EXPAND_ARG2(kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayGetWide,
- art_portable_aget_wide,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt64Ty, kArray),
- _EXPAND_ARG2(kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayGetObject,
- art_portable_aget_object,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kJavaObjectTy, kArray),
- _EXPAND_ARG2(kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayGetBoolean,
- art_portable_aget_boolean,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt1Ty, kArray),
- _EXPAND_ARG2(kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayGetByte,
- art_portable_aget_byte,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt8Ty, kArray),
- _EXPAND_ARG2(kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayGetChar,
- art_portable_aget_char,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt16Ty, kArray),
- _EXPAND_ARG2(kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayGetShort,
- art_portable_aget_short,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt16Ty, kArray),
- _EXPAND_ARG2(kJavaObjectTy, kInt32Ty))
-
-// void art_portable_aput_[type]([type] value, JavaObject* array, uint32_t index)
-_EVAL_DEF_INTRINSICS_FUNC(ArrayPut,
- art_portable_aput,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG3(_JTYPE(kInt32Ty, kArray), kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayPutWide,
- art_portable_aput_wide,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG3(_JTYPE(kInt64Ty, kArray), kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayPutObject,
- art_portable_aput_object,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG3(_JTYPE(kJavaObjectTy, kArray), kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayPutBoolean,
- art_portable_aput_boolean,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG3(_JTYPE(kInt1Ty, kArray), kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayPutByte,
- art_portable_aput_byte,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG3(_JTYPE(kInt8Ty, kArray), kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayPutChar,
- art_portable_aput_char,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG3(_JTYPE(kInt16Ty, kArray), kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayPutShort,
- art_portable_aput_short,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG3(_JTYPE(kInt16Ty, kArray), kJavaObjectTy, kInt32Ty))
-
-// void art_portable_check_put_array_element(JavaObject* value, JavaObject* array)
-_EVAL_DEF_INTRINSICS_FUNC(CheckPutArrayElement,
- art_portable_check_put_array_element,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG2(kJavaObjectTy, kJavaObjectTy))
-
-// void art_portable_filled_new_array(Array* array,
-// uint32_t elem_jty, ...)
-_EVAL_DEF_INTRINSICS_FUNC(FilledNewArray,
- art_portable_filled_new_array,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG3(kJavaObjectTy, kInt32ConstantTy, kVarArgTy))
-
-// void art_portable_fill_array_data(Method* referrer,
-// uint32_t dex_pc,
-// Array* array,
-// uint32_t payload_offset)
-_EVAL_DEF_INTRINSICS_FUNC(FillArrayData,
- art_portable_fill_array_data,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG4(kJavaMethodTy, kInt32ConstantTy, kJavaObjectTy, kInt32ConstantTy))
-
-// void art_portable_hl_fill_array_data(int32_t offset, JavaObject* array)
-_EVAL_DEF_INTRINSICS_FUNC(HLFillArrayData,
- art_portable_hl_fill_array_data,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG2(kInt32ConstantTy, kJavaObjectTy))
-
-//----------------------------------------------------------------------------
-// Instance Field
-//----------------------------------------------------------------------------
-
-// [type] art_portable_iget_[type](uint32_t field_idx,
-// Method* referrer,
-// JavaObject* obj)
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGet,
- art_portable_iget,
- kAttrNone,
- _JTYPE(kInt32Ty, kField),
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetWide,
- art_portable_iget_wide,
- kAttrNone,
- _JTYPE(kInt64Ty, kField),
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetObject,
- art_portable_iget_object,
- kAttrNone,
- _JTYPE(kJavaObjectTy, kField),
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetBoolean,
- art_portable_iget_boolean,
- kAttrNone,
- _JTYPE(kInt1Ty, kField),
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetByte,
- art_portable_iget_byte,
- kAttrNone,
- _JTYPE(kInt8Ty, kField),
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetChar,
- art_portable_iget_char,
- kAttrNone,
- _JTYPE(kInt16Ty, kField),
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetShort,
- art_portable_iget_short,
- kAttrNone,
- _JTYPE(kInt16Ty, kField),
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy))
-
-// [type] art_portable_iget_[type].fast(int field_offset,
-// bool is_volatile,
-// JavaObject* obj)
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetFast,
- art_portable_iget.fast,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt32Ty, kField),
- _EXPAND_ARG3(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetWideFast,
- art_portable_iget_wide.fast,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt64Ty, kField),
- _EXPAND_ARG3(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetObjectFast,
- art_portable_iget_object.fast,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kJavaObjectTy, kField),
- _EXPAND_ARG3(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetBooleanFast,
- art_portable_iget_boolean.fast,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt1Ty, kField),
- _EXPAND_ARG3(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetByteFast,
- art_portable_iget_byte.fast,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt8Ty, kField),
- _EXPAND_ARG3(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetCharFast,
- art_portable_iget_char.fast,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt16Ty, kField),
- _EXPAND_ARG3(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetShortFast,
- art_portable_iget_short.fast,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt16Ty, kField),
- _EXPAND_ARG3(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy))
-
-// void art_portable_iput_[type](uint32_t field_idx,
-// Method* referrer,
-// JavaObject* obj,
-// [type] new_value)
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPut,
- art_portable_iput,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy, _JTYPE(kInt32Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutWide,
- art_portable_iput_wide,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy, _JTYPE(kInt64Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutObject,
- art_portable_iput_object,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy, _JTYPE(kJavaObjectTy, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutBoolean,
- art_portable_iput_boolean,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy, _JTYPE(kInt1Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutByte,
- art_portable_iput_byte,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy, _JTYPE(kInt8Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutChar,
- art_portable_iput_char,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy, _JTYPE(kInt16Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutShort,
- art_portable_iput_short,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy, _JTYPE(kInt16Ty, kField)))
-
-// void art_portable_iput_[type].fast(int field_offset,
-// bool is_volatile,
-// JavaObject* obj,
-// [type] new_value)
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutFast,
- art_portable_iput.fast,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy, _JTYPE(kInt32Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutWideFast,
- art_portable_iput_wide.fast,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy, _JTYPE(kInt64Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutObjectFast,
- art_portable_iput_object.fast,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy, _JTYPE(kJavaObjectTy, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutBooleanFast,
- art_portable_iput_boolean.fast,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy, _JTYPE(kInt1Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutByteFast,
- art_portable_iput_byte.fast,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy, _JTYPE(kInt8Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutCharFast,
- art_portable_iput_char.fast,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy, _JTYPE(kInt16Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutShortFast,
- art_portable_iput_short.fast,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy, _JTYPE(kInt16Ty, kField)))
-
-//----------------------------------------------------------------------------
-// Static Field
-//----------------------------------------------------------------------------
-
-// [type] art_portable_sget_[type](uint32_t field_idx, Method* referrer)
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGet,
- art_portable_sget,
- kAttrNone,
- _JTYPE(kInt32Ty, kField),
- _EXPAND_ARG2(kInt32ConstantTy, kJavaMethodTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetWide,
- art_portable_sget_wide,
- kAttrNone,
- _JTYPE(kInt64Ty, kField),
- _EXPAND_ARG2(kInt32ConstantTy, kJavaMethodTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetObject,
- art_portable_sget_object,
- kAttrNone,
- _JTYPE(kJavaObjectTy, kField),
- _EXPAND_ARG2(kInt32ConstantTy, kJavaMethodTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetBoolean,
- art_portable_sget_boolean,
- kAttrNone,
- _JTYPE(kInt1Ty, kField),
- _EXPAND_ARG2(kInt32ConstantTy, kJavaMethodTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetByte,
- art_portable_sget_byte,
- kAttrNone,
- _JTYPE(kInt8Ty, kField),
- _EXPAND_ARG2(kInt32ConstantTy, kJavaMethodTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetChar,
- art_portable_sget_char,
- kAttrNone,
- _JTYPE(kInt16Ty, kField),
- _EXPAND_ARG2(kInt32ConstantTy, kJavaMethodTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetShort,
- art_portable_sget_short,
- kAttrNone,
- _JTYPE(kInt16Ty, kField),
- _EXPAND_ARG2(kInt32ConstantTy, kJavaMethodTy))
-
-// [type] art_portable_sget_[type].fast(JavaObject* ssb,
-// int field_offset,
-// bool is_volatile)
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetFast,
- art_portable_sget.fast,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt32Ty, kField),
- _EXPAND_ARG3(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetWideFast,
- art_portable_sget_wide.fast,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt64Ty, kField),
- _EXPAND_ARG3(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetObjectFast,
- art_portable_sget_object.fast,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kJavaObjectTy, kField),
- _EXPAND_ARG3(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetBooleanFast,
- art_portable_sget_boolean.fast,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt1Ty, kField),
- _EXPAND_ARG3(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetByteFast,
- art_portable_sget_byte.fast,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt8Ty, kField),
- _EXPAND_ARG3(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetCharFast,
- art_portable_sget_char.fast,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt16Ty, kField),
- _EXPAND_ARG3(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetShortFast,
- art_portable_sget_short.fast,
- kAttrReadOnly | kAttrNoThrow,
- _JTYPE(kInt16Ty, kField),
- _EXPAND_ARG3(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy))
-
-// void art_portable_sput_[type](uint32_t field_idx,
-// Method* referrer,
-// [type] new_value)
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPut,
- art_portable_sput,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, _JTYPE(kInt32Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutWide,
- art_portable_sput_wide,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, _JTYPE(kInt64Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutObject,
- art_portable_sput_object,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, _JTYPE(kJavaObjectTy, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutBoolean,
- art_portable_sput_boolean,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, _JTYPE(kInt1Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutByte,
- art_portable_sput_byte,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, _JTYPE(kInt8Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutChar,
- art_portable_sput_char,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, _JTYPE(kInt16Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutShort,
- art_portable_sput_short,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, _JTYPE(kInt16Ty, kField)))
-
-// void art_portable_sput_[type].fast(JavaObject* ssb,
-// int field_offset,
-// bool is_volatile,
-// [type] new_value)
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutFast,
- art_portable_sput.fast,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy, _JTYPE(kInt32Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutWideFast,
- art_portable_sput_wide.fast,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy, _JTYPE(kInt64Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutObjectFast,
- art_portable_sput_object.fast,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy, _JTYPE(kJavaObjectTy, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutBooleanFast,
- art_portable_sput_boolean.fast,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy, _JTYPE(kInt1Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutByteFast,
- art_portable_sput_byte.fast,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy, _JTYPE(kInt8Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutCharFast,
- art_portable_sput_char.fast,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy, _JTYPE(kInt16Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutShortFast,
- art_portable_sput_short.fast,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy, _JTYPE(kInt16Ty, kField)))
-
-// JavaObject* art_portable_load_declaring_class_ssb(Method* method)
-// Load the static storage base of the class that given method resides
-_EVAL_DEF_INTRINSICS_FUNC(LoadDeclaringClassSSB,
- art_portable_load_declaring_class_ssb,
- kAttrReadOnly | kAttrNoThrow,
- kJavaObjectTy,
- _EXPAND_ARG1(kJavaMethodTy))
-
-// JavaObject* art_portable_init_and_load_class_ssb(uint32_t type_idx,
-// Method* referrer,
-// Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(InitializeAndLoadClassSSB,
- art_portable_init_and_load_class_ssb,
- kAttrNone,
- kJavaObjectTy,
- _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaThreadTy))
-
-//----------------------------------------------------------------------------
-// High-level Array get/put
-//
-// Similar to art_portable_aget/aput_xxx, but checks not yet performed.
-// OptFlags contain info describing whether frontend has determined that
-// null check and/or array bounds check may be skipped.
-//
-// [type] void art_portable_hl_aget_[type](int optFlags, JavaObject* array, uint32_t index)
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGet,
- art_portable_hl_aget,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGetFloat,
- art_portable_hl_aget_float,
- kAttrReadOnly | kAttrNoThrow,
- kFloatTy,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGetWide,
- art_portable_hl_aget_wide,
- kAttrReadOnly | kAttrNoThrow,
- kInt64Ty,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGetDouble,
- art_portable_hl_aget_double,
- kAttrReadOnly | kAttrNoThrow,
- kDoubleTy,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGetObject,
- art_portable_hl_aget_object,
- kAttrReadOnly | kAttrNoThrow,
- kJavaObjectTy,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGetBoolean,
- art_portable_hl_aget_boolean,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGetByte,
- art_portable_hl_aget_byte,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGetChar,
- art_portable_hl_aget_char,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGetShort,
- art_portable_hl_aget_short,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-// void art_portable_aput_[type](int optFlags, [type] value, JavaObject* array, uint32_t index)
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPut,
- art_portable_hl_aput,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPutFloat,
- art_portable_hl_aput_float,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kFloatTy, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPutWide,
- art_portable_hl_aput_wide,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kInt64Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPutDouble,
- art_portable_hl_aput_double,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kDoubleTy, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPutObject,
- art_portable_hl_aput_object,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kJavaObjectTy, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPutBoolean,
- art_portable_hl_aput_boolean,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPutByte,
- art_portable_hl_aput_byte,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPutChar,
- art_portable_hl_aput_char,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPutShort,
- art_portable_hl_aput_short,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-//----------------------------------------------------------------------------
-// High-level Instance get/put
-//
-// Similar to art_portable_iget/iput_xxx, but checks not yet performed.
-// OptFlags contain info describing whether frontend has determined that
-// null check may be skipped.
-//
-// [type] void art_portable_hl_iget_[type](int optFlags, JavaObject* obj, uint32_t field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLIGet,
- art_portable_hl_iget,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIGetFloat,
- art_portable_hl_iget_float,
- kAttrReadOnly | kAttrNoThrow,
- kFloatTy,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIGetWide,
- art_portable_hl_iget_wide,
- kAttrReadOnly | kAttrNoThrow,
- kInt64Ty,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIGetDouble,
- art_portable_hl_iget_double,
- kAttrReadOnly | kAttrNoThrow,
- kDoubleTy,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIGetObject,
- art_portable_hl_iget_object,
- kAttrReadOnly | kAttrNoThrow,
- kJavaObjectTy,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIGetBoolean,
- art_portable_hl_iget_boolean,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIGetByte,
- art_portable_hl_iget_byte,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIGetChar,
- art_portable_hl_iget_char,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIGetShort,
- art_portable_hl_iget_short,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-// void art_portable_iput_[type](int optFlags, [type] value, JavaObject* obj, uint32_t field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLIPut,
- art_portable_hl_iput,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIPutFloat,
- art_portable_hl_iput_float,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kFloatTy, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIPutWide,
- art_portable_hl_iput_wide,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kInt64Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIPutDouble,
- art_portable_hl_iput_double,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kDoubleTy, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIPutObject,
- art_portable_hl_iput_object,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kJavaObjectTy, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIPutBoolean,
- art_portable_hl_iput_boolean,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIPutByte,
- art_portable_hl_iput_byte,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIPutChar,
- art_portable_hl_iput_char,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIPutShort,
- art_portable_hl_iput_short,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-//----------------------------------------------------------------------------
-// High-level Invokes (fast-path determination not yet performed)
-//
-// NOTE: We expect these intrinsics to be temporary. Once calling conventions are
-// fully merged, the unified front end will lower down to the
-// InvokeRetxxx() intrinsics in the next section and these will be
-// removed.
-//
-// arg0: InvokeType [ignored if FilledNewArray]
-// arg1: method_idx [ignored if FilledNewArray]
-// arg2: optimization_flags (primary to note whether null checking is needed)
-// [arg3..argN]: actual arguments
-//----------------------------------------------------------------------------
-// INVOKE method returns void
-_EVAL_DEF_INTRINSICS_FUNC(HLInvokeVoid,
- art_portable_hl_invoke.void,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG1(kVarArgTy))
-
-// INVOKE method returns object
-_EVAL_DEF_INTRINSICS_FUNC(HLInvokeObj,
- art_portable_hl_invoke.obj,
- kAttrNone,
- kJavaObjectTy,
- _EXPAND_ARG1(kVarArgTy))
-
-// INVOKE method returns int
-_EVAL_DEF_INTRINSICS_FUNC(HLInvokeInt,
- art_portable_hl_invoke.i32,
- kAttrNone,
- kInt32Ty,
- _EXPAND_ARG1(kVarArgTy))
-
-// INVOKE method returns float
-_EVAL_DEF_INTRINSICS_FUNC(HLInvokeFloat,
- art_portable_hl_invoke.f32,
- kAttrNone,
- kFloatTy,
- _EXPAND_ARG1(kVarArgTy))
-
-// INVOKE method returns long
-_EVAL_DEF_INTRINSICS_FUNC(HLInvokeLong,
- art_portable_hl_invoke.i64,
- kAttrNone,
- kInt64Ty,
- _EXPAND_ARG1(kVarArgTy))
-
-// INVOKE method returns double
-_EVAL_DEF_INTRINSICS_FUNC(HLInvokeDouble,
- art_portable_hl_invoke.f64,
- kAttrNone,
- kDoubleTy,
- _EXPAND_ARG1(kVarArgTy))
-
-// FILLED_NEW_ARRAY returns object
-_EVAL_DEF_INTRINSICS_FUNC(HLFilledNewArray,
- art_portable_hl_filled_new_array,
- kAttrNone,
- kJavaObjectTy,
- _EXPAND_ARG1(kVarArgTy))
-
-//----------------------------------------------------------------------------
-// Invoke
-//----------------------------------------------------------------------------
-
-// Method* art_portable_find_static_method_with_access_check(uint32_t method_idx,
-// JavaObject* this,
-// Method* referrer,
-// Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(FindStaticMethodWithAccessCheck,
- art_portable_find_static_method_with_access_check,
- kAttrNone,
- kJavaMethodTy,
- _EXPAND_ARG4(kInt32ConstantTy, kJavaObjectTy, kJavaMethodTy, kJavaThreadTy))
-
-// Method* art_portable_find_direct_method_with_access_check(uint32_t method_idx,
-// JavaObject* this,
-// Method* referrer,
-// Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(FindDirectMethodWithAccessCheck,
- art_portable_find_direct_method_with_access_check,
- kAttrNone,
- kJavaMethodTy,
- _EXPAND_ARG4(kInt32ConstantTy, kJavaObjectTy, kJavaMethodTy, kJavaThreadTy))
-
-// Method* art_portable_find_virtual_method_with_access_check(uint32_t method_idx,
-// JavaObject* this,
-// Method* referrer,
-// Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(FindVirtualMethodWithAccessCheck,
- art_portable_find_virtual_method_with_access_check,
- kAttrNone,
- kJavaMethodTy,
- _EXPAND_ARG4(kInt32ConstantTy, kJavaObjectTy, kJavaMethodTy, kJavaThreadTy))
-
-// Method* art_portable_find_super_method_with_access_check(uint32_t method_idx,
-// JavaObject* this,
-// Method* referrer,
-// Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(FindSuperMethodWithAccessCheck,
- art_portable_find_super_method_with_access_check,
- kAttrNone,
- kJavaMethodTy,
- _EXPAND_ARG4(kInt32ConstantTy, kJavaObjectTy, kJavaMethodTy, kJavaThreadTy))
-
-// Method* art_portable_find_interface_method_with_access_check(uint32_t method_idx,
-// JavaObject* this,
-// Method* referrer,
-// Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(FindInterfaceMethodWithAccessCheck,
- art_portable_find_interface_method_with_access_check,
- kAttrNone,
- kJavaMethodTy,
- _EXPAND_ARG4(kInt32ConstantTy, kJavaObjectTy, kJavaMethodTy, kJavaThreadTy))
-
-// Method* art_portable_get_sd_callee_method_obj_addr(uint32_t method_idx)
-_EVAL_DEF_INTRINSICS_FUNC(GetSDCalleeMethodObjAddrFast,
- art_portable_get_sd_callee_method_obj_addr_fast,
- kAttrReadOnly | kAttrNoThrow,
- kJavaMethodTy,
- _EXPAND_ARG1(kInt32ConstantTy))
-
-// Method* art_portable_get_virtual_callee_method_obj_addr(uint32_t vtable_idx,
-// JavaObject* this)
-_EVAL_DEF_INTRINSICS_FUNC(GetVirtualCalleeMethodObjAddrFast,
- art_portable_get_virtual_callee_method_obj_addr_fast,
- kAttrReadOnly | kAttrNoThrow,
- kJavaMethodTy,
- _EXPAND_ARG2(kInt32ConstantTy, kJavaObjectTy))
-
-// Method* art_portable_get_interface_callee_method_obj_addr(uint32_t method_idx,
-// JavaObject* this,
-// Method* referrer,
-// Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(GetInterfaceCalleeMethodObjAddrFast,
- art_portable_get_interface_callee_method_obj_addr_fast,
- kAttrNone,
- kJavaMethodTy,
- _EXPAND_ARG4(kInt32ConstantTy, kJavaObjectTy, kJavaMethodTy, kJavaThreadTy))
-
-// [type] art_portable_invoke.[type](Method* callee, ...)
-// INVOKE method returns void
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetVoid,
- art_portable_invoke.void,
- kAttrNone,
- kVoidTy,
- _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type boolean
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetBoolean,
- art_portable_invoke.bool,
- kAttrNone,
- kInt1Ty,
- _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type byte
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetByte,
- art_portable_invoke.byte,
- kAttrNone,
- kInt8Ty,
- _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type char
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetChar,
- art_portable_invoke.char,
- kAttrNone,
- kInt16Ty,
- _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type short
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetShort,
- art_portable_invoke.short,
- kAttrNone,
- kInt16Ty,
- _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type int
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetInt,
- art_portable_invoke.int,
- kAttrNone,
- kInt32Ty,
- _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type long
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetLong,
- art_portable_invoke.long,
- kAttrNone,
- kInt64Ty,
- _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type float
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetFloat,
- art_portable_invoke.float,
- kAttrNone,
- kFloatTy,
- _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type double
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetDouble,
- art_portable_invoke.double,
- kAttrNone,
- kDoubleTy,
- _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type "object"
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetObject,
- art_portable_invoke.object,
- kAttrNone,
- kJavaObjectTy,
- _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-//----------------------------------------------------------------------------
-// Math
-//----------------------------------------------------------------------------
-
-// int art_portable_{div,rem}_int(int a, int b)
-_EVAL_DEF_INTRINSICS_FUNC(DivInt,
- art_portable_div_int,
- kAttrReadNone | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG2(kInt32Ty, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(RemInt,
- art_portable_rem_int,
- kAttrReadNone | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG2(kInt32Ty, kInt32Ty))
-
-// long art_portable_{div,rem}_long(long a, long b)
-_EVAL_DEF_INTRINSICS_FUNC(DivLong,
- art_portable_div_long,
- kAttrReadNone | kAttrNoThrow,
- kInt64Ty,
- _EXPAND_ARG2(kInt64Ty, kInt64Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(RemLong,
- art_portable_rem_long,
- kAttrReadNone | kAttrNoThrow,
- kInt64Ty,
- _EXPAND_ARG2(kInt64Ty, kInt64Ty))
-
-// int64_t art_portable_d2l(double f)
-_EVAL_DEF_INTRINSICS_FUNC(D2L,
- art_portable_d2l,
- kAttrReadNone | kAttrNoThrow,
- kInt64Ty,
- _EXPAND_ARG1(kDoubleTy))
-
-// int32_t art_portable_d2l(double f)
-_EVAL_DEF_INTRINSICS_FUNC(D2I,
- art_portable_d2i,
- kAttrReadNone | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG1(kDoubleTy))
-
-// int64_t art_portable_f2l(float f)
-_EVAL_DEF_INTRINSICS_FUNC(F2L,
- art_portable_f2l,
- kAttrReadNone | kAttrNoThrow,
- kInt64Ty,
- _EXPAND_ARG1(kFloatTy))
-
-// int32_t art_portable_f2i(float f)
-_EVAL_DEF_INTRINSICS_FUNC(F2I,
- art_portable_f2i,
- kAttrReadNone | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG1(kFloatTy))
-
-//----------------------------------------------------------------------------
-// sput intrinsics to assist MIR to Greenland_ir conversion.
-// "HL" versions - will be deprecated when fast/slow path handling done
-// in the common frontend.
-//----------------------------------------------------------------------------
-
-// void sput_hl(int field_idx, int val)
-_EVAL_DEF_INTRINSICS_FUNC(HLSput,
- art_portable_hl_sput,
- kAttrReadOnly | kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG2(kInt32Ty, kInt32Ty))
-
-// void sput_hl_object(int field_idx, object* val)
-_EVAL_DEF_INTRINSICS_FUNC(HLSputObject,
- art_portable_hl_sput_object,
- kAttrReadOnly | kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG2(kInt32Ty, kJavaObjectTy))
-
-// void sput_hl_boolean(int field_idx, kInt1Ty)
-_EVAL_DEF_INTRINSICS_FUNC(HLSputBoolean,
- art_portable_hl_sput_boolean,
- kAttrReadOnly | kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG2(kInt32Ty, kInt32Ty))
-
-// void sput_hl_byte(int field_idx, int val)
-_EVAL_DEF_INTRINSICS_FUNC(HLSputByte,
- art_portable_hl_sput_byte,
- kAttrReadOnly | kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG2(kInt32Ty, kInt32Ty))
-
-// void sput_hl_char(int field_idx, kInt16Ty val)
-_EVAL_DEF_INTRINSICS_FUNC(HLSputChar,
- art_portable_hl_sput_char,
- kAttrReadOnly | kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG2(kInt32Ty, kInt32Ty))
-
-// void sput_hl_short(int field_idx, int val)
-_EVAL_DEF_INTRINSICS_FUNC(HLSputShort,
- art_portable_hl_sput_short,
- kAttrReadOnly | kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG2(kInt32Ty, kInt32Ty))
-
-// void sput_hl_wide(int field_idx, long val)
-_EVAL_DEF_INTRINSICS_FUNC(HLSputWide,
- art_portable_hl_sput_wide,
- kAttrReadOnly | kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG2(kInt32Ty, kInt64Ty))
-
-// void sput_hl_double(int field_idx, double val)
-_EVAL_DEF_INTRINSICS_FUNC(HLSputDouble,
- art_portable_hl_sput_double,
- kAttrReadOnly | kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG2(kInt32Ty, kDoubleTy))
-
-// void sput_hl_float(int field_idx, float val)
-_EVAL_DEF_INTRINSICS_FUNC(HLSputFloat,
- art_portable_hl_sput_float,
- kAttrReadOnly | kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG2(kInt32Ty, kFloatTy))
-
-//----------------------------------------------------------------------------
-// sget intrinsics to assist MIR to Greenland_ir conversion.
-// "HL" versions - will be deprecated when fast/slow path handling done
-// in the common frontend.
-//----------------------------------------------------------------------------
-
-// int sget_hl(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSget,
- art_portable_hl_sget,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG1(kInt32Ty))
-
-// object* sget_hl_object(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSgetObject,
- art_portable_hl_sget_object,
- kAttrReadOnly | kAttrNoThrow,
- kJavaObjectTy,
- _EXPAND_ARG1(kInt32Ty))
-
-// boolean sget_hl_boolean(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSgetBoolean,
- art_portable_hl_sget_boolean,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG1(kInt32Ty))
-
-// byte sget_hl_byte(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSgetByte,
- art_portable_hl_sget_byte,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG1(kInt32Ty))
-
-// char sget_hl_char(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSgetChar,
- art_portable_hl_sget_char,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG1(kInt32Ty))
-
-// char sget_hl_short(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSgetShort,
- art_portable_hl_sget_short,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG1(kInt32Ty))
-
-// char sget_hl_wide(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSgetWide,
- art_portable_hl_sget_wide,
- kAttrReadOnly | kAttrNoThrow,
- kInt64Ty,
- _EXPAND_ARG1(kInt32Ty))
-
-// char sget_hl_double(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSgetDouble,
- art_portable_hl_sget_double,
- kAttrReadOnly | kAttrNoThrow,
- kDoubleTy,
- _EXPAND_ARG1(kInt32Ty))
-
-// char sget_hl_float(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSgetFloat,
- art_portable_hl_sget_float,
- kAttrReadOnly | kAttrNoThrow,
- kFloatTy,
- _EXPAND_ARG1(kInt32Ty))
-//----------------------------------------------------------------------------
-// Monitor enter/exit
-//----------------------------------------------------------------------------
-// uint32_t art_portable_monitor_enter(int optFlags, JavaObject* obj)
-_EVAL_DEF_INTRINSICS_FUNC(MonitorEnter,
- art_portable_monitor_enter,
- kAttrReadOnly | kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG2(kInt32Ty, kJavaObjectTy))
-
-// uint32_t art_portable_monitor_exit(int optFlags, JavaObject* obj)
-_EVAL_DEF_INTRINSICS_FUNC(MonitorExit,
- art_portable_monitor_exit,
- kAttrReadOnly | kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG2(kInt32Ty, kJavaObjectTy))
-
-//----------------------------------------------------------------------------
-// Shadow Frame
-//----------------------------------------------------------------------------
-
-// void art_portable_alloca_shadow_frame(int num_entry)
-_EVAL_DEF_INTRINSICS_FUNC(AllocaShadowFrame,
- art_portable_alloca_shadow_frame,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG1(kInt32ConstantTy))
-
-// void art_portable_set_vreg(int entry_idx, ...)
-_EVAL_DEF_INTRINSICS_FUNC(SetVReg,
- art_portable_set_vreg,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG2(kInt32ConstantTy, kVarArgTy))
-
-// void art_portable_pop_shadow_frame()
-_EVAL_DEF_INTRINSICS_FUNC(PopShadowFrame,
- art_portable_pop_shadow_frame,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG0())
-
-// void art_portable_update_dex_pc(uint32_t dex_pc)
-_EVAL_DEF_INTRINSICS_FUNC(UpdateDexPC,
- art_portable_update_dex_pc,
- kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG1(kInt32ConstantTy))
-
-//----------------------------------------------------------------------------
-// FP Comparison
-//----------------------------------------------------------------------------
-// int cmpl_float(float, float)
-_EVAL_DEF_INTRINSICS_FUNC(CmplFloat,
- art_portable_cmpl_float,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG2(kFloatTy, kFloatTy))
-
-// int cmpg_float(float, float)
-_EVAL_DEF_INTRINSICS_FUNC(CmpgFloat,
- art_portable_cmpg_float,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG2(kFloatTy, kFloatTy))
-
-// int cmpl_double(double, double)
-_EVAL_DEF_INTRINSICS_FUNC(CmplDouble,
- art_portable_cmpl_double,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG2(kDoubleTy, kDoubleTy))
-
-// int cmpg_double(double, double)
-_EVAL_DEF_INTRINSICS_FUNC(CmpgDouble,
- art_portable_cmpg_double,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG2(kDoubleTy, kDoubleTy))
-
-//----------------------------------------------------------------------------
-// Long Comparison
-//----------------------------------------------------------------------------
-// int cmp_long(long, long)
-_EVAL_DEF_INTRINSICS_FUNC(CmpLong,
- art_portable_cmp_long,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG2(kInt64Ty, kInt64Ty))
-
-//----------------------------------------------------------------------------
-// Const intrinsics to assist MIR to Greenland_ir conversion. Should not materialize
-// For simplicity, all use integer input
-//----------------------------------------------------------------------------
-// int const_int(int)
-_EVAL_DEF_INTRINSICS_FUNC(ConstInt,
- art_portable_const_int,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG1(kInt32Ty))
-
-// JavaObject* const_obj(int)
-_EVAL_DEF_INTRINSICS_FUNC(ConstObj,
- art_portable_const_obj,
- kAttrReadOnly | kAttrNoThrow,
- kJavaObjectTy,
- _EXPAND_ARG1(kInt32Ty))
-
-// long const_long(long)
-_EVAL_DEF_INTRINSICS_FUNC(ConstLong,
- art_portable_const_long,
- kAttrReadOnly | kAttrNoThrow,
- kInt64Ty,
- _EXPAND_ARG1(kInt64Ty))
-
-// float const_float(int)
-_EVAL_DEF_INTRINSICS_FUNC(ConstFloat,
- art_portable_const_Float,
- kAttrReadOnly | kAttrNoThrow,
- kFloatTy,
- _EXPAND_ARG1(kInt32Ty))
-
-// double const_double(long)
-_EVAL_DEF_INTRINSICS_FUNC(ConstDouble,
- art_portable_const_Double,
- kAttrReadOnly | kAttrNoThrow,
- kDoubleTy,
- _EXPAND_ARG1(kInt64Ty))
-
-
-//----------------------------------------------------------------------------
-// Copy intrinsics to assist MIR to Greenland_ir conversion. Should not materialize
-//----------------------------------------------------------------------------
-
-// void method_info(void)
-_EVAL_DEF_INTRINSICS_FUNC(MethodInfo,
- art_portable_method_info,
- kAttrReadOnly | kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG0())
-
-// int copy_int(int)
-_EVAL_DEF_INTRINSICS_FUNC(CopyInt,
- art_portable_copy_int,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG1(kInt32Ty))
-
-// JavaObject* copy_obj(obj)
-_EVAL_DEF_INTRINSICS_FUNC(CopyObj,
- art_portable_copy_obj,
- kAttrReadOnly | kAttrNoThrow,
- kJavaObjectTy,
- _EXPAND_ARG1(kJavaObjectTy))
-
-// long copy_long(long)
-_EVAL_DEF_INTRINSICS_FUNC(CopyLong,
- art_portable_copy_long,
- kAttrReadOnly | kAttrNoThrow,
- kInt64Ty,
- _EXPAND_ARG1(kInt64Ty))
-
-// float copy_float(float)
-_EVAL_DEF_INTRINSICS_FUNC(CopyFloat,
- art_portable_copy_Float,
- kAttrReadOnly | kAttrNoThrow,
- kFloatTy,
- _EXPAND_ARG1(kFloatTy))
-
-// double copy_double(double)
-_EVAL_DEF_INTRINSICS_FUNC(CopyDouble,
- art_portable_copy_Double,
- kAttrReadOnly | kAttrNoThrow,
- kDoubleTy,
- _EXPAND_ARG1(kDoubleTy))
-
-//----------------------------------------------------------------------------
-// Shift intrinsics. Shift semantics for Dalvik are a bit different than
-// the llvm shift operators. For 32-bit shifts, the shift count is constrained
-// to the range of 0..31, while for 64-bit shifts we limit to 0..63.
-// Further, the shift count for Long shifts in Dalvik is 32 bits, while
-// llvm requires a 64-bit shift count. For GBC, we represent shifts as an
-// intrinsic to allow most efficient target-dependent lowering.
-//----------------------------------------------------------------------------
-// long shl_long(long,int)
-_EVAL_DEF_INTRINSICS_FUNC(SHLLong,
- art_portable_shl_long,
- kAttrReadOnly | kAttrNoThrow,
- kInt64Ty,
- _EXPAND_ARG2(kInt64Ty,kInt32Ty))
-// long shr_long(long,int)
-_EVAL_DEF_INTRINSICS_FUNC(SHRLong,
- art_portable_shr_long,
- kAttrReadOnly | kAttrNoThrow,
- kInt64Ty,
- _EXPAND_ARG2(kInt64Ty,kInt32Ty))
-// long ushr_long(long,int)
-_EVAL_DEF_INTRINSICS_FUNC(USHRLong,
- art_portable_ushl_long,
- kAttrReadOnly | kAttrNoThrow,
- kInt64Ty,
- _EXPAND_ARG2(kInt64Ty,kInt32Ty))
-// int shl_int(int,int)
-_EVAL_DEF_INTRINSICS_FUNC(SHLInt,
- art_portable_shl_int,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG2(kInt32Ty,kInt32Ty))
-// long shr_int(int,int)
-_EVAL_DEF_INTRINSICS_FUNC(SHRInt,
- art_portable_shr_int,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG2(kInt32Ty,kInt32Ty))
-// int ushr_long(int,int)
-_EVAL_DEF_INTRINSICS_FUNC(USHRInt,
- art_portable_ushl_int,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG2(kInt32Ty,kInt32Ty))
-//----------------------------------------------------------------------------
-// Conversion instrinsics. Note: these should eventually be removed. We
-// can express these directly in bitcode, but by using intrinsics the
-// Quick compiler can be more efficient. Some extra optimization infrastructure
-// will have to be developed to undo the bitcode verbosity when these are
-// done inline.
-//----------------------------------------------------------------------------
-// int int_to_byte(int)
-_EVAL_DEF_INTRINSICS_FUNC(IntToByte,
- art_portable_int_to_byte,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG1(kInt32Ty))
-
-// int int_to_char(int)
-_EVAL_DEF_INTRINSICS_FUNC(IntToChar,
- art_portable_int_to_char,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG1(kInt32Ty))
-
-// int int_to_short(int)
-_EVAL_DEF_INTRINSICS_FUNC(IntToShort,
- art_portable_int_to_short,
- kAttrReadOnly | kAttrNoThrow,
- kInt32Ty,
- _EXPAND_ARG1(kInt32Ty))
-
-//----------------------------------------------------------------------------
-// Memory barrier
-//----------------------------------------------------------------------------
-// void constructor_barrier()
-_EVAL_DEF_INTRINSICS_FUNC(ConstructorBarrier,
- art_portable_constructor_barrier,
- kAttrReadOnly | kAttrNoThrow,
- kVoidTy,
- _EXPAND_ARG0())
-
-// Clean up all internal used macros
-#undef _EXPAND_ARG0
-#undef _EXPAND_ARG1
-#undef _EXPAND_ARG2
-#undef _EXPAND_ARG3
-#undef _EXPAND_ARG4
-#undef _EXPAND_ARG5
-
-#undef _JTYPE_OF_kInt1Ty_UNDER_kArray
-#undef _JTYPE_OF_kInt8Ty_UNDER_kArray
-#undef _JTYPE_OF_kInt16Ty_UNDER_kArray
-#undef _JTYPE_OF_kInt32Ty_UNDER_kArray
-#undef _JTYPE_OF_kInt64Ty_UNDER_kArray
-#undef _JTYPE_OF_kJavaObjectTy_UNDER_kArray
-
-#undef _JTYPE_OF_kInt1Ty_UNDER_kField
-#undef _JTYPE_OF_kInt8Ty_UNDER_kField
-#undef _JTYPE_OF_kInt16Ty_UNDER_kField
-#undef _JTYPE_OF_kInt32Ty_UNDER_kField
-#undef _JTYPE_OF_kInt64Ty_UNDER_kField
-#undef _JTYPE_OF_kJavaObjectTy_UNDER_kField
-
-#undef DEF_INTRINSICS_FUNC
diff --git a/compiler/llvm/intrinsic_helper.cc b/compiler/llvm/intrinsic_helper.cc
deleted file mode 100644
index e5e7998..0000000
--- a/compiler/llvm/intrinsic_helper.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "intrinsic_helper.h"
-
-#include "ir_builder.h"
-
-#include <llvm/IR/Attributes.h>
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/Function.h>
-#include <llvm/IR/IRBuilder.h>
-#include <llvm/IR/Intrinsics.h>
-
-namespace art {
-namespace llvm {
-
-const IntrinsicHelper::IntrinsicInfo IntrinsicHelper::Info[] = {
-#define DEF_INTRINSICS_FUNC(_, NAME, ATTR, RET_TYPE, ARG1_TYPE, ARG2_TYPE, \
- ARG3_TYPE, ARG4_TYPE, \
- ARG5_TYPE) \
- { #NAME, ATTR, RET_TYPE, { ARG1_TYPE, ARG2_TYPE, \
- ARG3_TYPE, ARG4_TYPE, \
- ARG5_TYPE} },
-#include "intrinsic_func_list.def"
-};
-
-static ::llvm::Type* GetLLVMTypeOfIntrinsicValType(IRBuilder& irb,
- IntrinsicHelper::IntrinsicValType type) {
- switch (type) {
- case IntrinsicHelper::kVoidTy: {
- return irb.getVoidTy();
- }
- case IntrinsicHelper::kJavaObjectTy: {
- return irb.getJObjectTy();
- }
- case IntrinsicHelper::kJavaMethodTy: {
- return irb.getJMethodTy();
- }
- case IntrinsicHelper::kJavaThreadTy: {
- return irb.getJThreadTy();
- }
- case IntrinsicHelper::kInt1Ty:
- case IntrinsicHelper::kInt1ConstantTy: {
- return irb.getInt1Ty();
- }
- case IntrinsicHelper::kInt8Ty:
- case IntrinsicHelper::kInt8ConstantTy: {
- return irb.getInt8Ty();
- }
- case IntrinsicHelper::kInt16Ty:
- case IntrinsicHelper::kInt16ConstantTy: {
- return irb.getInt16Ty();
- }
- case IntrinsicHelper::kInt32Ty:
- case IntrinsicHelper::kInt32ConstantTy: {
- return irb.getInt32Ty();
- }
- case IntrinsicHelper::kInt64Ty:
- case IntrinsicHelper::kInt64ConstantTy: {
- return irb.getInt64Ty();
- }
- case IntrinsicHelper::kFloatTy:
- case IntrinsicHelper::kFloatConstantTy: {
- return irb.getFloatTy();
- }
- case IntrinsicHelper::kDoubleTy:
- case IntrinsicHelper::kDoubleConstantTy: {
- return irb.getDoubleTy();
- }
- case IntrinsicHelper::kNone:
- case IntrinsicHelper::kVarArgTy:
- default: {
- LOG(FATAL) << "Invalid intrinsic type " << type << "to get LLVM type!";
- return NULL;
- }
- }
- // unreachable
-}
-
-IntrinsicHelper::IntrinsicHelper(::llvm::LLVMContext& context,
- ::llvm::Module& module) {
- IRBuilder irb(context, module, *this);
-
- ::memset(intrinsic_funcs_, 0, sizeof(intrinsic_funcs_));
-
- // This loop does the following things:
- // 1. Introduce the intrinsic function into the module
- // 2. Add "nocapture" and "noalias" attribute to the arguments in all
- // intrinsics functions.
- // 3. Initialize intrinsic_funcs_map_.
- for (unsigned i = 0; i < MaxIntrinsicId; i++) {
- IntrinsicId id = static_cast<IntrinsicId>(i);
- const IntrinsicInfo& info = Info[i];
-
- // Parse and construct the argument type from IntrinsicInfo
- ::llvm::Type* arg_type[kIntrinsicMaxArgc];
- unsigned num_args = 0;
- bool is_var_arg = false;
- for (unsigned arg_iter = 0; arg_iter < kIntrinsicMaxArgc; arg_iter++) {
- IntrinsicValType type = info.arg_type_[arg_iter];
-
- if (type == kNone) {
- break;
- } else if (type == kVarArgTy) {
- // Variable argument type must be the last argument
- is_var_arg = true;
- break;
- }
-
- arg_type[num_args++] = GetLLVMTypeOfIntrinsicValType(irb, type);
- }
-
- // Construct the function type
- ::llvm::Type* ret_type =
- GetLLVMTypeOfIntrinsicValType(irb, info.ret_val_type_);
-
- ::llvm::FunctionType* type =
- ::llvm::FunctionType::get(ret_type,
- ::llvm::ArrayRef< ::llvm::Type*>(arg_type, num_args),
- is_var_arg);
-
- // Declare the function
- ::llvm::Function *fn = ::llvm::Function::Create(type,
- ::llvm::Function::ExternalLinkage,
- info.name_, &module);
-
- if (info.attr_ & kAttrReadOnly) {
- fn->setOnlyReadsMemory();
- }
- if (info.attr_ & kAttrReadNone) {
- fn->setDoesNotAccessMemory();
- }
- // None of the intrinsics throws exception
- fn->setDoesNotThrow();
-
- intrinsic_funcs_[id] = fn;
-
- DCHECK_NE(fn, static_cast< ::llvm::Function*>(NULL)) << "Intrinsic `"
- << GetName(id) << "' was not defined!";
-
- // Add "noalias" and "nocapture" attribute to all arguments of pointer type
- for (::llvm::Function::arg_iterator arg_iter = fn->arg_begin(),
- arg_end = fn->arg_end(); arg_iter != arg_end; arg_iter++) {
- if (arg_iter->getType()->isPointerTy()) {
- std::vector< ::llvm::Attribute::AttrKind> attributes;
- attributes.push_back(::llvm::Attribute::NoCapture);
- attributes.push_back(::llvm::Attribute::NoAlias);
- ::llvm::AttributeSet attribute_set = ::llvm::AttributeSet::get(fn->getContext(),
- arg_iter->getArgNo(),
- attributes);
- arg_iter->addAttr(attribute_set);
- }
- }
-
- // Insert the newly created intrinsic to intrinsic_funcs_map_
- if (!intrinsic_funcs_map_.insert(std::make_pair(fn, id)).second) {
- LOG(FATAL) << "Duplicate entry in intrinsic functions map?";
- }
- }
-
- return;
-}
-
-} // namespace llvm
-} // namespace art
diff --git a/compiler/llvm/intrinsic_helper.h b/compiler/llvm/intrinsic_helper.h
deleted file mode 100644
index 657db40..0000000
--- a/compiler/llvm/intrinsic_helper.h
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LLVM_INTRINSIC_HELPER_H_
-#define ART_COMPILER_LLVM_INTRINSIC_HELPER_H_
-
-#include "base/logging.h"
-
-#include <llvm/ADT/DenseMap.h>
-
-namespace llvm {
- class Function;
- class FunctionType;
- class LLVMContext;
- class Module;
-} // namespace llvm
-
-namespace art {
-namespace llvm {
-
-class IRBuilder;
-
-class IntrinsicHelper {
- public:
- enum IntrinsicId {
-#define DEF_INTRINSICS_FUNC(ID, ...) ID,
-#include "intrinsic_func_list.def"
- MaxIntrinsicId,
-
- // Pseudo-intrinsics Id
- UnknownId
- };
-
- enum IntrinsicAttribute {
- kAttrNone = 0,
-
- // Intrinsic that neither modified the memory state nor refer to the global
- // state
- kAttrReadNone = 1 << 0,
-
- // Intrinsic that doesn't modify the memory state. Note that one should set
- // this flag carefully when the intrinsic may throw exception. Since the
- // thread state is implicitly modified when an exception is thrown.
- kAttrReadOnly = 1 << 1,
-
- // Note that intrinsic without kAttrNoThrow and kAttrDoThrow set means that
- // intrinsic generates exception in some cases
-
- // Intrinsic that never generates exception
- kAttrNoThrow = 1 << 2,
- // Intrinsic that always generate exception
- kAttrDoThrow = 1 << 3,
- };
-
- enum IntrinsicValType {
- kNone,
-
- kVoidTy,
-
- kJavaObjectTy,
- kJavaMethodTy,
- kJavaThreadTy,
-
- kInt1Ty,
- kInt8Ty,
- kInt16Ty,
- kInt32Ty,
- kInt64Ty,
- kFloatTy,
- kDoubleTy,
-
- kInt1ConstantTy,
- kInt8ConstantTy,
- kInt16ConstantTy,
- kInt32ConstantTy,
- kInt64ConstantTy,
- kFloatConstantTy,
- kDoubleConstantTy,
-
- kVarArgTy,
- };
-
- enum {
- kIntrinsicMaxArgc = 5
- };
-
- typedef struct IntrinsicInfo {
- const char* name_;
- unsigned attr_;
- IntrinsicValType ret_val_type_;
- IntrinsicValType arg_type_[kIntrinsicMaxArgc];
- } IntrinsicInfo;
-
- private:
- static const IntrinsicInfo Info[];
-
- public:
- static const IntrinsicInfo& GetInfo(IntrinsicId id) {
- DCHECK(id >= 0 && id < MaxIntrinsicId) << "Unknown ART intrinsics ID: "
- << id;
- return Info[id];
- }
-
- static const char* GetName(IntrinsicId id) {
- return (id <= MaxIntrinsicId) ? GetInfo(id).name_ : "InvalidIntrinsic";
- }
-
- static unsigned GetAttr(IntrinsicId id) {
- return GetInfo(id).attr_;
- }
-
- public:
- IntrinsicHelper(::llvm::LLVMContext& context, ::llvm::Module& module);
-
- ::llvm::Function* GetIntrinsicFunction(IntrinsicId id) {
- DCHECK(id >= 0 && id < MaxIntrinsicId) << "Unknown ART intrinsics ID: "
- << id;
- return intrinsic_funcs_[id];
- }
-
- IntrinsicId GetIntrinsicId(const ::llvm::Function* func) const {
- ::llvm::DenseMap<const ::llvm::Function*, IntrinsicId>::const_iterator
- i = intrinsic_funcs_map_.find(func);
- if (i == intrinsic_funcs_map_.end()) {
- return UnknownId;
- } else {
- return i->second;
- }
- }
-
- private:
- // FIXME: "+1" is to workaround the GCC bugs:
- // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949
- // Remove this when uses newer GCC (> 4.4.3)
- ::llvm::Function* intrinsic_funcs_[MaxIntrinsicId + 1];
-
- // Map a llvm::Function to its intrinsic id
- ::llvm::DenseMap<const ::llvm::Function*, IntrinsicId> intrinsic_funcs_map_;
-};
-
-} // namespace llvm
-} // namespace art
-
-#endif // ART_COMPILER_LLVM_INTRINSIC_HELPER_H_
diff --git a/compiler/llvm/ir_builder.cc b/compiler/llvm/ir_builder.cc
deleted file mode 100644
index 9644ebd..0000000
--- a/compiler/llvm/ir_builder.cc
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ir_builder.h"
-
-#include "base/stringprintf.h"
-
-#include <llvm/IR/Module.h>
-
-namespace art {
-namespace llvm {
-
-
-//----------------------------------------------------------------------------
-// General
-//----------------------------------------------------------------------------
-
-IRBuilder::IRBuilder(::llvm::LLVMContext& context, ::llvm::Module& module,
- IntrinsicHelper& intrinsic_helper)
- : LLVMIRBuilder(context), module_(&module), mdb_(context), java_object_type_(NULL),
- java_method_type_(NULL), java_thread_type_(NULL), intrinsic_helper_(intrinsic_helper) {
- // Get java object type from module
- ::llvm::Type* jobject_struct_type = module.getTypeByName("JavaObject");
- CHECK(jobject_struct_type != NULL);
- java_object_type_ = jobject_struct_type->getPointerTo();
-
- // If type of Method is not explicitly defined in the module, use JavaObject*
- ::llvm::Type* type = module.getTypeByName("Method");
- if (type != NULL) {
- java_method_type_ = type->getPointerTo();
- } else {
- java_method_type_ = java_object_type_;
- }
-
- // If type of Thread is not explicitly defined in the module, use JavaObject*
- type = module.getTypeByName("Thread");
- if (type != NULL) {
- java_thread_type_ = type->getPointerTo();
- } else {
- java_thread_type_ = java_object_type_;
- }
-
- // Create JEnv* type
- ::llvm::Type* jenv_struct_type = ::llvm::StructType::create(context, "JEnv");
- jenv_type_ = jenv_struct_type->getPointerTo();
-
- // Get Art shadow frame struct type from module
- art_frame_type_ = module.getTypeByName("ShadowFrame");
- CHECK(art_frame_type_ != NULL);
-
- runtime_support_ = NULL;
-}
-
-
-//----------------------------------------------------------------------------
-// Type Helper Function
-//----------------------------------------------------------------------------
-
-::llvm::Type* IRBuilder::getJType(JType jty) {
- switch (jty) {
- case kVoid:
- return getJVoidTy();
-
- case kBoolean:
- return getJBooleanTy();
-
- case kByte:
- return getJByteTy();
-
- case kChar:
- return getJCharTy();
-
- case kShort:
- return getJShortTy();
-
- case kInt:
- return getJIntTy();
-
- case kLong:
- return getJLongTy();
-
- case kFloat:
- return getJFloatTy();
-
- case kDouble:
- return getJDoubleTy();
-
- case kObject:
- return getJObjectTy();
-
- default:
- LOG(FATAL) << "Unknown java type: " << jty;
- return NULL;
- }
-}
-
-::llvm::StructType* IRBuilder::getShadowFrameTy(uint32_t vreg_size) {
- std::string name(StringPrintf("ShadowFrame%u", vreg_size));
-
- // Try to find the existing struct type definition
- if (::llvm::Type* type = module_->getTypeByName(name)) {
- CHECK(::llvm::isa< ::llvm::StructType>(type));
- return static_cast< ::llvm::StructType*>(type);
- }
-
- // Create new struct type definition
- ::llvm::Type* elem_types[] = {
- art_frame_type_,
- ::llvm::ArrayType::get(getInt32Ty(), vreg_size),
- };
-
- return ::llvm::StructType::create(elem_types, name);
-}
-
-
-} // namespace llvm
-} // namespace art
diff --git a/compiler/llvm/ir_builder.h b/compiler/llvm/ir_builder.h
deleted file mode 100644
index 990ba02..0000000
--- a/compiler/llvm/ir_builder.h
+++ /dev/null
@@ -1,486 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LLVM_IR_BUILDER_H_
-#define ART_COMPILER_LLVM_IR_BUILDER_H_
-
-#include "backend_types.h"
-#include "dex/compiler_enums.h"
-#include "intrinsic_helper.h"
-#include "md_builder.h"
-#include "runtime_support_builder.h"
-#include "runtime_support_llvm_func.h"
-
-#include <llvm/IR/Constants.h>
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/IRBuilder.h>
-#include <llvm/IR/LLVMContext.h>
-#include <llvm/IR/Type.h>
-#include <llvm/Support/NoFolder.h>
-
-#include <stdint.h>
-
-
-namespace art {
-namespace llvm {
-
-class InserterWithDexOffset : public ::llvm::IRBuilderDefaultInserter<true> {
- public:
- InserterWithDexOffset() : node_(NULL) {}
-
- void InsertHelper(::llvm::Instruction *I, const ::llvm::Twine &Name,
- ::llvm::BasicBlock *BB,
- ::llvm::BasicBlock::iterator InsertPt) const {
- ::llvm::IRBuilderDefaultInserter<true>::InsertHelper(I, Name, BB, InsertPt);
- if (node_ != NULL) {
- I->setMetadata("DexOff", node_);
- }
- }
-
- void SetDexOffset(::llvm::MDNode* node) {
- node_ = node;
- }
- private:
- ::llvm::MDNode* node_;
-};
-
-typedef ::llvm::IRBuilder<true, ::llvm::ConstantFolder, InserterWithDexOffset> LLVMIRBuilder;
-// NOTE: Here we define our own LLVMIRBuilder type alias, so that we can
-// switch "preserveNames" template parameter easily.
-
-
-class IRBuilder : public LLVMIRBuilder {
- public:
- //--------------------------------------------------------------------------
- // General
- //--------------------------------------------------------------------------
-
- IRBuilder(::llvm::LLVMContext& context, ::llvm::Module& module,
- IntrinsicHelper& intrinsic_helper);
-
-
- //--------------------------------------------------------------------------
- // Extend load & store for TBAA
- //--------------------------------------------------------------------------
-
- ::llvm::LoadInst* CreateLoad(::llvm::Value* ptr, ::llvm::MDNode* tbaa_info) {
- ::llvm::LoadInst* inst = LLVMIRBuilder::CreateLoad(ptr);
- inst->setMetadata(::llvm::LLVMContext::MD_tbaa, tbaa_info);
- return inst;
- }
-
- ::llvm::StoreInst* CreateStore(::llvm::Value* val, ::llvm::Value* ptr, ::llvm::MDNode* tbaa_info) {
- ::llvm::StoreInst* inst = LLVMIRBuilder::CreateStore(val, ptr);
- inst->setMetadata(::llvm::LLVMContext::MD_tbaa, tbaa_info);
- return inst;
- }
-
- ::llvm::AtomicCmpXchgInst*
- CreateAtomicCmpXchgInst(::llvm::Value* ptr, ::llvm::Value* cmp, ::llvm::Value* val,
- ::llvm::MDNode* tbaa_info) {
- ::llvm::AtomicCmpXchgInst* inst =
- LLVMIRBuilder::CreateAtomicCmpXchg(ptr, cmp, val, ::llvm::Acquire);
- inst->setMetadata(::llvm::LLVMContext::MD_tbaa, tbaa_info);
- return inst;
- }
-
- //--------------------------------------------------------------------------
- // Extend memory barrier
- //--------------------------------------------------------------------------
- void CreateMemoryBarrier(MemBarrierKind barrier_kind) {
- // TODO: select atomic ordering according to given barrier kind.
- CreateFence(::llvm::SequentiallyConsistent);
- }
-
- //--------------------------------------------------------------------------
- // TBAA
- //--------------------------------------------------------------------------
-
- // TODO: After we design the non-special TBAA info, re-design the TBAA interface.
- ::llvm::LoadInst* CreateLoad(::llvm::Value* ptr, TBAASpecialType special_ty) {
- return CreateLoad(ptr, mdb_.GetTBAASpecialType(special_ty));
- }
-
- ::llvm::StoreInst* CreateStore(::llvm::Value* val, ::llvm::Value* ptr, TBAASpecialType special_ty) {
- DCHECK_NE(special_ty, kTBAAConstJObject) << "ConstJObject is read only!";
- return CreateStore(val, ptr, mdb_.GetTBAASpecialType(special_ty));
- }
-
- ::llvm::LoadInst* CreateLoad(::llvm::Value* ptr, TBAASpecialType special_ty, JType j_ty) {
- return CreateLoad(ptr, mdb_.GetTBAAMemoryJType(special_ty, j_ty));
- }
-
- ::llvm::StoreInst* CreateStore(::llvm::Value* val, ::llvm::Value* ptr,
- TBAASpecialType special_ty, JType j_ty) {
- DCHECK_NE(special_ty, kTBAAConstJObject) << "ConstJObject is read only!";
- return CreateStore(val, ptr, mdb_.GetTBAAMemoryJType(special_ty, j_ty));
- }
-
- ::llvm::LoadInst* LoadFromObjectOffset(::llvm::Value* object_addr,
- int64_t offset,
- ::llvm::Type* type,
- TBAASpecialType special_ty) {
- return LoadFromObjectOffset(object_addr, offset, type, mdb_.GetTBAASpecialType(special_ty));
- }
-
- void StoreToObjectOffset(::llvm::Value* object_addr,
- int64_t offset,
- ::llvm::Value* new_value,
- TBAASpecialType special_ty) {
- DCHECK_NE(special_ty, kTBAAConstJObject) << "ConstJObject is read only!";
- StoreToObjectOffset(object_addr, offset, new_value, mdb_.GetTBAASpecialType(special_ty));
- }
-
- ::llvm::LoadInst* LoadFromObjectOffset(::llvm::Value* object_addr,
- int64_t offset,
- ::llvm::Type* type,
- TBAASpecialType special_ty, JType j_ty) {
- return LoadFromObjectOffset(object_addr, offset, type, mdb_.GetTBAAMemoryJType(special_ty, j_ty));
- }
-
- void StoreToObjectOffset(::llvm::Value* object_addr,
- int64_t offset,
- ::llvm::Value* new_value,
- TBAASpecialType special_ty, JType j_ty) {
- DCHECK_NE(special_ty, kTBAAConstJObject) << "ConstJObject is read only!";
- StoreToObjectOffset(object_addr, offset, new_value, mdb_.GetTBAAMemoryJType(special_ty, j_ty));
- }
-
- ::llvm::AtomicCmpXchgInst*
- CompareExchangeObjectOffset(::llvm::Value* object_addr,
- int64_t offset,
- ::llvm::Value* cmp_value,
- ::llvm::Value* new_value,
- TBAASpecialType special_ty) {
- DCHECK_NE(special_ty, kTBAAConstJObject) << "ConstJObject is read only!";
- return CompareExchangeObjectOffset(object_addr, offset, cmp_value, new_value,
- mdb_.GetTBAASpecialType(special_ty));
- }
-
- void SetTBAA(::llvm::Instruction* inst, TBAASpecialType special_ty) {
- inst->setMetadata(::llvm::LLVMContext::MD_tbaa, mdb_.GetTBAASpecialType(special_ty));
- }
-
-
- //--------------------------------------------------------------------------
- // Static Branch Prediction
- //--------------------------------------------------------------------------
-
- // Import the orignal conditional branch
- using LLVMIRBuilder::CreateCondBr;
- ::llvm::BranchInst* CreateCondBr(::llvm::Value *cond,
- ::llvm::BasicBlock* true_bb,
- ::llvm::BasicBlock* false_bb,
- ExpectCond expect) {
- ::llvm::BranchInst* branch_inst = CreateCondBr(cond, true_bb, false_bb);
- if (false) {
- // TODO: http://b/8511695 Restore branch weight metadata
- branch_inst->setMetadata(::llvm::LLVMContext::MD_prof, mdb_.GetBranchWeights(expect));
- }
- return branch_inst;
- }
-
-
- //--------------------------------------------------------------------------
- // Pointer Arithmetic Helper Function
- //--------------------------------------------------------------------------
-
- ::llvm::IntegerType* getPtrEquivIntTy() {
- return getInt32Ty();
- }
-
- size_t getSizeOfPtrEquivInt() {
- return 4;
- }
-
- ::llvm::ConstantInt* getSizeOfPtrEquivIntValue() {
- return getPtrEquivInt(getSizeOfPtrEquivInt());
- }
-
- ::llvm::ConstantInt* getPtrEquivInt(int64_t i) {
- return ::llvm::ConstantInt::get(getPtrEquivIntTy(), i);
- }
-
- ::llvm::Value* CreatePtrDisp(::llvm::Value* base,
- ::llvm::Value* offset,
- ::llvm::PointerType* ret_ty) {
- ::llvm::Value* base_int = CreatePtrToInt(base, getPtrEquivIntTy());
- ::llvm::Value* result_int = CreateAdd(base_int, offset);
- ::llvm::Value* result = CreateIntToPtr(result_int, ret_ty);
-
- return result;
- }
-
- ::llvm::Value* CreatePtrDisp(::llvm::Value* base,
- ::llvm::Value* bs,
- ::llvm::Value* count,
- ::llvm::Value* offset,
- ::llvm::PointerType* ret_ty) {
- ::llvm::Value* block_offset = CreateMul(bs, count);
- ::llvm::Value* total_offset = CreateAdd(block_offset, offset);
-
- return CreatePtrDisp(base, total_offset, ret_ty);
- }
-
- ::llvm::LoadInst* LoadFromObjectOffset(::llvm::Value* object_addr,
- int64_t offset,
- ::llvm::Type* type,
- ::llvm::MDNode* tbaa_info) {
- // Convert offset to ::llvm::value
- ::llvm::Value* llvm_offset = getPtrEquivInt(offset);
- // Calculate the value's address
- ::llvm::Value* value_addr = CreatePtrDisp(object_addr, llvm_offset, type->getPointerTo());
- // Load
- return CreateLoad(value_addr, tbaa_info);
- }
-
- void StoreToObjectOffset(::llvm::Value* object_addr,
- int64_t offset,
- ::llvm::Value* new_value,
- ::llvm::MDNode* tbaa_info) {
- // Convert offset to ::llvm::value
- ::llvm::Value* llvm_offset = getPtrEquivInt(offset);
- // Calculate the value's address
- ::llvm::Value* value_addr = CreatePtrDisp(object_addr,
- llvm_offset,
- new_value->getType()->getPointerTo());
- // Store
- CreateStore(new_value, value_addr, tbaa_info);
- }
-
- ::llvm::AtomicCmpXchgInst* CompareExchangeObjectOffset(::llvm::Value* object_addr,
- int64_t offset,
- ::llvm::Value* cmp_value,
- ::llvm::Value* new_value,
- ::llvm::MDNode* tbaa_info) {
- // Convert offset to ::llvm::value
- ::llvm::Value* llvm_offset = getPtrEquivInt(offset);
- // Calculate the value's address
- ::llvm::Value* value_addr = CreatePtrDisp(object_addr,
- llvm_offset,
- new_value->getType()->getPointerTo());
- // Atomic compare and exchange
- return CreateAtomicCmpXchgInst(value_addr, cmp_value, new_value, tbaa_info);
- }
-
-
- //--------------------------------------------------------------------------
- // Runtime Helper Function
- //--------------------------------------------------------------------------
-
- RuntimeSupportBuilder& Runtime() {
- return *runtime_support_;
- }
-
- // TODO: Deprecate
- ::llvm::Function* GetRuntime(runtime_support::RuntimeId rt) {
- return runtime_support_->GetRuntimeSupportFunction(rt);
- }
-
- // TODO: Deprecate
- void SetRuntimeSupport(RuntimeSupportBuilder* runtime_support) {
- // Can only set once. We can't do this on constructor, because RuntimeSupportBuilder needs
- // IRBuilder.
- if (runtime_support_ == NULL && runtime_support != NULL) {
- runtime_support_ = runtime_support;
- }
- }
-
-
- //--------------------------------------------------------------------------
- // Type Helper Function
- //--------------------------------------------------------------------------
-
- ::llvm::Type* getJType(char shorty_jty) {
- return getJType(GetJTypeFromShorty(shorty_jty));
- }
-
- ::llvm::Type* getJType(JType jty);
-
- ::llvm::Type* getJVoidTy() {
- return getVoidTy();
- }
-
- ::llvm::IntegerType* getJBooleanTy() {
- return getInt8Ty();
- }
-
- ::llvm::IntegerType* getJByteTy() {
- return getInt8Ty();
- }
-
- ::llvm::IntegerType* getJCharTy() {
- return getInt16Ty();
- }
-
- ::llvm::IntegerType* getJShortTy() {
- return getInt16Ty();
- }
-
- ::llvm::IntegerType* getJIntTy() {
- return getInt32Ty();
- }
-
- ::llvm::IntegerType* getJLongTy() {
- return getInt64Ty();
- }
-
- ::llvm::Type* getJFloatTy() {
- return getFloatTy();
- }
-
- ::llvm::Type* getJDoubleTy() {
- return getDoubleTy();
- }
-
- ::llvm::PointerType* getJObjectTy() {
- return java_object_type_;
- }
-
- ::llvm::PointerType* getJMethodTy() {
- return java_method_type_;
- }
-
- ::llvm::PointerType* getJThreadTy() {
- return java_thread_type_;
- }
-
- ::llvm::Type* getArtFrameTy() {
- return art_frame_type_;
- }
-
- ::llvm::PointerType* getJEnvTy() {
- return jenv_type_;
- }
-
- ::llvm::Type* getJValueTy() {
- // NOTE: JValue is an union type, which may contains boolean, byte, char,
- // short, int, long, float, double, Object. However, LLVM itself does
- // not support union type, so we have to return a type with biggest size,
- // then bitcast it before we use it.
- return getJLongTy();
- }
-
- ::llvm::StructType* getShadowFrameTy(uint32_t vreg_size);
-
-
- //--------------------------------------------------------------------------
- // Constant Value Helper Function
- //--------------------------------------------------------------------------
-
- ::llvm::ConstantInt* getJBoolean(bool is_true) {
- return (is_true) ? getTrue() : getFalse();
- }
-
- ::llvm::ConstantInt* getJByte(int8_t i) {
- return ::llvm::ConstantInt::getSigned(getJByteTy(), i);
- }
-
- ::llvm::ConstantInt* getJChar(int16_t i) {
- return ::llvm::ConstantInt::getSigned(getJCharTy(), i);
- }
-
- ::llvm::ConstantInt* getJShort(int16_t i) {
- return ::llvm::ConstantInt::getSigned(getJShortTy(), i);
- }
-
- ::llvm::ConstantInt* getJInt(int32_t i) {
- return ::llvm::ConstantInt::getSigned(getJIntTy(), i);
- }
-
- ::llvm::ConstantInt* getJLong(int64_t i) {
- return ::llvm::ConstantInt::getSigned(getJLongTy(), i);
- }
-
- ::llvm::Constant* getJFloat(float f) {
- return ::llvm::ConstantFP::get(getJFloatTy(), f);
- }
-
- ::llvm::Constant* getJDouble(double d) {
- return ::llvm::ConstantFP::get(getJDoubleTy(), d);
- }
-
- ::llvm::ConstantPointerNull* getJNull() {
- return ::llvm::ConstantPointerNull::get(getJObjectTy());
- }
-
- ::llvm::Constant* getJZero(char shorty_jty) {
- return getJZero(GetJTypeFromShorty(shorty_jty));
- }
-
- ::llvm::Constant* getJZero(JType jty) {
- switch (jty) {
- case kVoid:
- LOG(FATAL) << "Zero is not a value of void type";
- return NULL;
-
- case kBoolean:
- return getJBoolean(false);
-
- case kByte:
- return getJByte(0);
-
- case kChar:
- return getJChar(0);
-
- case kShort:
- return getJShort(0);
-
- case kInt:
- return getJInt(0);
-
- case kLong:
- return getJLong(0);
-
- case kFloat:
- return getJFloat(0.0f);
-
- case kDouble:
- return getJDouble(0.0);
-
- case kObject:
- return getJNull();
-
- default:
- LOG(FATAL) << "Unknown java type: " << jty;
- return NULL;
- }
- }
-
-
- private:
- ::llvm::Module* module_;
-
- MDBuilder mdb_;
-
- ::llvm::PointerType* java_object_type_;
- ::llvm::PointerType* java_method_type_;
- ::llvm::PointerType* java_thread_type_;
-
- ::llvm::PointerType* jenv_type_;
-
- ::llvm::StructType* art_frame_type_;
-
- RuntimeSupportBuilder* runtime_support_;
-
- IntrinsicHelper& intrinsic_helper_;
-};
-
-
-} // namespace llvm
-} // namespace art
-
-#endif // ART_COMPILER_LLVM_IR_BUILDER_H_
diff --git a/compiler/llvm/llvm_compilation_unit.cc b/compiler/llvm/llvm_compilation_unit.cc
deleted file mode 100644
index 741c2d7..0000000
--- a/compiler/llvm/llvm_compilation_unit.cc
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// TODO: TargetLibraryInfo is included before sys/... because on Android bionic does #define tricks like:
-//
-// #define stat64 stat
-// #define fstat64 fstat
-// #define lstat64 lstat
-//
-// which causes grief. bionic probably should not do that.
-#include <llvm/Target/TargetLibraryInfo.h>
-
-#include "llvm_compilation_unit.h"
-
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <llvm/ADT/OwningPtr.h>
-#include <llvm/ADT/StringSet.h>
-#include <llvm/ADT/Triple.h>
-#include <llvm/Analysis/CallGraph.h>
-#include <llvm/Analysis/CallGraphSCCPass.h>
-#include <llvm/Analysis/Dominators.h>
-#include <llvm/Analysis/LoopInfo.h>
-#include <llvm/Analysis/LoopPass.h>
-#include <llvm/Analysis/RegionPass.h>
-#include <llvm/Analysis/ScalarEvolution.h>
-#include <llvm/Analysis/Verifier.h>
-#include <llvm/Assembly/PrintModulePass.h>
-#include <llvm/Bitcode/ReaderWriter.h>
-#include <llvm/CodeGen/MachineFrameInfo.h>
-#include <llvm/CodeGen/MachineFunction.h>
-#include <llvm/CodeGen/MachineFunctionPass.h>
-#include <llvm/DebugInfo.h>
-#include <llvm/IR/DataLayout.h>
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/LLVMContext.h>
-#include <llvm/IR/Module.h>
-#include <llvm/Object/ObjectFile.h>
-#include <llvm/PassManager.h>
-#include <llvm/Support/Debug.h>
-#include <llvm/Support/ELF.h>
-#include <llvm/Support/FormattedStream.h>
-#include <llvm/Support/ManagedStatic.h>
-#include <llvm/Support/MemoryBuffer.h>
-#include <llvm/Support/PassNameParser.h>
-#include <llvm/Support/PluginLoader.h>
-#include <llvm/Support/PrettyStackTrace.h>
-#include <llvm/Support/Signals.h>
-#include <llvm/Support/SystemUtils.h>
-#include <llvm/Support/TargetRegistry.h>
-#include <llvm/Support/TargetSelect.h>
-#include <llvm/Support/ToolOutputFile.h>
-#include <llvm/Support/raw_ostream.h>
-#include <llvm/Support/system_error.h>
-#include <llvm/Target/TargetMachine.h>
-#include <llvm/Transforms/IPO.h>
-#include <llvm/Transforms/IPO/PassManagerBuilder.h>
-#include <llvm/Transforms/Scalar.h>
-
-#include "base/logging.h"
-#include "base/unix_file/fd_file.h"
-#include "compiled_method.h"
-#include "compiler_llvm.h"
-#include "instruction_set.h"
-#include "ir_builder.h"
-#include "os.h"
-#include "runtime_support_builder_arm.h"
-#include "runtime_support_builder_x86.h"
-#include "utils_llvm.h"
-
-namespace art {
-namespace llvm {
-
-::llvm::FunctionPass*
-CreateGBCExpanderPass(const IntrinsicHelper& intrinsic_helper, IRBuilder& irb,
- CompilerDriver* compiler, const DexCompilationUnit* dex_compilation_unit);
-
-::llvm::Module* makeLLVMModuleContents(::llvm::Module* module);
-
-
-LlvmCompilationUnit::LlvmCompilationUnit(const CompilerLLVM* compiler_llvm, size_t cunit_id)
- : compiler_llvm_(compiler_llvm), cunit_id_(cunit_id) {
- driver_ = NULL;
- dex_compilation_unit_ = NULL;
- llvm_info_.reset(new LLVMInfo());
- context_.reset(llvm_info_->GetLLVMContext());
- module_ = llvm_info_->GetLLVMModule();
-
- // Include the runtime function declaration
- makeLLVMModuleContents(module_);
-
-
- intrinsic_helper_.reset(new IntrinsicHelper(*context_, *module_));
-
- // Create IRBuilder
- irb_.reset(new IRBuilder(*context_, *module_, *intrinsic_helper_));
-
- // We always need a switch case, so just use a normal function.
- switch (GetInstructionSet()) {
- default:
- runtime_support_.reset(new RuntimeSupportBuilder(*context_, *module_, *irb_));
- break;
- case kThumb2:
- case kArm:
- runtime_support_.reset(new RuntimeSupportBuilderARM(*context_, *module_, *irb_));
- break;
- case kX86:
- runtime_support_.reset(new RuntimeSupportBuilderX86(*context_, *module_, *irb_));
- break;
- }
-
- irb_->SetRuntimeSupport(runtime_support_.get());
-}
-
-
-LlvmCompilationUnit::~LlvmCompilationUnit() {
- ::llvm::LLVMContext* llvm_context = context_.release(); // Managed by llvm_info_
- CHECK(llvm_context != NULL);
-}
-
-
-InstructionSet LlvmCompilationUnit::GetInstructionSet() const {
- return compiler_llvm_->GetInstructionSet();
-}
-
-
-static std::string DumpDirectory() {
- if (kIsTargetBuild) {
- return GetDalvikCacheOrDie("llvm-dump");
- }
- return "/tmp";
-}
-
-void LlvmCompilationUnit::DumpBitcodeToFile() {
- std::string bitcode;
- DumpBitcodeToString(bitcode);
- std::string filename(StringPrintf("%s/Art%zu.bc", DumpDirectory().c_str(), cunit_id_));
- std::unique_ptr<File> output(OS::CreateEmptyFile(filename.c_str()));
- output->WriteFully(bitcode.data(), bitcode.size());
- LOG(INFO) << ".bc file written successfully: " << filename;
-}
-
-void LlvmCompilationUnit::DumpBitcodeToString(std::string& str_buffer) {
- ::llvm::raw_string_ostream str_os(str_buffer);
- ::llvm::WriteBitcodeToFile(module_, str_os);
-}
-
-bool LlvmCompilationUnit::Materialize() {
- const bool kDumpBitcode = false;
- if (kDumpBitcode) {
- // Dump the bitcode for debugging
- DumpBitcodeToFile();
- }
-
- // Compile and prelink ::llvm::Module
- if (!MaterializeToString(elf_object_)) {
- LOG(ERROR) << "Failed to materialize compilation unit " << cunit_id_;
- return false;
- }
-
- const bool kDumpELF = false;
- if (kDumpELF) {
- // Dump the ELF image for debugging
- std::string filename(StringPrintf("%s/Art%zu.o", DumpDirectory().c_str(), cunit_id_));
- std::unique_ptr<File> output(OS::CreateEmptyFile(filename.c_str()));
- output->WriteFully(elf_object_.data(), elf_object_.size());
- LOG(INFO) << ".o file written successfully: " << filename;
- }
-
- return true;
-}
-
-
-bool LlvmCompilationUnit::MaterializeToString(std::string& str_buffer) {
- ::llvm::raw_string_ostream str_os(str_buffer);
- return MaterializeToRawOStream(str_os);
-}
-
-
-bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_stream) {
- // Lookup the LLVM target
- std::string target_triple;
- std::string target_cpu;
- std::string target_attr;
- CompilerDriver::InstructionSetToLLVMTarget(GetInstructionSet(), &target_triple, &target_cpu,
- &target_attr);
-
- std::string errmsg;
- const ::llvm::Target* target =
- ::llvm::TargetRegistry::lookupTarget(target_triple, errmsg);
-
- CHECK(target != NULL) << errmsg;
-
- // Target options
- ::llvm::TargetOptions target_options;
- target_options.FloatABIType = ::llvm::FloatABI::Soft;
- target_options.NoFramePointerElim = true;
- target_options.UseSoftFloat = false;
- target_options.EnableFastISel = false;
-
- // Create the ::llvm::TargetMachine
- ::llvm::OwningPtr< ::llvm::TargetMachine> target_machine(
- target->createTargetMachine(target_triple, target_cpu, target_attr, target_options,
- ::llvm::Reloc::Static, ::llvm::CodeModel::Small,
- ::llvm::CodeGenOpt::Aggressive));
-
- CHECK(target_machine.get() != NULL) << "Failed to create target machine";
-
- // Add target data
- const ::llvm::DataLayout* data_layout = target_machine->getDataLayout();
-
- // PassManager for code generation passes
- ::llvm::PassManager pm;
- pm.add(new ::llvm::DataLayout(*data_layout));
-
- // FunctionPassManager for optimization pass
- ::llvm::FunctionPassManager fpm(module_);
- fpm.add(new ::llvm::DataLayout(*data_layout));
-
- if (bitcode_filename_.empty()) {
- // If we don't need write the bitcode to file, add the AddSuspendCheckToLoopLatchPass to the
- // regular FunctionPass.
- fpm.add(CreateGBCExpanderPass(*llvm_info_->GetIntrinsicHelper(), *irb_.get(),
- driver_, dex_compilation_unit_));
- } else {
- ::llvm::FunctionPassManager fpm2(module_);
- fpm2.add(CreateGBCExpanderPass(*llvm_info_->GetIntrinsicHelper(), *irb_.get(),
- driver_, dex_compilation_unit_));
- fpm2.doInitialization();
- for (::llvm::Module::iterator F = module_->begin(), E = module_->end();
- F != E; ++F) {
- fpm2.run(*F);
- }
- fpm2.doFinalization();
-
- // Write bitcode to file
- std::string errmsg;
-
- ::llvm::OwningPtr< ::llvm::tool_output_file> out_file(
- new ::llvm::tool_output_file(bitcode_filename_.c_str(), errmsg,
- ::llvm::sys::fs::F_Binary));
-
-
- if (!errmsg.empty()) {
- LOG(ERROR) << "Failed to create bitcode output file: " << errmsg;
- return false;
- }
-
- ::llvm::WriteBitcodeToFile(module_, out_file->os());
- out_file->keep();
- }
-
- // Add optimization pass
- ::llvm::PassManagerBuilder pm_builder;
- // TODO: Use inliner after we can do IPO.
- pm_builder.Inliner = NULL;
- // pm_builder.Inliner = ::llvm::createFunctionInliningPass();
- // pm_builder.Inliner = ::llvm::createAlwaysInlinerPass();
- // pm_builder.Inliner = ::llvm::createPartialInliningPass();
- pm_builder.OptLevel = 3;
- pm_builder.DisableUnitAtATime = 1;
- pm_builder.populateFunctionPassManager(fpm);
- pm_builder.populateModulePassManager(pm);
- pm.add(::llvm::createStripDeadPrototypesPass());
-
- // Add passes to emit ELF image
- {
- ::llvm::formatted_raw_ostream formatted_os(out_stream, false);
-
- // Ask the target to add backend passes as necessary.
- if (target_machine->addPassesToEmitFile(pm,
- formatted_os,
- ::llvm::TargetMachine::CGFT_ObjectFile,
- true)) {
- LOG(FATAL) << "Unable to generate ELF for this target";
- return false;
- }
-
- // Run the per-function optimization
- fpm.doInitialization();
- for (::llvm::Module::iterator F = module_->begin(), E = module_->end();
- F != E; ++F) {
- fpm.run(*F);
- }
- fpm.doFinalization();
-
- // Run the code generation passes
- pm.run(*module_);
- }
-
- return true;
-}
-
-// Check whether the align is less than or equal to the code alignment of
-// that architecture. Since the Oat writer only guarantee that the compiled
-// method being aligned to kArchAlignment, we have no way to align the ELf
-// section if the section alignment is greater than kArchAlignment.
-void LlvmCompilationUnit::CheckCodeAlign(uint32_t align) const {
- InstructionSet insn_set = GetInstructionSet();
- size_t insn_set_align = GetInstructionSetAlignment(insn_set);
- CHECK_LE(align, static_cast<uint32_t>(insn_set_align));
-}
-
-
-} // namespace llvm
-} // namespace art
diff --git a/compiler/llvm/llvm_compilation_unit.h b/compiler/llvm/llvm_compilation_unit.h
deleted file mode 100644
index f11fb6e..0000000
--- a/compiler/llvm/llvm_compilation_unit.h
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LLVM_LLVM_COMPILATION_UNIT_H_
-#define ART_COMPILER_LLVM_LLVM_COMPILATION_UNIT_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/logging.h"
-#include "base/mutex.h"
-#include "dex/compiler_internals.h"
-#include "driver/compiler_driver.h"
-#include "driver/dex_compilation_unit.h"
-#include "globals.h"
-#include "instruction_set.h"
-#include "runtime_support_builder.h"
-#include "runtime_support_llvm_func.h"
-#include "safe_map.h"
-
-namespace art {
- class CompiledMethod;
-}
-
-namespace llvm {
- class Function;
- class LLVMContext;
- class Module;
- class raw_ostream;
-}
-
-namespace art {
-namespace llvm {
-
-class CompilerLLVM;
-class IRBuilder;
-
-class LlvmCompilationUnit {
- public:
- ~LlvmCompilationUnit();
-
- uint32_t GetCompilationUnitId() const {
- return cunit_id_;
- }
-
- InstructionSet GetInstructionSet() const;
-
- ::llvm::LLVMContext* GetLLVMContext() const {
- return context_.get();
- }
-
- ::llvm::Module* GetModule() const {
- return module_;
- }
-
- IRBuilder* GetIRBuilder() const {
- return irb_.get();
- }
-
- void SetBitcodeFileName(const std::string& bitcode_filename) {
- bitcode_filename_ = bitcode_filename;
- }
-
- LLVMInfo* GetQuickContext() const {
- return llvm_info_.get();
- }
- void SetCompilerDriver(CompilerDriver* driver) {
- driver_ = driver;
- }
- DexCompilationUnit* GetDexCompilationUnit() {
- return dex_compilation_unit_;
- }
- void SetDexCompilationUnit(DexCompilationUnit* dex_compilation_unit) {
- dex_compilation_unit_ = dex_compilation_unit;
- }
-
- bool Materialize();
-
- bool IsMaterialized() const {
- return !elf_object_.empty();
- }
-
- const std::string& GetElfObject() const {
- DCHECK(IsMaterialized());
- return elf_object_;
- }
-
- private:
- LlvmCompilationUnit(const CompilerLLVM* compiler_llvm,
- size_t cunit_id);
-
- const CompilerLLVM* compiler_llvm_;
- const size_t cunit_id_;
-
- std::unique_ptr< ::llvm::LLVMContext> context_;
- std::unique_ptr<IRBuilder> irb_;
- std::unique_ptr<RuntimeSupportBuilder> runtime_support_;
- ::llvm::Module* module_; // Managed by context_
- std::unique_ptr<IntrinsicHelper> intrinsic_helper_;
- std::unique_ptr<LLVMInfo> llvm_info_;
- CompilerDriver* driver_;
- DexCompilationUnit* dex_compilation_unit_;
-
- std::string bitcode_filename_;
-
- std::string elf_object_;
-
- SafeMap<const ::llvm::Function*, CompiledMethod*> compiled_methods_map_;
-
- void CheckCodeAlign(uint32_t offset) const;
-
- void DumpBitcodeToFile();
- void DumpBitcodeToString(std::string& str_buffer);
-
- bool MaterializeToString(std::string& str_buffer);
- bool MaterializeToRawOStream(::llvm::raw_ostream& out_stream);
-
- friend class CompilerLLVM; // For LlvmCompilationUnit constructor
-};
-
-} // namespace llvm
-} // namespace art
-
-#endif // ART_COMPILER_LLVM_LLVM_COMPILATION_UNIT_H_
diff --git a/compiler/llvm/llvm_compiler.cc b/compiler/llvm/llvm_compiler.cc
deleted file mode 100644
index fa93e00..0000000
--- a/compiler/llvm/llvm_compiler.cc
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "llvm_compiler.h"
-
-#include "base/macros.h"
-#ifdef ART_USE_PORTABLE_COMPILER
-#include "compiler.h"
-#include "compiler_llvm.h"
-#include "dex/portable/mir_to_gbc.h"
-#include "dex_file.h"
-#include "elf_writer_mclinker.h"
-#include "mirror/art_method-inl.h"
-#endif
-
-namespace art {
-
-#ifdef ART_USE_PORTABLE_COMPILER
-
-namespace llvm {
-
-// Thread-local storage compiler worker threads
-class LLVMCompilerTls : public CompilerTls {
- public:
- LLVMCompilerTls() : llvm_info_(nullptr) {}
- ~LLVMCompilerTls() {}
-
- void* GetLLVMInfo() { return llvm_info_; }
-
- void SetLLVMInfo(void* llvm_info) { llvm_info_ = llvm_info; }
-
- private:
- void* llvm_info_;
-};
-
-
-
-class LLVMCompiler FINAL : public Compiler {
- public:
- explicit LLVMCompiler(CompilerDriver* driver) : Compiler(driver, 1000) {}
-
- CompilerTls* CreateNewCompilerTls() {
- return new LLVMCompilerTls();
- }
-
- void Init() const OVERRIDE {
- ArtInitCompilerContext(GetCompilerDriver());
- }
-
- void UnInit() const OVERRIDE {
- ArtUnInitCompilerContext(GetCompilerDriver());
- }
-
- bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const
- OVERRIDE {
- return true;
- }
-
- CompiledMethod* Compile(const DexFile::CodeItem* code_item,
- uint32_t access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const DexFile& dex_file) const OVERRIDE {
- CompiledMethod* method = TryCompileWithSeaIR(code_item,
- access_flags,
- invoke_type,
- class_def_idx,
- method_idx,
- class_loader,
- dex_file);
- if (method != nullptr) {
- return method;
- }
-
- return ArtCompileMethod(GetCompilerDriver(),
- code_item,
- access_flags,
- invoke_type,
- class_def_idx,
- method_idx,
- class_loader,
- dex_file);
- }
-
- CompiledMethod* JniCompile(uint32_t access_flags,
- uint32_t method_idx,
- const DexFile& dex_file) const OVERRIDE {
- return ArtLLVMJniCompileMethod(GetCompilerDriver(), access_flags, method_idx, dex_file);
- }
-
- uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const {
- return reinterpret_cast<uintptr_t>(method->GetEntryPointFromPortableCompiledCode());
- }
-
- bool WriteElf(art::File* file,
- OatWriter* oat_writer,
- const std::vector<const art::DexFile*>& dex_files,
- const std::string& android_root,
- bool is_host, const CompilerDriver& driver) const
- OVERRIDE
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return art::ElfWriterMclinker::Create(
- file, oat_writer, dex_files, android_root, is_host, driver);
- }
-
- Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
- return PortableCodeGenerator(
- cu, cu->mir_graph.get(), &cu->arena,
- reinterpret_cast<art::llvm::LlvmCompilationUnit*>(compilation_unit));
- }
-
- void InitCompilationUnit(CompilationUnit& cu) const {
- // Fused long branches not currently useful in bitcode.
- cu.disable_opt |=
- (1 << kBranchFusing) |
- (1 << kSuppressExceptionEdges);
- }
-
- bool IsPortable() const OVERRIDE {
- return true;
- }
-
- void SetBitcodeFileName(const CompilerDriver& driver, const std::string& filename) {
- typedef void (*SetBitcodeFileNameFn)(const CompilerDriver&, const std::string&);
-
- SetBitcodeFileNameFn set_bitcode_file_name =
- reinterpret_cast<SetBitcodeFileNameFn>(compilerLLVMSetBitcodeFileName);
-
- set_bitcode_file_name(driver, filename);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(LLVMCompiler);
-};
-
-} // namespace llvm
-#endif
-
-Compiler* CreateLLVMCompiler(CompilerDriver* driver) {
-#ifdef ART_USE_PORTABLE_COMPILER
- return new llvm::LLVMCompiler(driver);
-#else
- UNUSED(driver);
- return nullptr;
-#endif
-}
-
-} // namespace art
diff --git a/compiler/llvm/md_builder.cc b/compiler/llvm/md_builder.cc
deleted file mode 100644
index 4331557..0000000
--- a/compiler/llvm/md_builder.cc
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#include "md_builder.h"
-
-#include "llvm/IR/MDBuilder.h"
-
-#include <string>
-
-namespace art {
-namespace llvm {
-
-
-::llvm::MDNode* MDBuilder::GetTBAASpecialType(TBAASpecialType sty_id) {
- DCHECK_GE(sty_id, 0) << "Unknown TBAA special type: " << sty_id;
- DCHECK_LT(sty_id, MAX_TBAA_SPECIAL_TYPE) << "Unknown TBAA special type: " << sty_id;
- DCHECK(tbaa_root_ != NULL);
-
- ::llvm::MDNode*& spec_ty = tbaa_special_type_[sty_id];
- if (spec_ty == NULL) {
- switch (sty_id) {
- case kTBAARegister:
- spec_ty = createTBAANode("Register", tbaa_root_);
- break;
- case kTBAAStackTemp:
- spec_ty = createTBAANode("StackTemp", tbaa_root_);
- break;
- case kTBAAHeapArray:
- spec_ty = createTBAANode("HeapArray", tbaa_root_);
- break;
- case kTBAAHeapInstance:
- spec_ty = createTBAANode("HeapInstance", tbaa_root_);
- break;
- case kTBAAHeapStatic:
- spec_ty = createTBAANode("HeapStatic", tbaa_root_);
- break;
- case kTBAAJRuntime:
- spec_ty = createTBAANode("JRuntime", tbaa_root_);
- break;
- case kTBAARuntimeInfo:
- spec_ty = createTBAANode("RuntimeInfo", GetTBAASpecialType(kTBAAJRuntime));
- break;
- case kTBAAShadowFrame:
- spec_ty = createTBAANode("ShadowFrame", GetTBAASpecialType(kTBAAJRuntime));
- break;
- case kTBAAConstJObject:
- spec_ty = createTBAANode("ConstJObject", tbaa_root_, true);
- break;
- default:
- LOG(FATAL) << "Unknown TBAA special type: " << sty_id;
- break;
- }
- }
- return spec_ty;
-}
-
-::llvm::MDNode* MDBuilder::GetTBAAMemoryJType(TBAASpecialType sty_id, JType jty_id) {
- DCHECK(sty_id == kTBAAHeapArray ||
- sty_id == kTBAAHeapInstance ||
- sty_id == kTBAAHeapStatic) << "SpecialType must be array, instance, or static";
-
- DCHECK_GE(jty_id, 0) << "Unknown JType: " << jty_id;
- DCHECK_LT(jty_id, MAX_JTYPE) << "Unknown JType: " << jty_id;
- DCHECK_NE(jty_id, kVoid) << "Can't load/store Void type!";
-
- std::string name;
- size_t sty_mapped_index = 0;
- switch (sty_id) {
- case kTBAAHeapArray: sty_mapped_index = 0; name = "HeapArray "; break;
- case kTBAAHeapInstance: sty_mapped_index = 1; name = "HeapInstance "; break;
- case kTBAAHeapStatic: sty_mapped_index = 2; name = "HeapStatic "; break;
- default:
- LOG(FATAL) << "Unknown TBAA special type: " << sty_id;
- break;
- }
-
- ::llvm::MDNode*& spec_ty = tbaa_memory_jtype_[sty_mapped_index][jty_id];
- if (spec_ty != NULL) {
- return spec_ty;
- }
-
- switch (jty_id) {
- case kBoolean: name += "Boolean"; break;
- case kByte: name += "Byte"; break;
- case kChar: name += "Char"; break;
- case kShort: name += "Short"; break;
- case kInt: name += "Int"; break;
- case kLong: name += "Long"; break;
- case kFloat: name += "Float"; break;
- case kDouble: name += "Double"; break;
- case kObject: name += "Object"; break;
- default:
- LOG(FATAL) << "Unknown JType: " << jty_id;
- break;
- }
-
- spec_ty = createTBAANode(name, GetTBAASpecialType(sty_id));
- return spec_ty;
-}
-
-
-} // namespace llvm
-} // namespace art
diff --git a/compiler/llvm/md_builder.h b/compiler/llvm/md_builder.h
deleted file mode 100644
index 1246f9b..0000000
--- a/compiler/llvm/md_builder.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LLVM_MD_BUILDER_H_
-#define ART_COMPILER_LLVM_MD_BUILDER_H_
-
-#include "backend_types.h"
-
-#include "llvm/IR/MDBuilder.h"
-
-#include <cstring>
-
-namespace llvm {
- class LLVMContext;
- class MDNode;
-}
-
-namespace art {
-namespace llvm {
-
-typedef ::llvm::MDBuilder LLVMMDBuilder;
-
-class MDBuilder : public LLVMMDBuilder {
- public:
- explicit MDBuilder(::llvm::LLVMContext& context)
- : LLVMMDBuilder(context), tbaa_root_(createTBAARoot("Art TBAA Root")) {
- std::memset(tbaa_special_type_, 0, sizeof(tbaa_special_type_));
- std::memset(tbaa_memory_jtype_, 0, sizeof(tbaa_memory_jtype_));
-
- // Pre-generate the MDNode for static branch prediction
- // 64 and 4 are the llvm.expect's default values
- expect_cond_[kLikely] = createBranchWeights(64, 4);
- expect_cond_[kUnlikely] = createBranchWeights(4, 64);
- }
-
- ::llvm::MDNode* GetTBAASpecialType(TBAASpecialType special_ty);
- ::llvm::MDNode* GetTBAAMemoryJType(TBAASpecialType special_ty, JType j_ty);
-
- ::llvm::MDNode* GetBranchWeights(ExpectCond expect) {
- DCHECK_LT(expect, MAX_EXPECT) << "MAX_EXPECT is not for branch weight";
- return expect_cond_[expect];
- }
-
- private:
- ::llvm::MDNode* const tbaa_root_;
- ::llvm::MDNode* tbaa_special_type_[MAX_TBAA_SPECIAL_TYPE];
- // There are 3 categories of memory types will not alias: array element, instance field, and
- // static field.
- ::llvm::MDNode* tbaa_memory_jtype_[3][MAX_JTYPE];
-
- ::llvm::MDNode* expect_cond_[MAX_EXPECT];
-};
-
-
-} // namespace llvm
-} // namespace art
-
-#endif // ART_COMPILER_LLVM_MD_BUILDER_H_
diff --git a/compiler/llvm/runtime_support_builder.cc b/compiler/llvm/runtime_support_builder.cc
deleted file mode 100644
index c825fbf..0000000
--- a/compiler/llvm/runtime_support_builder.cc
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "runtime_support_builder.h"
-
-#include "gc/accounting/card_table.h"
-#include "ir_builder.h"
-#include "monitor.h"
-#include "mirror/object.h"
-#include "runtime_support_llvm_func_list.h"
-#include "thread.h"
-
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/Function.h>
-#include <llvm/IR/Module.h>
-#include <llvm/IR/Type.h>
-
-using ::llvm::BasicBlock;
-using ::llvm::CallInst;
-using ::llvm::Function;
-using ::llvm::Value;
-
-namespace art {
-namespace llvm {
-
-RuntimeSupportBuilder::RuntimeSupportBuilder(::llvm::LLVMContext& context,
- ::llvm::Module& module,
- IRBuilder& irb)
- : context_(context), module_(module), irb_(irb) {
- memset(target_runtime_support_func_, 0, sizeof(target_runtime_support_func_));
-#define GET_RUNTIME_SUPPORT_FUNC_DECL(ID, NAME) \
- do { \
- ::llvm::Function* fn = module_.getFunction(#NAME); \
- DCHECK(fn != NULL) << "Function not found: " << #NAME; \
- runtime_support_func_decls_[runtime_support::ID] = fn; \
- } while (0);
-
- RUNTIME_SUPPORT_FUNC_LIST(GET_RUNTIME_SUPPORT_FUNC_DECL)
-}
-
-
-/* Thread */
-
-::llvm::Value* RuntimeSupportBuilder::EmitGetCurrentThread() {
- Function* func = GetRuntimeSupportFunction(runtime_support::GetCurrentThread);
- CallInst* call_inst = irb_.CreateCall(func);
- call_inst->setOnlyReadsMemory();
- irb_.SetTBAA(call_inst, kTBAAConstJObject);
- return call_inst;
-}
-
-::llvm::Value* RuntimeSupportBuilder::EmitLoadFromThreadOffset(int64_t offset, ::llvm::Type* type,
- TBAASpecialType s_ty) {
- Value* thread = EmitGetCurrentThread();
- return irb_.LoadFromObjectOffset(thread, offset, type, s_ty);
-}
-
-void RuntimeSupportBuilder::EmitStoreToThreadOffset(int64_t offset, ::llvm::Value* value,
- TBAASpecialType s_ty) {
- Value* thread = EmitGetCurrentThread();
- irb_.StoreToObjectOffset(thread, offset, value, s_ty);
-}
-
-::llvm::Value* RuntimeSupportBuilder::EmitSetCurrentThread(::llvm::Value* thread) {
- Function* func = GetRuntimeSupportFunction(runtime_support::SetCurrentThread);
- return irb_.CreateCall(func, thread);
-}
-
-
-/* ShadowFrame */
-
-::llvm::Value* RuntimeSupportBuilder::EmitPushShadowFrame(::llvm::Value* new_shadow_frame,
- ::llvm::Value* method,
- uint32_t num_vregs) {
- Value* old_shadow_frame = EmitLoadFromThreadOffset(Thread::TopShadowFrameOffset().Int32Value(),
- irb_.getArtFrameTy()->getPointerTo(),
- kTBAARuntimeInfo);
- EmitStoreToThreadOffset(Thread::TopShadowFrameOffset().Int32Value(),
- new_shadow_frame,
- kTBAARuntimeInfo);
-
- // Store the method pointer
- irb_.StoreToObjectOffset(new_shadow_frame,
- ShadowFrame::MethodOffset(),
- method,
- kTBAAShadowFrame);
-
- // Store the number of vregs
- irb_.StoreToObjectOffset(new_shadow_frame,
- ShadowFrame::NumberOfVRegsOffset(),
- irb_.getInt32(num_vregs),
- kTBAAShadowFrame);
-
- // Store the link to previous shadow frame
- irb_.StoreToObjectOffset(new_shadow_frame,
- ShadowFrame::LinkOffset(),
- old_shadow_frame,
- kTBAAShadowFrame);
-
- return old_shadow_frame;
-}
-
-::llvm::Value*
-RuntimeSupportBuilder::EmitPushShadowFrameNoInline(::llvm::Value* new_shadow_frame,
- ::llvm::Value* method,
- uint32_t num_vregs) {
- Function* func = GetRuntimeSupportFunction(runtime_support::PushShadowFrame);
- ::llvm::CallInst* call_inst =
- irb_.CreateCall4(func,
- EmitGetCurrentThread(),
- new_shadow_frame,
- method,
- irb_.getInt32(num_vregs));
- irb_.SetTBAA(call_inst, kTBAARuntimeInfo);
- return call_inst;
-}
-
-void RuntimeSupportBuilder::EmitPopShadowFrame(::llvm::Value* old_shadow_frame) {
- // Store old shadow frame to TopShadowFrame
- EmitStoreToThreadOffset(Thread::TopShadowFrameOffset().Int32Value(),
- old_shadow_frame,
- kTBAARuntimeInfo);
-}
-
-
-/* Exception */
-
-::llvm::Value* RuntimeSupportBuilder::EmitGetAndClearException() {
- Function* slow_func = GetRuntimeSupportFunction(runtime_support::GetAndClearException);
- return irb_.CreateCall(slow_func, EmitGetCurrentThread());
-}
-
-::llvm::Value* RuntimeSupportBuilder::EmitIsExceptionPending() {
- Value* exception = EmitLoadFromThreadOffset(Thread::ExceptionOffset().Int32Value(),
- irb_.getJObjectTy(),
- kTBAARuntimeInfo);
- // If exception not null
- return irb_.CreateIsNotNull(exception);
-}
-
-
-/* Suspend */
-
-void RuntimeSupportBuilder::EmitTestSuspend() {
- Function* slow_func = GetRuntimeSupportFunction(runtime_support::TestSuspend);
- CallInst* call_inst = irb_.CreateCall(slow_func, EmitGetCurrentThread());
- irb_.SetTBAA(call_inst, kTBAAJRuntime);
-}
-
-
-/* Monitor */
-
-void RuntimeSupportBuilder::EmitLockObject(::llvm::Value* object) {
- Function* slow_func = GetRuntimeSupportFunction(runtime_support::LockObject);
- irb_.CreateCall2(slow_func, object, EmitGetCurrentThread());
-}
-
-void RuntimeSupportBuilder::EmitUnlockObject(::llvm::Value* object) {
- Function* slow_func = GetRuntimeSupportFunction(runtime_support::UnlockObject);
- irb_.CreateCall2(slow_func, object, EmitGetCurrentThread());
-}
-
-
-void RuntimeSupportBuilder::EmitMarkGCCard(::llvm::Value* value, ::llvm::Value* target_addr) {
- Function* parent_func = irb_.GetInsertBlock()->getParent();
- BasicBlock* bb_mark_gc_card = BasicBlock::Create(context_, "mark_gc_card", parent_func);
- BasicBlock* bb_cont = BasicBlock::Create(context_, "mark_gc_card_cont", parent_func);
-
- ::llvm::Value* not_null = irb_.CreateIsNotNull(value);
- irb_.CreateCondBr(not_null, bb_mark_gc_card, bb_cont);
-
- irb_.SetInsertPoint(bb_mark_gc_card);
- Value* card_table = EmitLoadFromThreadOffset(Thread::CardTableOffset().Int32Value(),
- irb_.getInt8Ty()->getPointerTo(),
- kTBAAConstJObject);
- Value* target_addr_int = irb_.CreatePtrToInt(target_addr, irb_.getPtrEquivIntTy());
- Value* card_no = irb_.CreateLShr(target_addr_int,
- irb_.getPtrEquivInt(gc::accounting::CardTable::kCardShift));
- Value* card_table_entry = irb_.CreateGEP(card_table, card_no);
- irb_.CreateStore(irb_.getInt8(gc::accounting::CardTable::kCardDirty), card_table_entry,
- kTBAARuntimeInfo);
- irb_.CreateBr(bb_cont);
-
- irb_.SetInsertPoint(bb_cont);
-}
-
-
-} // namespace llvm
-} // namespace art
diff --git a/compiler/llvm/runtime_support_builder.h b/compiler/llvm/runtime_support_builder.h
deleted file mode 100644
index 898611a..0000000
--- a/compiler/llvm/runtime_support_builder.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_H_
-#define ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_H_
-
-#include "backend_types.h"
-#include "base/logging.h"
-#include "runtime_support_llvm_func.h"
-
-#include <stdint.h>
-
-namespace llvm {
- class LLVMContext;
- class Module;
- class Function;
- class Type;
- class Value;
-}
-
-namespace art {
-namespace llvm {
-
-class IRBuilder;
-
-
-class RuntimeSupportBuilder {
- public:
- RuntimeSupportBuilder(::llvm::LLVMContext& context, ::llvm::Module& module, IRBuilder& irb);
-
- /* Thread */
- virtual ::llvm::Value* EmitGetCurrentThread();
- virtual ::llvm::Value* EmitLoadFromThreadOffset(int64_t offset, ::llvm::Type* type,
- TBAASpecialType s_ty);
- virtual void EmitStoreToThreadOffset(int64_t offset, ::llvm::Value* value,
- TBAASpecialType s_ty);
- virtual ::llvm::Value* EmitSetCurrentThread(::llvm::Value* thread);
-
- /* ShadowFrame */
- virtual ::llvm::Value* EmitPushShadowFrame(::llvm::Value* new_shadow_frame,
- ::llvm::Value* method, uint32_t num_vregs);
- virtual ::llvm::Value* EmitPushShadowFrameNoInline(::llvm::Value* new_shadow_frame,
- ::llvm::Value* method, uint32_t num_vregs);
- virtual void EmitPopShadowFrame(::llvm::Value* old_shadow_frame);
-
- /* Exception */
- virtual ::llvm::Value* EmitGetAndClearException();
- virtual ::llvm::Value* EmitIsExceptionPending();
-
- /* Suspend */
- virtual void EmitTestSuspend();
-
- /* Monitor */
- void EmitLockObject(::llvm::Value* object);
- void EmitUnlockObject(::llvm::Value* object);
-
- /* MarkGCCard */
- virtual void EmitMarkGCCard(::llvm::Value* value, ::llvm::Value* target_addr);
-
- ::llvm::Function* GetRuntimeSupportFunction(runtime_support::RuntimeId id) {
- if (id >= 0 && id < runtime_support::MAX_ID) {
- return runtime_support_func_decls_[id];
- } else {
- LOG(ERROR) << "Unknown runtime function id: " << id;
- return NULL;
- }
- }
-
- virtual ~RuntimeSupportBuilder() {}
-
- protected:
- ::llvm::LLVMContext& context_;
- ::llvm::Module& module_;
- IRBuilder& irb_;
-
- private:
- ::llvm::Function* runtime_support_func_decls_[runtime_support::MAX_ID];
- bool target_runtime_support_func_[runtime_support::MAX_ID];
-};
-
-
-} // namespace llvm
-} // namespace art
-
-#endif // ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_H_
diff --git a/compiler/llvm/runtime_support_builder_arm.cc b/compiler/llvm/runtime_support_builder_arm.cc
deleted file mode 100644
index cad4624..0000000
--- a/compiler/llvm/runtime_support_builder_arm.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "runtime_support_builder_arm.h"
-
-#include "ir_builder.h"
-#include "thread.h"
-#include "utils_llvm.h"
-
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/Function.h>
-#include <llvm/IR/InlineAsm.h>
-#include <llvm/IR/Module.h>
-#include <llvm/IR/Type.h>
-
-#include <vector>
-
-using ::llvm::CallInst;
-using ::llvm::Function;
-using ::llvm::FunctionType;
-using ::llvm::InlineAsm;
-using ::llvm::IntegerType;
-using ::llvm::Type;
-using ::llvm::Value;
-
-namespace {
-
-char LDRSTRSuffixByType(art::llvm::IRBuilder& irb, Type* type) {
- int width = type->isPointerTy() ?
- irb.getSizeOfPtrEquivInt()*8 :
- ::llvm::cast<IntegerType>(type)->getBitWidth();
- switch (width) {
- case 8: return 'b';
- case 16: return 'h';
- case 32: return ' ';
- default:
- LOG(FATAL) << "Unsupported width: " << width;
- return ' ';
- }
-}
-
-} // namespace
-
-namespace art {
-namespace llvm {
-
-/* Thread */
-
-Value* RuntimeSupportBuilderARM::EmitGetCurrentThread() {
- Function* ori_func = GetRuntimeSupportFunction(runtime_support::GetCurrentThread);
- InlineAsm* func = InlineAsm::get(ori_func->getFunctionType(), "mov $0, r9", "=r", false);
- CallInst* thread = irb_.CreateCall(func);
- thread->setDoesNotAccessMemory();
- irb_.SetTBAA(thread, kTBAAConstJObject);
- return thread;
-}
-
-Value* RuntimeSupportBuilderARM::EmitLoadFromThreadOffset(int64_t offset, ::llvm::Type* type,
- TBAASpecialType s_ty) {
- FunctionType* func_ty = FunctionType::get(/*Result=*/type,
- /*isVarArg=*/false);
- std::string inline_asm(StringPrintf("ldr%c $0, [r9, #%d]",
- LDRSTRSuffixByType(irb_, type),
- static_cast<int>(offset)));
- InlineAsm* func = InlineAsm::get(func_ty, inline_asm, "=r", true);
- CallInst* result = irb_.CreateCall(func);
- result->setOnlyReadsMemory();
- irb_.SetTBAA(result, s_ty);
- return result;
-}
-
-void RuntimeSupportBuilderARM::EmitStoreToThreadOffset(int64_t offset, Value* value,
- TBAASpecialType s_ty) {
- FunctionType* func_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_),
- /*Params=*/value->getType(),
- /*isVarArg=*/false);
- std::string inline_asm(StringPrintf("str%c $0, [r9, #%d]",
- LDRSTRSuffixByType(irb_, value->getType()),
- static_cast<int>(offset)));
- InlineAsm* func = InlineAsm::get(func_ty, inline_asm, "r", true);
- CallInst* call_inst = irb_.CreateCall(func, value);
- irb_.SetTBAA(call_inst, s_ty);
-}
-
-Value* RuntimeSupportBuilderARM::EmitSetCurrentThread(Value* thread) {
- // Separate to two InlineAsm: The first one produces the return value, while the second,
- // sets the current thread.
- // LLVM can delete the first one if the caller in LLVM IR doesn't use the return value.
- //
- // Here we don't call EmitGetCurrentThread, because we mark it as DoesNotAccessMemory and
- // ConstJObject. We denote side effect to "true" below instead, so LLVM won't
- // reorder these instructions incorrectly.
- Function* ori_func = GetRuntimeSupportFunction(runtime_support::GetCurrentThread);
- InlineAsm* func = InlineAsm::get(ori_func->getFunctionType(), "mov $0, r9", "=r", true);
- CallInst* old_thread_register = irb_.CreateCall(func);
- old_thread_register->setOnlyReadsMemory();
-
- FunctionType* func_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_),
- /*Params=*/irb_.getJObjectTy(),
- /*isVarArg=*/false);
- func = InlineAsm::get(func_ty, "mov r9, $0", "r", true);
- irb_.CreateCall(func, thread);
- return old_thread_register;
-}
-
-} // namespace llvm
-} // namespace art
diff --git a/compiler/llvm/runtime_support_builder_arm.h b/compiler/llvm/runtime_support_builder_arm.h
deleted file mode 100644
index 0d01509..0000000
--- a/compiler/llvm/runtime_support_builder_arm.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_ARM_H_
-#define ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_ARM_H_
-
-#include "runtime_support_builder.h"
-
-namespace art {
-namespace llvm {
-
-class RuntimeSupportBuilderARM : public RuntimeSupportBuilder {
- public:
- RuntimeSupportBuilderARM(::llvm::LLVMContext& context, ::llvm::Module& module, IRBuilder& irb)
- : RuntimeSupportBuilder(context, module, irb) {}
-
- /* Thread */
- virtual ::llvm::Value* EmitGetCurrentThread();
- virtual ::llvm::Value* EmitLoadFromThreadOffset(int64_t offset, ::llvm::Type* type,
- TBAASpecialType s_ty);
- virtual void EmitStoreToThreadOffset(int64_t offset, ::llvm::Value* value,
- TBAASpecialType s_ty);
- virtual ::llvm::Value* EmitSetCurrentThread(::llvm::Value* thread);
-};
-
-} // namespace llvm
-} // namespace art
-
-#endif // ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_ARM_H_
diff --git a/compiler/llvm/runtime_support_builder_x86.cc b/compiler/llvm/runtime_support_builder_x86.cc
deleted file mode 100644
index 3d11f9d..0000000
--- a/compiler/llvm/runtime_support_builder_x86.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "runtime_support_builder_x86.h"
-
-#include "base/stringprintf.h"
-#include "ir_builder.h"
-#include "thread.h"
-#include "utils_llvm.h"
-
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/Function.h>
-#include <llvm/IR/InlineAsm.h>
-#include <llvm/IR/Module.h>
-#include <llvm/IR/Type.h>
-
-#include <vector>
-
-using ::llvm::CallInst;
-using ::llvm::Function;
-using ::llvm::FunctionType;
-using ::llvm::InlineAsm;
-using ::llvm::Type;
-using ::llvm::UndefValue;
-using ::llvm::Value;
-
-namespace art {
-namespace llvm {
-
-
-Value* RuntimeSupportBuilderX86::EmitGetCurrentThread() {
- Function* ori_func = GetRuntimeSupportFunction(runtime_support::GetCurrentThread);
- std::string inline_asm(StringPrintf("mov %%fs:%d, $0", Thread::SelfOffset().Int32Value()));
- InlineAsm* func = InlineAsm::get(ori_func->getFunctionType(), inline_asm, "=r", false);
- CallInst* thread = irb_.CreateCall(func);
- thread->setDoesNotAccessMemory();
- irb_.SetTBAA(thread, kTBAAConstJObject);
- return thread;
-}
-
-Value* RuntimeSupportBuilderX86::EmitLoadFromThreadOffset(int64_t offset, Type* type,
- TBAASpecialType s_ty) {
- FunctionType* func_ty = FunctionType::get(/*Result=*/type,
- /*isVarArg=*/false);
- std::string inline_asm(StringPrintf("mov %%fs:%d, $0", static_cast<int>(offset)));
- InlineAsm* func = InlineAsm::get(func_ty, inline_asm, "=r", true);
- CallInst* result = irb_.CreateCall(func);
- result->setOnlyReadsMemory();
- irb_.SetTBAA(result, s_ty);
- return result;
-}
-
-void RuntimeSupportBuilderX86::EmitStoreToThreadOffset(int64_t offset, Value* value,
- TBAASpecialType s_ty) {
- FunctionType* func_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_),
- /*Params=*/value->getType(),
- /*isVarArg=*/false);
- std::string inline_asm(StringPrintf("mov $0, %%fs:%d", static_cast<int>(offset)));
- InlineAsm* func = InlineAsm::get(func_ty, inline_asm, "r", true);
- CallInst* call_inst = irb_.CreateCall(func, value);
- irb_.SetTBAA(call_inst, s_ty);
-}
-
-Value* RuntimeSupportBuilderX86::EmitSetCurrentThread(Value*) {
- /* Nothing to be done. */
- return UndefValue::get(irb_.getJObjectTy());
-}
-
-
-} // namespace llvm
-} // namespace art
diff --git a/compiler/llvm/runtime_support_builder_x86.h b/compiler/llvm/runtime_support_builder_x86.h
deleted file mode 100644
index 5f36e7c..0000000
--- a/compiler/llvm/runtime_support_builder_x86.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_X86_H_
-#define ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_X86_H_
-
-#include "runtime_support_builder.h"
-
-namespace art {
-namespace llvm {
-
-class RuntimeSupportBuilderX86 : public RuntimeSupportBuilder {
- public:
- RuntimeSupportBuilderX86(::llvm::LLVMContext& context, ::llvm::Module& module, IRBuilder& irb)
- : RuntimeSupportBuilder(context, module, irb) {}
-
- /* Thread */
- virtual ::llvm::Value* EmitGetCurrentThread();
- virtual ::llvm::Value* EmitLoadFromThreadOffset(int64_t offset, ::llvm::Type* type,
- TBAASpecialType s_ty);
- virtual void EmitStoreToThreadOffset(int64_t offset, ::llvm::Value* value,
- TBAASpecialType s_ty);
- virtual ::llvm::Value* EmitSetCurrentThread(::llvm::Value* thread);
-};
-
-} // namespace llvm
-} // namespace art
-
-#endif // ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_X86_H_
diff --git a/compiler/llvm/runtime_support_llvm_func.h b/compiler/llvm/runtime_support_llvm_func.h
deleted file mode 100644
index a5ad852..0000000
--- a/compiler/llvm/runtime_support_llvm_func.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_FUNC_H_
-#define ART_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_FUNC_H_
-
-#include "runtime_support_llvm_func_list.h"
-
-namespace art {
-namespace llvm {
-namespace runtime_support {
-
- enum RuntimeId {
-#define DEFINE_RUNTIME_SUPPORT_FUNC_ID(ID, NAME) ID,
- RUNTIME_SUPPORT_FUNC_LIST(DEFINE_RUNTIME_SUPPORT_FUNC_ID)
-
- MAX_ID
- };
-
-} // namespace runtime_support
-} // namespace llvm
-} // namespace art
-
-#endif // ART_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_FUNC_H_
diff --git a/compiler/llvm/runtime_support_llvm_func_list.h b/compiler/llvm/runtime_support_llvm_func_list.h
deleted file mode 100644
index b5ac1ff..0000000
--- a/compiler/llvm/runtime_support_llvm_func_list.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_FUNC_LIST_H_
-#define ART_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_FUNC_LIST_H_
-
-#define RUNTIME_SUPPORT_FUNC_LIST(V) \
- V(LockObject, art_portable_lock_object_from_code) \
- V(UnlockObject, art_portable_unlock_object_from_code) \
- V(GetCurrentThread, art_portable_get_current_thread_from_code) \
- V(SetCurrentThread, art_portable_set_current_thread_from_code) \
- V(PushShadowFrame, art_portable_push_shadow_frame_from_code) \
- V(PopShadowFrame, art_portable_pop_shadow_frame_from_code) \
- V(TestSuspend, art_portable_test_suspend_from_code) \
- V(ThrowException, art_portable_throw_exception_from_code) \
- V(ThrowStackOverflowException, art_portable_throw_stack_overflow_from_code) \
- V(ThrowNullPointerException, art_portable_throw_null_pointer_exception_from_code) \
- V(ThrowDivZeroException, art_portable_throw_div_zero_from_code) \
- V(ThrowIndexOutOfBounds, art_portable_throw_array_bounds_from_code) \
- V(InitializeTypeAndVerifyAccess, art_portable_initialize_type_and_verify_access_from_code) \
- V(InitializeType, art_portable_initialize_type_from_code) \
- V(IsAssignable, art_portable_is_assignable_from_code) \
- V(CheckCast, art_portable_check_cast_from_code) \
- V(CheckPutArrayElement, art_portable_check_put_array_element_from_code) \
- V(AllocObject, art_portable_alloc_object_from_code) \
- V(AllocObjectWithAccessCheck, art_portable_alloc_object_from_code_with_access_check) \
- V(AllocArray, art_portable_alloc_array_from_code) \
- V(AllocArrayWithAccessCheck, art_portable_alloc_array_from_code_with_access_check) \
- V(CheckAndAllocArray, art_portable_check_and_alloc_array_from_code) \
- V(CheckAndAllocArrayWithAccessCheck, art_portable_check_and_alloc_array_from_code_with_access_check) \
- V(FindStaticMethodWithAccessCheck, art_portable_find_static_method_from_code_with_access_check) \
- V(FindDirectMethodWithAccessCheck, art_portable_find_direct_method_from_code_with_access_check) \
- V(FindVirtualMethodWithAccessCheck, art_portable_find_virtual_method_from_code_with_access_check) \
- V(FindSuperMethodWithAccessCheck, art_portable_find_super_method_from_code_with_access_check) \
- V(FindInterfaceMethodWithAccessCheck, art_portable_find_interface_method_from_code_with_access_check) \
- V(FindInterfaceMethod, art_portable_find_interface_method_from_code) \
- V(ResolveString, art_portable_resolve_string_from_code) \
- V(Set32Static, art_portable_set32_static_from_code) \
- V(Set64Static, art_portable_set64_static_from_code) \
- V(SetObjectStatic, art_portable_set_obj_static_from_code) \
- V(Get32Static, art_portable_get32_static_from_code) \
- V(Get64Static, art_portable_get64_static_from_code) \
- V(GetObjectStatic, art_portable_get_obj_static_from_code) \
- V(Set32Instance, art_portable_set32_instance_from_code) \
- V(Set64Instance, art_portable_set64_instance_from_code) \
- V(SetObjectInstance, art_portable_set_obj_instance_from_code) \
- V(Get32Instance, art_portable_get32_instance_from_code) \
- V(Get64Instance, art_portable_get64_instance_from_code) \
- V(GetObjectInstance, art_portable_get_obj_instance_from_code) \
- V(InitializeStaticStorage, art_portable_initialize_static_storage_from_code) \
- V(FillArrayData, art_portable_fill_array_data_from_code) \
- V(GetAndClearException, art_portable_get_and_clear_exception) \
- V(IsExceptionPending, art_portable_is_exception_pending_from_code) \
- V(FindCatchBlock, art_portable_find_catch_block_from_code) \
- V(MarkGCCard, art_portable_mark_gc_card_from_code) \
- V(ProxyInvokeHandler, art_portable_proxy_invoke_handler_from_code) \
- V(art_d2l, art_d2l) \
- V(art_d2i, art_d2i) \
- V(art_f2l, art_f2l) \
- V(art_f2i, art_f2i) \
- V(JniMethodStart, art_portable_jni_method_start) \
- V(JniMethodStartSynchronized, art_portable_jni_method_start_synchronized) \
- V(JniMethodEnd, art_portable_jni_method_end) \
- V(JniMethodEndSynchronized, art_portable_jni_method_end_synchronized) \
- V(JniMethodEndWithReference, art_portable_jni_method_end_with_reference) \
- V(JniMethodEndWithReferenceSynchronized, art_portable_jni_method_end_with_reference_synchronized)
-
-#endif // ART_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_FUNC_LIST_H_
diff --git a/compiler/llvm/tools/gen_art_module_cc.sh b/compiler/llvm/tools/gen_art_module_cc.sh
deleted file mode 100755
index c5df333..0000000
--- a/compiler/llvm/tools/gen_art_module_cc.sh
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/bin/bash -e
-
-# Copyright (C) 2012 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-SCRIPTDIR=`dirname "$0"`
-cd "${SCRIPTDIR}/.."
-
-mkdir -p generated
-
-OUTPUT_FILE=generated/art_module.cc
-
-echo "// Generated with ${0}" > ${OUTPUT_FILE}
-
-echo '
-
-#pragma GCC diagnostic ignored "-Wframe-larger-than="
-// TODO: Remove this pragma after llc can generate makeLLVMModuleContents()
-// with smaller frame size.
-
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/Function.h>
-#include <llvm/IR/Module.h>
-#include <llvm/IR/Type.h>
-
-#include <vector>
-
-using namespace llvm;
-
-namespace art {
-namespace llvm {
-
-' >> ${OUTPUT_FILE}
-
-llc -march=cpp -cppgen=contents art_module.ll -o - >> ${OUTPUT_FILE}
-
-echo '
-} // namespace llvm
-} // namespace art' >> ${OUTPUT_FILE}
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 9fe98e3..46aed60 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -18,9 +18,10 @@
#include "class_linker.h"
#include "common_compiler_test.h"
#include "compiler.h"
-#include "dex/verification_results.h"
+#include "dex/pass_manager.h"
#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "dex/quick_compiler_callbacks.h"
+#include "dex/verification_results.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
@@ -39,49 +40,32 @@
void CheckMethod(mirror::ArtMethod* method,
const OatFile::OatMethod& oat_method,
- const DexFile* dex_file)
+ const DexFile& dex_file)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
const CompiledMethod* compiled_method =
- compiler_driver_->GetCompiledMethod(MethodReference(dex_file,
+ compiler_driver_->GetCompiledMethod(MethodReference(&dex_file,
method->GetDexMethodIndex()));
if (compiled_method == nullptr) {
EXPECT_TRUE(oat_method.GetQuickCode() == nullptr) << PrettyMethod(method) << " "
<< oat_method.GetQuickCode();
- EXPECT_TRUE(oat_method.GetPortableCode() == nullptr) << PrettyMethod(method) << " "
- << oat_method.GetPortableCode();
EXPECT_EQ(oat_method.GetFrameSizeInBytes(), 0U);
EXPECT_EQ(oat_method.GetCoreSpillMask(), 0U);
EXPECT_EQ(oat_method.GetFpSpillMask(), 0U);
} else {
const void* quick_oat_code = oat_method.GetQuickCode();
- if (quick_oat_code != nullptr) {
- EXPECT_EQ(oat_method.GetFrameSizeInBytes(), compiled_method->GetFrameSizeInBytes());
- EXPECT_EQ(oat_method.GetCoreSpillMask(), compiled_method->GetCoreSpillMask());
- EXPECT_EQ(oat_method.GetFpSpillMask(), compiled_method->GetFpSpillMask());
- uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(quick_oat_code), 2);
- quick_oat_code = reinterpret_cast<const void*>(oat_code_aligned);
- const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
- EXPECT_TRUE(quick_code != nullptr);
- size_t code_size = quick_code->size() * sizeof(quick_code[0]);
- EXPECT_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size))
- << PrettyMethod(method) << " " << code_size;
- CHECK_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size));
- } else {
- const void* portable_oat_code = oat_method.GetPortableCode();
- EXPECT_TRUE(portable_oat_code != nullptr) << PrettyMethod(method);
- EXPECT_EQ(oat_method.GetFrameSizeInBytes(), 0U);
- EXPECT_EQ(oat_method.GetCoreSpillMask(), 0U);
- EXPECT_EQ(oat_method.GetFpSpillMask(), 0U);
- uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(portable_oat_code), 2);
- portable_oat_code = reinterpret_cast<const void*>(oat_code_aligned);
- const std::vector<uint8_t>* portable_code = compiled_method->GetPortableCode();
- EXPECT_TRUE(portable_code != nullptr);
- size_t code_size = portable_code->size() * sizeof(portable_code[0]);
- EXPECT_EQ(0, memcmp(quick_oat_code, &portable_code[0], code_size))
- << PrettyMethod(method) << " " << code_size;
- CHECK_EQ(0, memcmp(quick_oat_code, &portable_code[0], code_size));
- }
+ EXPECT_TRUE(quick_oat_code != nullptr) << PrettyMethod(method);
+ EXPECT_EQ(oat_method.GetFrameSizeInBytes(), compiled_method->GetFrameSizeInBytes());
+ EXPECT_EQ(oat_method.GetCoreSpillMask(), compiled_method->GetCoreSpillMask());
+ EXPECT_EQ(oat_method.GetFpSpillMask(), compiled_method->GetFpSpillMask());
+ uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(quick_oat_code), 2);
+ quick_oat_code = reinterpret_cast<const void*>(oat_code_aligned);
+ const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
+ EXPECT_TRUE(quick_code != nullptr);
+ size_t code_size = quick_code->size() * sizeof(quick_code[0]);
+ EXPECT_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size))
+ << PrettyMethod(method) << " " << code_size;
+ CHECK_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size));
}
}
};
@@ -91,9 +75,7 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
// TODO: make selectable.
- Compiler::Kind compiler_kind = kUsePortableCompiler
- ? Compiler::kPortable
- : Compiler::kQuick;
+ Compiler::Kind compiler_kind = Compiler::kQuick;
InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86;
std::string error_msg;
@@ -111,7 +93,7 @@
method_inliner_map_.get(),
compiler_kind, insn_set,
insn_features.get(), false, nullptr, nullptr, 2, true,
- true, timer_.get(), ""));
+ true, "", timer_.get(), -1, ""));
jobject class_loader = nullptr;
if (kCompile) {
TimingLogger timings2("OatTest::WriteRead", false, false);
@@ -149,22 +131,23 @@
ASSERT_EQ(4096U, oat_header.GetImageFileLocationOatDataBegin());
ASSERT_EQ("lue.art", std::string(oat_header.GetStoreValueByKey(OatHeader::kImageLocationKey)));
- const DexFile* dex_file = java_lang_dex_file_;
- uint32_t dex_file_checksum = dex_file->GetLocationChecksum();
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file->GetLocation().c_str(),
+ ASSERT_TRUE(java_lang_dex_file_ != nullptr);
+ const DexFile& dex_file = *java_lang_dex_file_;
+ uint32_t dex_file_checksum = dex_file.GetLocationChecksum();
+ const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(),
&dex_file_checksum);
ASSERT_TRUE(oat_dex_file != nullptr);
- CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
+ CHECK_EQ(dex_file.GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
ScopedObjectAccess soa(Thread::Current());
- for (size_t i = 0; i < dex_file->NumClassDefs(); i++) {
- const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
- const uint8_t* class_data = dex_file->GetClassData(class_def);
+ for (size_t i = 0; i < dex_file.NumClassDefs(); i++) {
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(i);
+ const uint8_t* class_data = dex_file.GetClassData(class_def);
size_t num_virtual_methods = 0;
if (class_data != nullptr) {
- ClassDataItemIterator it(*dex_file, class_data);
+ ClassDataItemIterator it(dex_file, class_data);
num_virtual_methods = it.NumVirtualMethods();
}
- const char* descriptor = dex_file->GetClassDescriptor(class_def);
+ const char* descriptor = dex_file.GetClassDescriptor(class_def);
StackHandleScope<1> hs(soa.Self());
mirror::Class* klass = class_linker->FindClass(soa.Self(), descriptor,
NullHandle<mirror::ClassLoader>());
@@ -189,7 +172,7 @@
TEST_F(OatTest, OatHeaderSizeCheck) {
// If this test is failing and you have to update these constants,
// it is time to update OatHeader::kOatVersion
- EXPECT_EQ(84U, sizeof(OatHeader));
+ EXPECT_EQ(72U, sizeof(OatHeader));
EXPECT_EQ(4U, sizeof(OatMethodOffsets));
EXPECT_EQ(28U, sizeof(OatQuickMethodHeader));
EXPECT_EQ(91 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 8a7abb4..9c0157e 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -417,9 +417,6 @@
size_interpreter_to_interpreter_bridge_(0),
size_interpreter_to_compiled_code_bridge_(0),
size_jni_dlsym_lookup_(0),
- size_portable_imt_conflict_trampoline_(0),
- size_portable_resolution_trampoline_(0),
- size_portable_to_interpreter_bridge_(0),
size_quick_generic_jni_trampoline_(0),
size_quick_imt_conflict_trampoline_(0),
size_quick_resolution_trampoline_(0),
@@ -507,7 +504,7 @@
}
struct OatWriter::GcMapDataAccess {
- static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+ static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
return compiled_method->GetGcMap();
}
@@ -529,8 +526,8 @@
};
struct OatWriter::MappingTableDataAccess {
- static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
- return &compiled_method->GetMappingTable();
+ static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+ return compiled_method->GetMappingTable();
}
static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE {
@@ -551,7 +548,7 @@
};
struct OatWriter::VmapTableDataAccess {
- static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+ static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
return &compiled_method->GetVmapTable();
}
@@ -722,110 +719,101 @@
// Derived from CompiledMethod.
uint32_t quick_code_offset = 0;
- const std::vector<uint8_t>* portable_code = compiled_method->GetPortableCode();
- const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
- if (portable_code != nullptr) {
- CHECK(quick_code == nullptr);
- size_t oat_method_offsets_offset =
- oat_class->GetOatMethodOffsetsOffsetFromOatHeader(class_def_method_index);
- compiled_method->AddOatdataOffsetToCompliledCodeOffset(
- oat_method_offsets_offset + OFFSETOF_MEMBER(OatMethodOffsets, code_offset_));
+ const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
+ CHECK(quick_code != nullptr);
+ offset_ = writer_->relative_call_patcher_->ReserveSpace(offset_, compiled_method);
+ offset_ = compiled_method->AlignCode(offset_);
+ DCHECK_ALIGNED_PARAM(offset_,
+ GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
+ uint32_t code_size = quick_code->size() * sizeof(uint8_t);
+ CHECK_NE(code_size, 0U);
+ uint32_t thumb_offset = compiled_method->CodeDelta();
+ quick_code_offset = offset_ + sizeof(OatQuickMethodHeader) + thumb_offset;
+
+ bool deduped = false;
+
+ // Deduplicate code arrays.
+ auto lb = dedupe_map_.lower_bound(compiled_method);
+ if (lb != dedupe_map_.end() && !dedupe_map_.key_comp()(compiled_method, lb->first)) {
+ quick_code_offset = lb->second;
+ deduped = true;
} else {
- CHECK(quick_code != nullptr);
- offset_ = writer_->relative_call_patcher_->ReserveSpace(offset_, compiled_method);
- offset_ = compiled_method->AlignCode(offset_);
- DCHECK_ALIGNED_PARAM(offset_,
- GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
- uint32_t code_size = quick_code->size() * sizeof(uint8_t);
- CHECK_NE(code_size, 0U);
- uint32_t thumb_offset = compiled_method->CodeDelta();
- quick_code_offset = offset_ + sizeof(OatQuickMethodHeader) + thumb_offset;
+ dedupe_map_.PutBefore(lb, compiled_method, quick_code_offset);
+ }
- bool deduped = false;
+ MethodReference method_ref(dex_file_, it.GetMemberIndex());
+ auto method_lb = writer_->method_offset_map_.lower_bound(method_ref);
+ if (method_lb != writer_->method_offset_map_.end() &&
+ !writer_->method_offset_map_.key_comp()(method_ref, method_lb->first)) {
+ // TODO: Should this be a hard failure?
+ LOG(WARNING) << "Multiple definitions of "
+ << PrettyMethod(method_ref.dex_method_index, *method_ref.dex_file)
+ << ((method_lb->second != quick_code_offset) ? "; OFFSET MISMATCH" : "");
+ } else {
+ writer_->method_offset_map_.PutBefore(method_lb, method_ref, quick_code_offset);
+ }
- // Deduplicate code arrays.
- auto lb = dedupe_map_.lower_bound(compiled_method);
- if (lb != dedupe_map_.end() && !dedupe_map_.key_comp()(compiled_method, lb->first)) {
- quick_code_offset = lb->second;
- deduped = true;
- } else {
- dedupe_map_.PutBefore(lb, compiled_method, quick_code_offset);
- }
+ // Update quick method header.
+ DCHECK_LT(method_offsets_index_, oat_class->method_headers_.size());
+ OatQuickMethodHeader* method_header = &oat_class->method_headers_[method_offsets_index_];
+ uint32_t mapping_table_offset = method_header->mapping_table_offset_;
+ uint32_t vmap_table_offset = method_header->vmap_table_offset_;
+ uint32_t gc_map_offset = method_header->gc_map_offset_;
+ // The code offset was 0 when the mapping/vmap table offset was set, so it's set
+ // to 0-offset and we need to adjust it by code_offset.
+ uint32_t code_offset = quick_code_offset - thumb_offset;
+ if (mapping_table_offset != 0u) {
+ mapping_table_offset += code_offset;
+ DCHECK_LT(mapping_table_offset, code_offset);
+ }
+ if (vmap_table_offset != 0u) {
+ vmap_table_offset += code_offset;
+ DCHECK_LT(vmap_table_offset, code_offset);
+ }
+ if (gc_map_offset != 0u) {
+ gc_map_offset += code_offset;
+ DCHECK_LT(gc_map_offset, code_offset);
+ }
+ uint32_t frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
+ uint32_t core_spill_mask = compiled_method->GetCoreSpillMask();
+ uint32_t fp_spill_mask = compiled_method->GetFpSpillMask();
+ *method_header = OatQuickMethodHeader(mapping_table_offset, vmap_table_offset,
+ gc_map_offset, frame_size_in_bytes, core_spill_mask,
+ fp_spill_mask, code_size);
- MethodReference method_ref(dex_file_, it.GetMemberIndex());
- auto method_lb = writer_->method_offset_map_.lower_bound(method_ref);
- if (method_lb != writer_->method_offset_map_.end() &&
- !writer_->method_offset_map_.key_comp()(method_ref, method_lb->first)) {
- // TODO: Should this be a hard failure?
- LOG(WARNING) << "Multiple definitions of "
- << PrettyMethod(method_ref.dex_method_index, *method_ref.dex_file)
- << ((method_lb->second != quick_code_offset) ? "; OFFSET MISMATCH" : "");
- } else {
- writer_->method_offset_map_.PutBefore(method_lb, method_ref, quick_code_offset);
- }
-
- // Update quick method header.
- DCHECK_LT(method_offsets_index_, oat_class->method_headers_.size());
- OatQuickMethodHeader* method_header = &oat_class->method_headers_[method_offsets_index_];
- uint32_t mapping_table_offset = method_header->mapping_table_offset_;
- uint32_t vmap_table_offset = method_header->vmap_table_offset_;
- uint32_t gc_map_offset = method_header->gc_map_offset_;
- // The code offset was 0 when the mapping/vmap table offset was set, so it's set
- // to 0-offset and we need to adjust it by code_offset.
- uint32_t code_offset = quick_code_offset - thumb_offset;
- if (mapping_table_offset != 0u) {
- mapping_table_offset += code_offset;
- DCHECK_LT(mapping_table_offset, code_offset);
- }
- if (vmap_table_offset != 0u) {
- vmap_table_offset += code_offset;
- DCHECK_LT(vmap_table_offset, code_offset);
- }
- if (gc_map_offset != 0u) {
- gc_map_offset += code_offset;
- DCHECK_LT(gc_map_offset, code_offset);
- }
- uint32_t frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
- uint32_t core_spill_mask = compiled_method->GetCoreSpillMask();
- uint32_t fp_spill_mask = compiled_method->GetFpSpillMask();
- *method_header = OatQuickMethodHeader(mapping_table_offset, vmap_table_offset,
- gc_map_offset, frame_size_in_bytes, core_spill_mask,
- fp_spill_mask, code_size);
-
- if (!deduped) {
- // Update offsets. (Checksum is updated when writing.)
- offset_ += sizeof(*method_header); // Method header is prepended before code.
- offset_ += code_size;
- // Record absolute patch locations.
- if (!compiled_method->GetPatches().empty()) {
- uintptr_t base_loc = offset_ - code_size - writer_->oat_header_->GetExecutableOffset();
- for (const LinkerPatch& patch : compiled_method->GetPatches()) {
- if (patch.Type() != kLinkerPatchCallRelative) {
- writer_->absolute_patch_locations_.push_back(base_loc + patch.LiteralOffset());
- }
+ if (!deduped) {
+ // Update offsets. (Checksum is updated when writing.)
+ offset_ += sizeof(*method_header); // Method header is prepended before code.
+ offset_ += code_size;
+ // Record absolute patch locations.
+ if (!compiled_method->GetPatches().empty()) {
+ uintptr_t base_loc = offset_ - code_size - writer_->oat_header_->GetExecutableOffset();
+ for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+ if (patch.Type() != kLinkerPatchCallRelative) {
+ writer_->absolute_patch_locations_.push_back(base_loc + patch.LiteralOffset());
}
}
}
+ }
- if (writer_->compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
- // Record debug information for this function if we are doing that.
+ if (writer_->compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
+ // Record debug information for this function if we are doing that.
- std::string name = PrettyMethod(it.GetMemberIndex(), *dex_file_, true);
- if (deduped) {
- // TODO We should place the DEDUPED tag on the first instance of a deduplicated symbol
- // so that it will show up in a debuggerd crash report.
- name += " [ DEDUPED ]";
- }
-
- const uint32_t quick_code_start = quick_code_offset -
- writer_->oat_header_->GetExecutableOffset();
- const DexFile::CodeItem *code_item = it.GetMethodCodeItem();
- writer_->method_info_.push_back(DebugInfo(name,
- dex_file_->GetSourceFile(dex_file_->GetClassDef(class_def_index_)),
- quick_code_start, quick_code_start + code_size,
- code_item == nullptr ? nullptr : dex_file_->GetDebugInfoStream(code_item),
- compiled_method));
+ std::string name = PrettyMethod(it.GetMemberIndex(), *dex_file_, true);
+ if (deduped) {
+ // TODO We should place the DEDUPED tag on the first instance of a deduplicated symbol
+ // so that it will show up in a debuggerd crash report.
+ name += " [ DEDUPED ]";
}
+
+ const uint32_t quick_code_start = quick_code_offset -
+ writer_->oat_header_->GetExecutableOffset();
+ const DexFile::CodeItem *code_item = it.GetMethodCodeItem();
+ writer_->method_info_.push_back(DebugInfo(name,
+ dex_file_->GetSourceFile(dex_file_->GetClassDef(class_def_index_)),
+ quick_code_start, quick_code_start + code_size,
+ code_item == nullptr ? nullptr : dex_file_->GetDebugInfoStream(code_item),
+ compiled_method));
}
if (kIsDebugBuild) {
@@ -841,7 +829,7 @@
} else {
status = mirror::Class::kStatusNotReady;
}
- std::vector<uint8_t> const * gc_map = compiled_method->GetGcMap();
+ const SwapVector<uint8_t>* gc_map = compiled_method->GetGcMap();
if (gc_map != nullptr) {
size_t gc_map_size = gc_map->size() * sizeof(gc_map[0]);
bool is_native = it.MemberIsNative();
@@ -883,7 +871,7 @@
DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
DCHECK_EQ(DataAccess::GetOffset(oat_class, method_offsets_index_), 0u);
- const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
+ const SwapVector<uint8_t>* map = DataAccess::GetData(compiled_method);
uint32_t map_size = map == nullptr ? 0 : map->size() * sizeof((*map)[0]);
if (map_size != 0u) {
auto lb = dedupe_map_.lower_bound(map);
@@ -905,13 +893,14 @@
private:
// Deduplication is already done on a pointer basis by the compiler driver,
// so we can simply compare the pointers to find out if things are duplicated.
- SafeMap<const std::vector<uint8_t>*, uint32_t> dedupe_map_;
+ SafeMap<const SwapVector<uint8_t>*, uint32_t> dedupe_map_;
};
class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor {
public:
InitImageMethodVisitor(OatWriter* writer, size_t offset)
- : OatDexMethodVisitor(writer, offset) {
+ : OatDexMethodVisitor(writer, offset),
+ pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())) {
}
bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
@@ -944,11 +933,14 @@
std::string dump = exc->Dump();
LOG(FATAL) << dump;
}
- // Portable code offsets are set by ElfWriterMclinker::FixupCompiledCodeOffset after linking.
- method->SetQuickOatCodeOffset(offsets.code_offset_);
+ method->SetEntryPointFromQuickCompiledCodePtrSize(reinterpret_cast<void*>(offsets.code_offset_),
+ pointer_size_);
return true;
}
+
+ protected:
+ const size_t pointer_size_;
};
class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
@@ -1003,9 +995,11 @@
size_t file_offset = file_offset_;
OutputStream* out = out_;
- const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
+ const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
if (quick_code != nullptr) {
- CHECK(compiled_method->GetPortableCode() == nullptr);
+ // Need a wrapper if we create a copy for patching.
+ ArrayRef<const uint8_t> wrapped(*quick_code);
+
offset_ = writer_->relative_call_patcher_->WriteThunks(out, offset_);
if (offset_ == 0u) {
ReportWriteFailure("relative call thunk", it);
@@ -1044,8 +1038,8 @@
DCHECK_OFFSET_();
if (!compiled_method->GetPatches().empty()) {
- patched_code_ = *quick_code;
- quick_code = &patched_code_;
+ patched_code_ = std::vector<uint8_t>(quick_code->begin(), quick_code->end());
+ wrapped = ArrayRef<const uint8_t>(patched_code_);
for (const LinkerPatch& patch : compiled_method->GetPatches()) {
if (patch.Type() == kLinkerPatchCallRelative) {
// NOTE: Relative calls across oat files are not supported.
@@ -1066,8 +1060,8 @@
}
}
- writer_->oat_header_->UpdateChecksum(&(*quick_code)[0], code_size);
- if (!out->WriteFully(&(*quick_code)[0], code_size)) {
+ writer_->oat_header_->UpdateChecksum(wrapped.data(), code_size);
+ if (!out->WriteFully(wrapped.data(), code_size)) {
ReportWriteFailure("method code", it);
return false;
}
@@ -1114,10 +1108,18 @@
if (UNLIKELY(target_offset == 0)) {
mirror::ArtMethod* target = GetTargetMethod(patch);
DCHECK(target != nullptr);
- DCHECK_EQ(target->GetQuickOatCodeOffset(), 0u);
- target_offset = target->IsNative()
- ? writer_->oat_header_->GetQuickGenericJniTrampolineOffset()
- : writer_->oat_header_->GetQuickToInterpreterBridgeOffset();
+ size_t size = GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet());
+ const void* oat_code_offset = target->GetEntryPointFromQuickCompiledCodePtrSize(size);
+ if (oat_code_offset != 0) {
+ DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(oat_code_offset));
+ DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(oat_code_offset));
+ DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickGenericJniStub(oat_code_offset));
+ target_offset = PointerToLowMemUInt32(oat_code_offset);
+ } else {
+ target_offset = target->IsNative()
+ ? writer_->oat_header_->GetQuickGenericJniTrampolineOffset()
+ : writer_->oat_header_->GetQuickToInterpreterBridgeOffset();
+ }
}
return target_offset;
}
@@ -1149,10 +1151,9 @@
void PatchCodeAddress(std::vector<uint8_t>* code, uint32_t offset, uint32_t target_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- // NOTE: Direct calls across oat files don't use linker patches.
- DCHECK(writer_->image_writer_ != nullptr);
- uint32_t address = PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin() +
- writer_->oat_data_offset_ + target_offset);
+ uint32_t address = writer_->image_writer_ == nullptr ? target_offset :
+ PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin() +
+ writer_->oat_data_offset_ + target_offset);
DCHECK_LE(offset + 4, code->size());
uint8_t* data = &(*code)[offset];
data[0] = address & 0xffu;
@@ -1184,7 +1185,7 @@
++method_offsets_index_;
// Write deduplicated map.
- const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
+ const SwapVector<uint8_t>* map = DataAccess::GetData(compiled_method);
size_t map_size = map == nullptr ? 0 : map->size() * sizeof((*map)[0]);
DCHECK((map_size == 0u && map_offset == 0u) ||
(map_size != 0u && map_offset != 0u && map_offset <= offset_))
@@ -1357,9 +1358,6 @@
DO_TRAMPOLINE(interpreter_to_interpreter_bridge_, InterpreterToInterpreterBridge);
DO_TRAMPOLINE(interpreter_to_compiled_code_bridge_, InterpreterToCompiledCodeBridge);
DO_TRAMPOLINE(jni_dlsym_lookup_, JniDlsymLookup);
- DO_TRAMPOLINE(portable_imt_conflict_trampoline_, PortableImtConflictTrampoline);
- DO_TRAMPOLINE(portable_resolution_trampoline_, PortableResolutionTrampoline);
- DO_TRAMPOLINE(portable_to_interpreter_bridge_, PortableToInterpreterBridge);
DO_TRAMPOLINE(quick_generic_jni_trampoline_, QuickGenericJniTrampoline);
DO_TRAMPOLINE(quick_imt_conflict_trampoline_, QuickImtConflictTrampoline);
DO_TRAMPOLINE(quick_resolution_trampoline_, QuickResolutionTrampoline);
@@ -1370,9 +1368,6 @@
oat_header_->SetInterpreterToInterpreterBridgeOffset(0);
oat_header_->SetInterpreterToCompiledCodeBridgeOffset(0);
oat_header_->SetJniDlsymLookupOffset(0);
- oat_header_->SetPortableImtConflictTrampolineOffset(0);
- oat_header_->SetPortableResolutionTrampolineOffset(0);
- oat_header_->SetPortableToInterpreterBridgeOffset(0);
oat_header_->SetQuickGenericJniTrampolineOffset(0);
oat_header_->SetQuickImtConflictTrampolineOffset(0);
oat_header_->SetQuickResolutionTrampolineOffset(0);
@@ -1467,9 +1462,6 @@
DO_STAT(size_interpreter_to_interpreter_bridge_);
DO_STAT(size_interpreter_to_compiled_code_bridge_);
DO_STAT(size_jni_dlsym_lookup_);
- DO_STAT(size_portable_imt_conflict_trampoline_);
- DO_STAT(size_portable_resolution_trampoline_);
- DO_STAT(size_portable_to_interpreter_bridge_);
DO_STAT(size_quick_generic_jni_trampoline_);
DO_STAT(size_quick_imt_conflict_trampoline_);
DO_STAT(size_quick_resolution_trampoline_);
@@ -1612,9 +1604,6 @@
DO_TRAMPOLINE(interpreter_to_interpreter_bridge_);
DO_TRAMPOLINE(interpreter_to_compiled_code_bridge_);
DO_TRAMPOLINE(jni_dlsym_lookup_);
- DO_TRAMPOLINE(portable_imt_conflict_trampoline_);
- DO_TRAMPOLINE(portable_resolution_trampoline_);
- DO_TRAMPOLINE(portable_to_interpreter_bridge_);
DO_TRAMPOLINE(quick_generic_jni_trampoline_);
DO_TRAMPOLINE(quick_imt_conflict_trampoline_);
DO_TRAMPOLINE(quick_resolution_trampoline_);
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index b3ac7ff..e020d31 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -214,10 +214,7 @@
}
// Offset of start of OatClass from beginning of OatHeader. It is
- // used to validate file position when writing. For Portable, it
- // is also used to calculate the position of the OatMethodOffsets
- // so that code pointers within the OatMethodOffsets can be
- // patched to point to code in the Portable .o ELF objects.
+ // used to validate file position when writing.
size_t offset_;
// CompiledMethods for each class_def_method_index, or NULL if no method is available.
@@ -285,9 +282,6 @@
std::unique_ptr<const std::vector<uint8_t>> interpreter_to_interpreter_bridge_;
std::unique_ptr<const std::vector<uint8_t>> interpreter_to_compiled_code_bridge_;
std::unique_ptr<const std::vector<uint8_t>> jni_dlsym_lookup_;
- std::unique_ptr<const std::vector<uint8_t>> portable_imt_conflict_trampoline_;
- std::unique_ptr<const std::vector<uint8_t>> portable_resolution_trampoline_;
- std::unique_ptr<const std::vector<uint8_t>> portable_to_interpreter_bridge_;
std::unique_ptr<const std::vector<uint8_t>> quick_generic_jni_trampoline_;
std::unique_ptr<const std::vector<uint8_t>> quick_imt_conflict_trampoline_;
std::unique_ptr<const std::vector<uint8_t>> quick_resolution_trampoline_;
@@ -302,9 +296,6 @@
uint32_t size_interpreter_to_interpreter_bridge_;
uint32_t size_interpreter_to_compiled_code_bridge_;
uint32_t size_jni_dlsym_lookup_;
- uint32_t size_portable_imt_conflict_trampoline_;
- uint32_t size_portable_resolution_trampoline_;
- uint32_t size_portable_to_interpreter_bridge_;
uint32_t size_quick_generic_jni_trampoline_;
uint32_t size_quick_imt_conflict_trampoline_;
uint32_t size_quick_resolution_trampoline_;
@@ -347,8 +338,8 @@
return lhs->GetQuickCode() < rhs->GetQuickCode();
}
// If the code is the same, all other fields are likely to be the same as well.
- if (UNLIKELY(&lhs->GetMappingTable() != &rhs->GetMappingTable())) {
- return &lhs->GetMappingTable() < &rhs->GetMappingTable();
+ if (UNLIKELY(lhs->GetMappingTable() != rhs->GetMappingTable())) {
+ return lhs->GetMappingTable() < rhs->GetMappingTable();
}
if (UNLIKELY(&lhs->GetVmapTable() != &rhs->GetVmapTable())) {
return &lhs->GetVmapTable() < &rhs->GetVmapTable();
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
new file mode 100644
index 0000000..bcee563
--- /dev/null
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -0,0 +1,768 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bounds_check_elimination.h"
+#include "nodes.h"
+#include "utils/arena_containers.h"
+
+namespace art {
+
+class MonotonicValueRange;
+
+/**
+ * A value bound is represented as a pair of value and constant,
+ * e.g. array.length - 1.
+ */
+class ValueBound : public ValueObject {
+ public:
+ ValueBound(HInstruction* instruction, int32_t constant) {
+ if (instruction != nullptr && instruction->IsIntConstant()) {
+ // Normalize ValueBound with constant instruction.
+ int32_t instr_const = instruction->AsIntConstant()->GetValue();
+ if (constant >= 0 && (instr_const <= INT_MAX - constant)) {
+ // No overflow.
+ instruction_ = nullptr;
+ constant_ = instr_const + constant;
+ return;
+ }
+ if (constant < 0 && (instr_const >= INT_MIN - constant)) {
+ // No underflow.
+ instruction_ = nullptr;
+ constant_ = instr_const + constant;
+ return;
+ }
+ }
+ instruction_ = instruction;
+ constant_ = constant;
+ }
+
+ static bool IsAddOrSubAConstant(HInstruction* instruction,
+ HInstruction** left_instruction,
+ int* right_constant) {
+ if (instruction->IsAdd() || instruction->IsSub()) {
+ HBinaryOperation* bin_op = instruction->AsBinaryOperation();
+ HInstruction* left = bin_op->GetLeft();
+ HInstruction* right = bin_op->GetRight();
+ if (right->IsIntConstant()) {
+ *left_instruction = left;
+ int32_t c = right->AsIntConstant()->GetValue();
+ *right_constant = instruction->IsAdd() ? c : -c;
+ return true;
+ }
+ }
+ *left_instruction = nullptr;
+ *right_constant = 0;
+ return false;
+ }
+
+ // Try to detect useful value bound format from an instruction, e.g.
+ // a constant or array length related value.
+ static ValueBound DetectValueBoundFromValue(HInstruction* instruction, bool* found) {
+ DCHECK(instruction != nullptr);
+ if (instruction->IsIntConstant()) {
+ *found = true;
+ return ValueBound(nullptr, instruction->AsIntConstant()->GetValue());
+ }
+
+ if (instruction->IsArrayLength()) {
+ *found = true;
+ return ValueBound(instruction, 0);
+ }
+ // Try to detect (array.length + c) format.
+ HInstruction *left;
+ int32_t right;
+ if (IsAddOrSubAConstant(instruction, &left, &right)) {
+ if (left->IsArrayLength()) {
+ *found = true;
+ return ValueBound(left, right);
+ }
+ }
+
+ // No useful bound detected.
+ *found = false;
+ return ValueBound::Max();
+ }
+
+ HInstruction* GetInstruction() const { return instruction_; }
+ int32_t GetConstant() const { return constant_; }
+
+ bool IsRelatedToArrayLength() const {
+ // Some bounds are created with HNewArray* as the instruction instead
+ // of HArrayLength*. They are treated the same.
+ return (instruction_ != nullptr) &&
+ (instruction_->IsArrayLength() || instruction_->IsNewArray());
+ }
+
+ bool IsConstant() const {
+ return instruction_ == nullptr;
+ }
+
+ static ValueBound Min() { return ValueBound(nullptr, INT_MIN); }
+ static ValueBound Max() { return ValueBound(nullptr, INT_MAX); }
+
+ bool Equals(ValueBound bound) const {
+ return instruction_ == bound.instruction_ && constant_ == bound.constant_;
+ }
+
+ static HInstruction* FromArrayLengthToNewArrayIfPossible(HInstruction* instruction) {
+ // Null check on the NewArray should have been eliminated by instruction
+ // simplifier already.
+ if (instruction->IsArrayLength() && instruction->InputAt(0)->IsNewArray()) {
+ return instruction->InputAt(0)->AsNewArray();
+ }
+ return instruction;
+ }
+
+ static bool Equal(HInstruction* instruction1, HInstruction* instruction2) {
+ if (instruction1 == instruction2) {
+ return true;
+ }
+
+ if (instruction1 == nullptr || instruction2 == nullptr) {
+ return false;
+ }
+
+ // Some bounds are created with HNewArray* as the instruction instead
+ // of HArrayLength*. They are treated the same.
+ instruction1 = FromArrayLengthToNewArrayIfPossible(instruction1);
+ instruction2 = FromArrayLengthToNewArrayIfPossible(instruction2);
+ return instruction1 == instruction2;
+ }
+
+ // Returns if it's certain this->bound >= `bound`.
+ bool GreaterThanOrEqualTo(ValueBound bound) const {
+ if (Equal(instruction_, bound.instruction_)) {
+ return constant_ >= bound.constant_;
+ }
+ // Not comparable. Just return false.
+ return false;
+ }
+
+ // Returns if it's certain this->bound <= `bound`.
+ bool LessThanOrEqualTo(ValueBound bound) const {
+ if (Equal(instruction_, bound.instruction_)) {
+ return constant_ <= bound.constant_;
+ }
+ // Not comparable. Just return false.
+ return false;
+ }
+
+ // Try to narrow lower bound. Returns the greatest of the two if possible.
+ // Pick one if they are not comparable.
+ static ValueBound NarrowLowerBound(ValueBound bound1, ValueBound bound2) {
+ if (bound1.GreaterThanOrEqualTo(bound2)) {
+ return bound1;
+ }
+ if (bound2.GreaterThanOrEqualTo(bound1)) {
+ return bound2;
+ }
+
+ // Not comparable. Just pick one. We may lose some info, but that's ok.
+ // Favor constant as lower bound.
+ return bound1.IsConstant() ? bound1 : bound2;
+ }
+
+ // Try to narrow upper bound. Returns the lowest of the two if possible.
+ // Pick one if they are not comparable.
+ static ValueBound NarrowUpperBound(ValueBound bound1, ValueBound bound2) {
+ if (bound1.LessThanOrEqualTo(bound2)) {
+ return bound1;
+ }
+ if (bound2.LessThanOrEqualTo(bound1)) {
+ return bound2;
+ }
+
+ // Not comparable. Just pick one. We may lose some info, but that's ok.
+ // Favor array length as upper bound.
+ return bound1.IsRelatedToArrayLength() ? bound1 : bound2;
+ }
+
+ // Add a constant to a ValueBound.
+ // `overflow` or `underflow` will return whether the resulting bound may
+ // overflow or underflow an int.
+ ValueBound Add(int32_t c, bool* overflow, bool* underflow) const {
+ *overflow = *underflow = false;
+ if (c == 0) {
+ return *this;
+ }
+
+ int32_t new_constant;
+ if (c > 0) {
+ if (constant_ > INT_MAX - c) {
+ *overflow = true;
+ return Max();
+ }
+
+ new_constant = constant_ + c;
+ // (array.length + non-positive-constant) won't overflow an int.
+ if (IsConstant() || (IsRelatedToArrayLength() && new_constant <= 0)) {
+ return ValueBound(instruction_, new_constant);
+ }
+ // Be conservative.
+ *overflow = true;
+ return Max();
+ } else {
+ if (constant_ < INT_MIN - c) {
+ *underflow = true;
+ return Min();
+ }
+
+ new_constant = constant_ + c;
+ // Regardless of the value new_constant, (array.length+new_constant) will
+ // never underflow since array.length is no less than 0.
+ if (IsConstant() || IsRelatedToArrayLength()) {
+ return ValueBound(instruction_, new_constant);
+ }
+ // Be conservative.
+ *underflow = true;
+ return Min();
+ }
+ return ValueBound(instruction_, new_constant);
+ }
+
+ private:
+ HInstruction* instruction_;
+ int32_t constant_;
+};
+
+/**
+ * Represent a range of lower bound and upper bound, both being inclusive.
+ * Currently a ValueRange may be generated as a result of the following:
+ * comparisons related to array bounds, array bounds check, add/sub on top
+ * of an existing value range, NewArray or a loop phi corresponding to an
+ * incrementing/decrementing array index (MonotonicValueRange).
+ */
+class ValueRange : public ArenaObject<kArenaAllocMisc> {
+ public:
+ ValueRange(ArenaAllocator* allocator, ValueBound lower, ValueBound upper)
+ : allocator_(allocator), lower_(lower), upper_(upper) {}
+
+ virtual ~ValueRange() {}
+
+ virtual const MonotonicValueRange* AsMonotonicValueRange() const { return nullptr; }
+ bool IsMonotonicValueRange() const {
+ return AsMonotonicValueRange() != nullptr;
+ }
+
+ ArenaAllocator* GetAllocator() const { return allocator_; }
+ ValueBound GetLower() const { return lower_; }
+ ValueBound GetUpper() const { return upper_; }
+
+ // If it's certain that this value range fits in other_range.
+ virtual bool FitsIn(ValueRange* other_range) const {
+ if (other_range == nullptr) {
+ return true;
+ }
+ DCHECK(!other_range->IsMonotonicValueRange());
+ return lower_.GreaterThanOrEqualTo(other_range->lower_) &&
+ upper_.LessThanOrEqualTo(other_range->upper_);
+ }
+
+ // Returns the intersection of this and range.
+ // If it's not possible to do intersection because some
+ // bounds are not comparable, it's ok to pick either bound.
+ virtual ValueRange* Narrow(ValueRange* range) {
+ if (range == nullptr) {
+ return this;
+ }
+
+ if (range->IsMonotonicValueRange()) {
+ return this;
+ }
+
+ return new (allocator_) ValueRange(
+ allocator_,
+ ValueBound::NarrowLowerBound(lower_, range->lower_),
+ ValueBound::NarrowUpperBound(upper_, range->upper_));
+ }
+
+ // Shift a range by a constant.
+ ValueRange* Add(int32_t constant) const {
+ bool overflow, underflow;
+ ValueBound lower = lower_.Add(constant, &overflow, &underflow);
+ if (underflow) {
+ // Lower bound underflow will wrap around to positive values
+ // and invalidate the upper bound.
+ return nullptr;
+ }
+ ValueBound upper = upper_.Add(constant, &overflow, &underflow);
+ if (overflow) {
+ // Upper bound overflow will wrap around to negative values
+ // and invalidate the lower bound.
+ return nullptr;
+ }
+ return new (allocator_) ValueRange(allocator_, lower, upper);
+ }
+
+ private:
+ ArenaAllocator* const allocator_;
+ const ValueBound lower_; // inclusive
+ const ValueBound upper_; // inclusive
+
+ DISALLOW_COPY_AND_ASSIGN(ValueRange);
+};
+
+/**
+ * A monotonically incrementing/decrementing value range, e.g.
+ * the variable i in "for (int i=0; i<array.length; i++)".
+ * Special care needs to be taken to account for overflow/underflow
+ * of such value ranges.
+ */
+class MonotonicValueRange : public ValueRange {
+ public:
+ MonotonicValueRange(ArenaAllocator* allocator,
+ HInstruction* initial,
+ int32_t increment,
+ ValueBound bound)
+ // To be conservative, give it full range [INT_MIN, INT_MAX] in case it's
+ // used as a regular value range, due to possible overflow/underflow.
+ : ValueRange(allocator, ValueBound::Min(), ValueBound::Max()),
+ initial_(initial),
+ increment_(increment),
+ bound_(bound) {}
+
+ virtual ~MonotonicValueRange() {}
+
+ const MonotonicValueRange* AsMonotonicValueRange() const OVERRIDE { return this; }
+
+ // If it's certain that this value range fits in other_range.
+ bool FitsIn(ValueRange* other_range) const OVERRIDE {
+ if (other_range == nullptr) {
+ return true;
+ }
+ DCHECK(!other_range->IsMonotonicValueRange());
+ return false;
+ }
+
+ // Try to narrow this MonotonicValueRange given another range.
+ // Ideally it will return a normal ValueRange. But due to
+ // possible overflow/underflow, that may not be possible.
+ ValueRange* Narrow(ValueRange* range) OVERRIDE {
+ if (range == nullptr) {
+ return this;
+ }
+ DCHECK(!range->IsMonotonicValueRange());
+
+ if (increment_ > 0) {
+ // Monotonically increasing.
+ ValueBound lower = ValueBound::NarrowLowerBound(bound_, range->GetLower());
+
+ // We currently conservatively assume max array length is INT_MAX. If we can
+ // make assumptions about the max array length, e.g. due to the max heap size,
+ // divided by the element size (such as 4 bytes for each integer array), we can
+ // lower this number and rule out some possible overflows.
+ int32_t max_array_len = INT_MAX;
+
+ // max possible integer value of range's upper value.
+ int32_t upper = INT_MAX;
+ // Try to lower upper.
+ ValueBound upper_bound = range->GetUpper();
+ if (upper_bound.IsConstant()) {
+ upper = upper_bound.GetConstant();
+ } else if (upper_bound.IsRelatedToArrayLength() && upper_bound.GetConstant() <= 0) {
+ // Normal case. e.g. <= array.length - 1.
+ upper = max_array_len + upper_bound.GetConstant();
+ }
+
+ // If we can prove for the last number in sequence of initial_,
+ // initial_ + increment_, initial_ + 2 x increment_, ...
+ // that's <= upper, (last_num_in_sequence + increment_) doesn't trigger overflow,
+ // then this MonoticValueRange is narrowed to a normal value range.
+
+ // Be conservative first, assume last number in the sequence hits upper.
+ int32_t last_num_in_sequence = upper;
+ if (initial_->IsIntConstant()) {
+ int32_t initial_constant = initial_->AsIntConstant()->GetValue();
+ if (upper <= initial_constant) {
+ last_num_in_sequence = upper;
+ } else {
+ // Cast to int64_t for the substraction part to avoid int32_t overflow.
+ last_num_in_sequence = initial_constant +
+ ((int64_t)upper - (int64_t)initial_constant) / increment_ * increment_;
+ }
+ }
+ if (last_num_in_sequence <= INT_MAX - increment_) {
+ // No overflow. The sequence will be stopped by the upper bound test as expected.
+ return new (GetAllocator()) ValueRange(GetAllocator(), lower, range->GetUpper());
+ }
+
+ // There might be overflow. Give up narrowing.
+ return this;
+ } else {
+ DCHECK_NE(increment_, 0);
+ // Monotonically decreasing.
+ ValueBound upper = ValueBound::NarrowUpperBound(bound_, range->GetUpper());
+
+ // Need to take care of underflow. Try to prove underflow won't happen
+ // for common cases.
+ if (range->GetLower().IsConstant()) {
+ int32_t constant = range->GetLower().GetConstant();
+ if (constant >= INT_MIN - increment_) {
+ return new (GetAllocator()) ValueRange(GetAllocator(), range->GetLower(), upper);
+ }
+ }
+
+ // For non-constant lower bound, just assume might be underflow. Give up narrowing.
+ return this;
+ }
+ }
+
+ private:
+ HInstruction* const initial_;
+ const int32_t increment_;
+ ValueBound bound_; // Additional value bound info for initial_;
+
+ DISALLOW_COPY_AND_ASSIGN(MonotonicValueRange);
+};
+
+class BCEVisitor : public HGraphVisitor {
+ public:
+ explicit BCEVisitor(HGraph* graph)
+ : HGraphVisitor(graph),
+ maps_(graph->GetBlocks().Size()) {}
+
+ private:
+ // Return the map of proven value ranges at the beginning of a basic block.
+ ArenaSafeMap<int, ValueRange*>* GetValueRangeMap(HBasicBlock* basic_block) {
+ int block_id = basic_block->GetBlockId();
+ if (maps_.at(block_id) == nullptr) {
+ std::unique_ptr<ArenaSafeMap<int, ValueRange*>> map(
+ new ArenaSafeMap<int, ValueRange*>(
+ std::less<int>(), GetGraph()->GetArena()->Adapter()));
+ maps_.at(block_id) = std::move(map);
+ }
+ return maps_.at(block_id).get();
+ }
+
+ // Traverse up the dominator tree to look for value range info.
+ ValueRange* LookupValueRange(HInstruction* instruction, HBasicBlock* basic_block) {
+ while (basic_block != nullptr) {
+ ArenaSafeMap<int, ValueRange*>* map = GetValueRangeMap(basic_block);
+ if (map->find(instruction->GetId()) != map->end()) {
+ return map->Get(instruction->GetId());
+ }
+ basic_block = basic_block->GetDominator();
+ }
+ // Didn't find any.
+ return nullptr;
+ }
+
+ // Narrow the value range of `instruction` at the end of `basic_block` with `range`,
+ // and push the narrowed value range to `successor`.
+ void ApplyRangeFromComparison(HInstruction* instruction, HBasicBlock* basic_block,
+ HBasicBlock* successor, ValueRange* range) {
+ ValueRange* existing_range = LookupValueRange(instruction, basic_block);
+ ValueRange* narrowed_range = (existing_range == nullptr) ?
+ range : existing_range->Narrow(range);
+ if (narrowed_range != nullptr) {
+ GetValueRangeMap(successor)->Overwrite(instruction->GetId(), narrowed_range);
+ }
+ }
+
+ // Handle "if (left cmp_cond right)".
+ void HandleIf(HIf* instruction, HInstruction* left, HInstruction* right, IfCondition cond) {
+ HBasicBlock* block = instruction->GetBlock();
+
+ HBasicBlock* true_successor = instruction->IfTrueSuccessor();
+ // There should be no critical edge at this point.
+ DCHECK_EQ(true_successor->GetPredecessors().Size(), 1u);
+
+ HBasicBlock* false_successor = instruction->IfFalseSuccessor();
+ // There should be no critical edge at this point.
+ DCHECK_EQ(false_successor->GetPredecessors().Size(), 1u);
+
+ bool found;
+ ValueBound bound = ValueBound::DetectValueBoundFromValue(right, &found);
+ // Each comparison can establish a lower bound and an upper bound
+ // for the left hand side.
+ ValueBound lower = bound;
+ ValueBound upper = bound;
+ if (!found) {
+ // No constant or array.length+c format bound found.
+ // For i<j, we can still use j's upper bound as i's upper bound. Same for lower.
+ ValueRange* range = LookupValueRange(right, block);
+ if (range != nullptr) {
+ lower = range->GetLower();
+ upper = range->GetUpper();
+ } else {
+ lower = ValueBound::Min();
+ upper = ValueBound::Max();
+ }
+ }
+
+ bool overflow, underflow;
+ if (cond == kCondLT || cond == kCondLE) {
+ if (!upper.Equals(ValueBound::Max())) {
+ int32_t compensation = (cond == kCondLT) ? -1 : 0; // upper bound is inclusive
+ ValueBound new_upper = upper.Add(compensation, &overflow, &underflow);
+ if (overflow || underflow) {
+ return;
+ }
+ ValueRange* new_range = new (GetGraph()->GetArena())
+ ValueRange(GetGraph()->GetArena(), ValueBound::Min(), new_upper);
+ ApplyRangeFromComparison(left, block, true_successor, new_range);
+ }
+
+ // array.length as a lower bound isn't considered useful.
+ if (!lower.Equals(ValueBound::Min()) && !lower.IsRelatedToArrayLength()) {
+ int32_t compensation = (cond == kCondLE) ? 1 : 0; // lower bound is inclusive
+ ValueBound new_lower = lower.Add(compensation, &overflow, &underflow);
+ if (overflow || underflow) {
+ return;
+ }
+ ValueRange* new_range = new (GetGraph()->GetArena())
+ ValueRange(GetGraph()->GetArena(), new_lower, ValueBound::Max());
+ ApplyRangeFromComparison(left, block, false_successor, new_range);
+ }
+ } else if (cond == kCondGT || cond == kCondGE) {
+ // array.length as a lower bound isn't considered useful.
+ if (!lower.Equals(ValueBound::Min()) && !lower.IsRelatedToArrayLength()) {
+ int32_t compensation = (cond == kCondGT) ? 1 : 0; // lower bound is inclusive
+ ValueBound new_lower = lower.Add(compensation, &overflow, &underflow);
+ if (overflow || underflow) {
+ return;
+ }
+ ValueRange* new_range = new (GetGraph()->GetArena())
+ ValueRange(GetGraph()->GetArena(), new_lower, ValueBound::Max());
+ ApplyRangeFromComparison(left, block, true_successor, new_range);
+ }
+
+ if (!upper.Equals(ValueBound::Max())) {
+ int32_t compensation = (cond == kCondGE) ? -1 : 0; // upper bound is inclusive
+ ValueBound new_upper = upper.Add(compensation, &overflow, &underflow);
+ if (overflow || underflow) {
+ return;
+ }
+ ValueRange* new_range = new (GetGraph()->GetArena())
+ ValueRange(GetGraph()->GetArena(), ValueBound::Min(), new_upper);
+ ApplyRangeFromComparison(left, block, false_successor, new_range);
+ }
+ }
+ }
+
+ void VisitBoundsCheck(HBoundsCheck* bounds_check) {
+ HBasicBlock* block = bounds_check->GetBlock();
+ HInstruction* index = bounds_check->InputAt(0);
+ HInstruction* array_length = bounds_check->InputAt(1);
+ DCHECK(array_length->IsIntConstant() || array_length->IsArrayLength());
+
+ if (!index->IsIntConstant()) {
+ ValueRange* index_range = LookupValueRange(index, block);
+ if (index_range != nullptr) {
+ ValueBound lower = ValueBound(nullptr, 0); // constant 0
+ ValueBound upper = ValueBound(array_length, -1); // array_length - 1
+ ValueRange* array_range = new (GetGraph()->GetArena())
+ ValueRange(GetGraph()->GetArena(), lower, upper);
+ if (index_range->FitsIn(array_range)) {
+ ReplaceBoundsCheck(bounds_check, index);
+ return;
+ }
+ }
+ } else {
+ int32_t constant = index->AsIntConstant()->GetValue();
+ if (constant < 0) {
+ // Will always throw exception.
+ return;
+ }
+ if (array_length->IsIntConstant()) {
+ if (constant < array_length->AsIntConstant()->GetValue()) {
+ ReplaceBoundsCheck(bounds_check, index);
+ }
+ return;
+ }
+
+ DCHECK(array_length->IsArrayLength());
+ ValueRange* existing_range = LookupValueRange(array_length, block);
+ if (existing_range != nullptr) {
+ ValueBound lower = existing_range->GetLower();
+ DCHECK(lower.IsConstant());
+ if (constant < lower.GetConstant()) {
+ ReplaceBoundsCheck(bounds_check, index);
+ return;
+ } else {
+ // Existing range isn't strong enough to eliminate the bounds check.
+ // Fall through to update the array_length range with info from this
+ // bounds check.
+ }
+ }
+
+ // Once we have an array access like 'array[5] = 1', we record array.length >= 6.
+ // We currently don't do it for non-constant index since a valid array[i] can't prove
+ // a valid array[i-1] yet due to the lower bound side.
+ ValueBound lower = ValueBound(nullptr, constant + 1);
+ ValueBound upper = ValueBound::Max();
+ ValueRange* range = new (GetGraph()->GetArena())
+ ValueRange(GetGraph()->GetArena(), lower, upper);
+ GetValueRangeMap(block)->Overwrite(array_length->GetId(), range);
+ }
+ }
+
+ void ReplaceBoundsCheck(HInstruction* bounds_check, HInstruction* index) {
+ bounds_check->ReplaceWith(index);
+ bounds_check->GetBlock()->RemoveInstruction(bounds_check);
+ }
+
+ void VisitPhi(HPhi* phi) {
+ if (phi->IsLoopHeaderPhi() && phi->GetType() == Primitive::kPrimInt) {
+ DCHECK_EQ(phi->InputCount(), 2U);
+ HInstruction* instruction = phi->InputAt(1);
+ HInstruction *left;
+ int32_t increment;
+ if (ValueBound::IsAddOrSubAConstant(instruction, &left, &increment)) {
+ if (left == phi) {
+ HInstruction* initial_value = phi->InputAt(0);
+ ValueRange* range = nullptr;
+ if (increment == 0) {
+ // Add constant 0. It's really a fixed value.
+ range = new (GetGraph()->GetArena()) ValueRange(
+ GetGraph()->GetArena(),
+ ValueBound(initial_value, 0),
+ ValueBound(initial_value, 0));
+ } else {
+ // Monotonically increasing/decreasing.
+ bool found;
+ ValueBound bound = ValueBound::DetectValueBoundFromValue(
+ initial_value, &found);
+ if (!found) {
+ // No constant or array.length+c bound found.
+ // For i=j, we can still use j's upper bound as i's upper bound.
+ // Same for lower.
+ ValueRange* initial_range = LookupValueRange(initial_value, phi->GetBlock());
+ if (initial_range != nullptr) {
+ bound = increment > 0 ? initial_range->GetLower() :
+ initial_range->GetUpper();
+ } else {
+ bound = increment > 0 ? ValueBound::Min() : ValueBound::Max();
+ }
+ }
+ range = new (GetGraph()->GetArena()) MonotonicValueRange(
+ GetGraph()->GetArena(),
+ initial_value,
+ increment,
+ bound);
+ }
+ GetValueRangeMap(phi->GetBlock())->Overwrite(phi->GetId(), range);
+ }
+ }
+ }
+ }
+
+ void VisitIf(HIf* instruction) {
+ if (instruction->InputAt(0)->IsCondition()) {
+ HCondition* cond = instruction->InputAt(0)->AsCondition();
+ IfCondition cmp = cond->GetCondition();
+ if (cmp == kCondGT || cmp == kCondGE ||
+ cmp == kCondLT || cmp == kCondLE) {
+ HInstruction* left = cond->GetLeft();
+ HInstruction* right = cond->GetRight();
+ HandleIf(instruction, left, right, cmp);
+ }
+ }
+ }
+
+ void VisitAdd(HAdd* add) {
+ HInstruction* right = add->GetRight();
+ if (right->IsIntConstant()) {
+ ValueRange* left_range = LookupValueRange(add->GetLeft(), add->GetBlock());
+ if (left_range == nullptr) {
+ return;
+ }
+ ValueRange* range = left_range->Add(right->AsIntConstant()->GetValue());
+ if (range != nullptr) {
+ GetValueRangeMap(add->GetBlock())->Overwrite(add->GetId(), range);
+ }
+ }
+ }
+
+ void VisitSub(HSub* sub) {
+ HInstruction* left = sub->GetLeft();
+ HInstruction* right = sub->GetRight();
+ if (right->IsIntConstant()) {
+ ValueRange* left_range = LookupValueRange(left, sub->GetBlock());
+ if (left_range == nullptr) {
+ return;
+ }
+ ValueRange* range = left_range->Add(-right->AsIntConstant()->GetValue());
+ if (range != nullptr) {
+ GetValueRangeMap(sub->GetBlock())->Overwrite(sub->GetId(), range);
+ return;
+ }
+ }
+
+ // Here we are interested in the typical triangular case of nested loops,
+ // such as the inner loop 'for (int j=0; j<array.length-i; j++)' where i
+ // is the index for outer loop. In this case, we know j is bounded by array.length-1.
+ if (left->IsArrayLength()) {
+ HInstruction* array_length = left->AsArrayLength();
+ ValueRange* right_range = LookupValueRange(right, sub->GetBlock());
+ if (right_range != nullptr) {
+ ValueBound lower = right_range->GetLower();
+ ValueBound upper = right_range->GetUpper();
+ if (lower.IsConstant() && upper.IsRelatedToArrayLength()) {
+ HInstruction* upper_inst = upper.GetInstruction();
+ // Make sure it's the same array.
+ if (ValueBound::Equal(array_length, upper_inst)) {
+ // (array.length - v) where v is in [c1, array.length + c2]
+ // gets [-c2, array.length - c1] as its value range.
+ ValueRange* range = new (GetGraph()->GetArena()) ValueRange(
+ GetGraph()->GetArena(),
+ ValueBound(nullptr, - upper.GetConstant()),
+ ValueBound(array_length, - lower.GetConstant()));
+ GetValueRangeMap(sub->GetBlock())->Overwrite(sub->GetId(), range);
+ }
+ }
+ }
+ }
+ }
+
+ void VisitNewArray(HNewArray* new_array) {
+ HInstruction* len = new_array->InputAt(0);
+ if (!len->IsIntConstant()) {
+ HInstruction *left;
+ int32_t right_const;
+ if (ValueBound::IsAddOrSubAConstant(len, &left, &right_const)) {
+ // (left + right_const) is used as size to new the array.
+ // We record "-right_const <= left <= new_array - right_const";
+ ValueBound lower = ValueBound(nullptr, -right_const);
+ // We use new_array for the bound instead of new_array.length,
+ // which isn't available as an instruction yet. new_array will
+ // be treated the same as new_array.length when it's used in a ValueBound.
+ ValueBound upper = ValueBound(new_array, -right_const);
+ ValueRange* range = new (GetGraph()->GetArena())
+ ValueRange(GetGraph()->GetArena(), lower, upper);
+ GetValueRangeMap(new_array->GetBlock())->Overwrite(left->GetId(), range);
+ }
+ }
+ }
+
+ std::vector<std::unique_ptr<ArenaSafeMap<int, ValueRange*>>> maps_;
+
+ DISALLOW_COPY_AND_ASSIGN(BCEVisitor);
+};
+
+void BoundsCheckElimination::Run() {
+ BCEVisitor visitor(graph_);
+ // Reverse post order guarantees a node's dominators are visited first.
+ // We want to visit in the dominator-based order since if a value is known to
+ // be bounded by a range at one instruction, it must be true that all uses of
+ // that value dominated by that instruction fits in that range. Range of that
+ // value can be narrowed further down in the dominator tree.
+ //
+ // TODO: only visit blocks that dominate some array accesses.
+ visitor.VisitReversePostOrder();
+}
+
+} // namespace art
diff --git a/compiler/optimizing/bounds_check_elimination.h b/compiler/optimizing/bounds_check_elimination.h
new file mode 100644
index 0000000..05cb185
--- /dev/null
+++ b/compiler/optimizing/bounds_check_elimination.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_BOUNDS_CHECK_ELIMINATION_H_
+#define ART_COMPILER_OPTIMIZING_BOUNDS_CHECK_ELIMINATION_H_
+
+#include "optimization.h"
+
+namespace art {
+
+class BoundsCheckElimination : public HOptimization {
+ public:
+ explicit BoundsCheckElimination(HGraph* graph) : HOptimization(graph, true, "BCE") {}
+
+ void Run() OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BoundsCheckElimination);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_BOUNDS_CHECK_ELIMINATION_H_
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc
new file mode 100644
index 0000000..662834a
--- /dev/null
+++ b/compiler/optimizing/bounds_check_elimination_test.cc
@@ -0,0 +1,1055 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bounds_check_elimination.h"
+#include "builder.h"
+#include "gvn.h"
+#include "instruction_simplifier.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+#include "side_effects_analysis.h"
+#include "utils/arena_allocator.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+static void RunSimplifierAndGvn(HGraph* graph) {
+ InstructionSimplifier simplify(graph);
+ simplify.Run();
+ SideEffectsAnalysis side_effects(graph);
+ side_effects.Run();
+ GVNOptimization(graph, side_effects).Run();
+}
+
+// if (i < 0) { array[i] = 1; // Can't eliminate. }
+// else if (i >= array.length) { array[i] = 1; // Can't eliminate. }
+// else { array[i] = 1; // Can eliminate. }
+TEST(BoundsCheckEliminationTest, NarrowingRangeArrayBoundsElimination) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+
+ HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(entry);
+ graph->SetEntryBlock(entry);
+ HInstruction* parameter1 = new (&allocator)
+ HParameterValue(0, Primitive::kPrimNot); // array
+ HInstruction* parameter2 = new (&allocator)
+ HParameterValue(0, Primitive::kPrimInt); // i
+ HInstruction* constant_1 = new (&allocator) HIntConstant(1);
+ HInstruction* constant_0 = new (&allocator) HIntConstant(0);
+ entry->AddInstruction(parameter1);
+ entry->AddInstruction(parameter2);
+ entry->AddInstruction(constant_1);
+ entry->AddInstruction(constant_0);
+
+ HBasicBlock* block1 = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block1);
+ HInstruction* cmp = new (&allocator) HGreaterThanOrEqual(parameter2, constant_0);
+ HIf* if_inst = new (&allocator) HIf(cmp);
+ block1->AddInstruction(cmp);
+ block1->AddInstruction(if_inst);
+ entry->AddSuccessor(block1);
+
+ HBasicBlock* block2 = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block2);
+ HNullCheck* null_check = new (&allocator) HNullCheck(parameter1, 0);
+ HArrayLength* array_length = new (&allocator) HArrayLength(null_check);
+ HBoundsCheck* bounds_check2 = new (&allocator)
+ HBoundsCheck(parameter2, array_length, 0);
+ HArraySet* array_set = new (&allocator) HArraySet(
+ null_check, bounds_check2, constant_1, Primitive::kPrimInt, 0);
+ block2->AddInstruction(null_check);
+ block2->AddInstruction(array_length);
+ block2->AddInstruction(bounds_check2);
+ block2->AddInstruction(array_set);
+
+ HBasicBlock* block3 = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block3);
+ null_check = new (&allocator) HNullCheck(parameter1, 0);
+ array_length = new (&allocator) HArrayLength(null_check);
+ cmp = new (&allocator) HLessThan(parameter2, array_length);
+ if_inst = new (&allocator) HIf(cmp);
+ block3->AddInstruction(null_check);
+ block3->AddInstruction(array_length);
+ block3->AddInstruction(cmp);
+ block3->AddInstruction(if_inst);
+
+ HBasicBlock* block4 = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block4);
+ null_check = new (&allocator) HNullCheck(parameter1, 0);
+ array_length = new (&allocator) HArrayLength(null_check);
+ HBoundsCheck* bounds_check4 = new (&allocator)
+ HBoundsCheck(parameter2, array_length, 0);
+ array_set = new (&allocator) HArraySet(
+ null_check, bounds_check4, constant_1, Primitive::kPrimInt, 0);
+ block4->AddInstruction(null_check);
+ block4->AddInstruction(array_length);
+ block4->AddInstruction(bounds_check4);
+ block4->AddInstruction(array_set);
+
+ HBasicBlock* block5 = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block5);
+ null_check = new (&allocator) HNullCheck(parameter1, 0);
+ array_length = new (&allocator) HArrayLength(null_check);
+ HBoundsCheck* bounds_check5 = new (&allocator)
+ HBoundsCheck(parameter2, array_length, 0);
+ array_set = new (&allocator) HArraySet(
+ null_check, bounds_check5, constant_1, Primitive::kPrimInt, 0);
+ block5->AddInstruction(null_check);
+ block5->AddInstruction(array_length);
+ block5->AddInstruction(bounds_check5);
+ block5->AddInstruction(array_set);
+
+ HBasicBlock* exit = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(exit);
+ block2->AddSuccessor(exit);
+ block4->AddSuccessor(exit);
+ block5->AddSuccessor(exit);
+ exit->AddInstruction(new (&allocator) HExit());
+
+ block1->AddSuccessor(block3); // True successor
+ block1->AddSuccessor(block2); // False successor
+
+ block3->AddSuccessor(block5); // True successor
+ block3->AddSuccessor(block4); // False successor
+
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination(graph);
+ bounds_check_elimination.Run();
+ ASSERT_FALSE(IsRemoved(bounds_check2));
+ ASSERT_FALSE(IsRemoved(bounds_check4));
+ ASSERT_TRUE(IsRemoved(bounds_check5));
+}
+
+// if (i > 0) {
+// // Positive number plus MAX_INT will overflow and be negative.
+// int j = i + Integer.MAX_VALUE;
+// if (j < array.length) array[j] = 1; // Can't eliminate.
+// }
+TEST(BoundsCheckEliminationTest, OverflowArrayBoundsElimination) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+
+ HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(entry);
+ graph->SetEntryBlock(entry);
+ HInstruction* parameter1 = new (&allocator)
+ HParameterValue(0, Primitive::kPrimNot); // array
+ HInstruction* parameter2 = new (&allocator)
+ HParameterValue(0, Primitive::kPrimInt); // i
+ HInstruction* constant_1 = new (&allocator) HIntConstant(1);
+ HInstruction* constant_0 = new (&allocator) HIntConstant(0);
+ HInstruction* constant_max_int = new (&allocator) HIntConstant(INT_MAX);
+ entry->AddInstruction(parameter1);
+ entry->AddInstruction(parameter2);
+ entry->AddInstruction(constant_1);
+ entry->AddInstruction(constant_0);
+ entry->AddInstruction(constant_max_int);
+
+ HBasicBlock* block1 = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block1);
+ HInstruction* cmp = new (&allocator) HLessThanOrEqual(parameter2, constant_0);
+ HIf* if_inst = new (&allocator) HIf(cmp);
+ block1->AddInstruction(cmp);
+ block1->AddInstruction(if_inst);
+ entry->AddSuccessor(block1);
+
+ HBasicBlock* block2 = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block2);
+ HInstruction* add = new (&allocator) HAdd(Primitive::kPrimInt, parameter2, constant_max_int);
+ HNullCheck* null_check = new (&allocator) HNullCheck(parameter1, 0);
+ HArrayLength* array_length = new (&allocator) HArrayLength(null_check);
+ HInstruction* cmp2 = new (&allocator) HGreaterThanOrEqual(add, array_length);
+ if_inst = new (&allocator) HIf(cmp2);
+ block2->AddInstruction(add);
+ block2->AddInstruction(null_check);
+ block2->AddInstruction(array_length);
+ block2->AddInstruction(cmp2);
+ block2->AddInstruction(if_inst);
+
+ HBasicBlock* block3 = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block3);
+ HBoundsCheck* bounds_check = new (&allocator)
+ HBoundsCheck(add, array_length, 0);
+ HArraySet* array_set = new (&allocator) HArraySet(
+ null_check, bounds_check, constant_1, Primitive::kPrimInt, 0);
+ block3->AddInstruction(bounds_check);
+ block3->AddInstruction(array_set);
+
+ HBasicBlock* exit = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(exit);
+ exit->AddInstruction(new (&allocator) HExit());
+ block1->AddSuccessor(exit); // true successor
+ block1->AddSuccessor(block2); // false successor
+ block2->AddSuccessor(exit); // true successor
+ block2->AddSuccessor(block3); // false successor
+ block3->AddSuccessor(exit);
+
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination(graph);
+ bounds_check_elimination.Run();
+ ASSERT_FALSE(IsRemoved(bounds_check));
+}
+
+// if (i < array.length) {
+// int j = i - Integer.MAX_VALUE;
+// j = j - Integer.MAX_VALUE; // j is (i+2) after substracting MAX_INT twice
+// if (j > 0) array[j] = 1; // Can't eliminate.
+// }
+TEST(BoundsCheckEliminationTest, UnderflowArrayBoundsElimination) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+
+ HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(entry);
+ graph->SetEntryBlock(entry);
+ HInstruction* parameter1 = new (&allocator)
+ HParameterValue(0, Primitive::kPrimNot); // array
+ HInstruction* parameter2 = new (&allocator)
+ HParameterValue(0, Primitive::kPrimInt); // i
+ HInstruction* constant_1 = new (&allocator) HIntConstant(1);
+ HInstruction* constant_0 = new (&allocator) HIntConstant(0);
+ HInstruction* constant_max_int = new (&allocator) HIntConstant(INT_MAX);
+ entry->AddInstruction(parameter1);
+ entry->AddInstruction(parameter2);
+ entry->AddInstruction(constant_1);
+ entry->AddInstruction(constant_0);
+ entry->AddInstruction(constant_max_int);
+
+ HBasicBlock* block1 = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block1);
+ HNullCheck* null_check = new (&allocator) HNullCheck(parameter1, 0);
+ HArrayLength* array_length = new (&allocator) HArrayLength(null_check);
+ HInstruction* cmp = new (&allocator) HGreaterThanOrEqual(parameter2, array_length);
+ HIf* if_inst = new (&allocator) HIf(cmp);
+ block1->AddInstruction(null_check);
+ block1->AddInstruction(array_length);
+ block1->AddInstruction(cmp);
+ block1->AddInstruction(if_inst);
+ entry->AddSuccessor(block1);
+
+ HBasicBlock* block2 = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block2);
+ HInstruction* sub1 = new (&allocator) HSub(Primitive::kPrimInt, parameter2, constant_max_int);
+ HInstruction* sub2 = new (&allocator) HSub(Primitive::kPrimInt, sub1, constant_max_int);
+ HInstruction* cmp2 = new (&allocator) HLessThanOrEqual(sub2, constant_0);
+ if_inst = new (&allocator) HIf(cmp2);
+ block2->AddInstruction(sub1);
+ block2->AddInstruction(sub2);
+ block2->AddInstruction(cmp2);
+ block2->AddInstruction(if_inst);
+
+ HBasicBlock* block3 = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block3);
+ HBoundsCheck* bounds_check = new (&allocator)
+ HBoundsCheck(sub2, array_length, 0);
+ HArraySet* array_set = new (&allocator) HArraySet(
+ null_check, bounds_check, constant_1, Primitive::kPrimInt, 0);
+ block3->AddInstruction(bounds_check);
+ block3->AddInstruction(array_set);
+
+ HBasicBlock* exit = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(exit);
+ exit->AddInstruction(new (&allocator) HExit());
+ block1->AddSuccessor(exit); // true successor
+ block1->AddSuccessor(block2); // false successor
+ block2->AddSuccessor(exit); // true successor
+ block2->AddSuccessor(block3); // false successor
+ block3->AddSuccessor(exit);
+
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination(graph);
+ bounds_check_elimination.Run();
+ ASSERT_FALSE(IsRemoved(bounds_check));
+}
+
+// array[5] = 1; // Can't eliminate.
+// array[4] = 1; // Can eliminate.
+// array[6] = 1; // Can't eliminate.
+TEST(BoundsCheckEliminationTest, ConstantArrayBoundsElimination) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+
+ HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(entry);
+ graph->SetEntryBlock(entry);
+ HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
+ HInstruction* constant_5 = new (&allocator) HIntConstant(5);
+ HInstruction* constant_4 = new (&allocator) HIntConstant(4);
+ HInstruction* constant_6 = new (&allocator) HIntConstant(6);
+ HInstruction* constant_1 = new (&allocator) HIntConstant(1);
+ entry->AddInstruction(parameter);
+ entry->AddInstruction(constant_5);
+ entry->AddInstruction(constant_4);
+ entry->AddInstruction(constant_6);
+ entry->AddInstruction(constant_1);
+
+ HBasicBlock* block = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block);
+ entry->AddSuccessor(block);
+
+ HNullCheck* null_check = new (&allocator) HNullCheck(parameter, 0);
+ HArrayLength* array_length = new (&allocator) HArrayLength(null_check);
+ HBoundsCheck* bounds_check5 = new (&allocator)
+ HBoundsCheck(constant_5, array_length, 0);
+ HInstruction* array_set = new (&allocator) HArraySet(
+ null_check, bounds_check5, constant_1, Primitive::kPrimInt, 0);
+ block->AddInstruction(null_check);
+ block->AddInstruction(array_length);
+ block->AddInstruction(bounds_check5);
+ block->AddInstruction(array_set);
+
+ null_check = new (&allocator) HNullCheck(parameter, 0);
+ array_length = new (&allocator) HArrayLength(null_check);
+ HBoundsCheck* bounds_check4 = new (&allocator)
+ HBoundsCheck(constant_4, array_length, 0);
+ array_set = new (&allocator) HArraySet(
+ null_check, bounds_check4, constant_1, Primitive::kPrimInt, 0);
+ block->AddInstruction(null_check);
+ block->AddInstruction(array_length);
+ block->AddInstruction(bounds_check4);
+ block->AddInstruction(array_set);
+
+ null_check = new (&allocator) HNullCheck(parameter, 0);
+ array_length = new (&allocator) HArrayLength(null_check);
+ HBoundsCheck* bounds_check6 = new (&allocator)
+ HBoundsCheck(constant_6, array_length, 0);
+ array_set = new (&allocator) HArraySet(
+ null_check, bounds_check6, constant_1, Primitive::kPrimInt, 0);
+ block->AddInstruction(null_check);
+ block->AddInstruction(array_length);
+ block->AddInstruction(bounds_check6);
+ block->AddInstruction(array_set);
+
+ block->AddInstruction(new (&allocator) HGoto());
+
+ HBasicBlock* exit = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(exit);
+ block->AddSuccessor(exit);
+ exit->AddInstruction(new (&allocator) HExit());
+
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination(graph);
+ bounds_check_elimination.Run();
+ ASSERT_FALSE(IsRemoved(bounds_check5));
+ ASSERT_TRUE(IsRemoved(bounds_check4));
+ ASSERT_FALSE(IsRemoved(bounds_check6));
+}
+
+// for (int i=initial; i<array.length; i+=increment) { array[i] = 10; }
+static HGraph* BuildSSAGraph1(ArenaAllocator* allocator,
+ HInstruction** bounds_check,
+ int initial,
+ int increment,
+ IfCondition cond = kCondGE) {
+ HGraph* graph = new (allocator) HGraph(allocator);
+
+ HBasicBlock* entry = new (allocator) HBasicBlock(graph);
+ graph->AddBlock(entry);
+ graph->SetEntryBlock(entry);
+ HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimNot);
+ HInstruction* constant_initial = new (allocator) HIntConstant(initial);
+ HInstruction* constant_increment = new (allocator) HIntConstant(increment);
+ HInstruction* constant_10 = new (allocator) HIntConstant(10);
+ entry->AddInstruction(parameter);
+ entry->AddInstruction(constant_initial);
+ entry->AddInstruction(constant_increment);
+ entry->AddInstruction(constant_10);
+
+ HBasicBlock* block = new (allocator) HBasicBlock(graph);
+ graph->AddBlock(block);
+ entry->AddSuccessor(block);
+ block->AddInstruction(new (allocator) HGoto());
+
+ HBasicBlock* loop_header = new (allocator) HBasicBlock(graph);
+ HBasicBlock* loop_body = new (allocator) HBasicBlock(graph);
+ HBasicBlock* exit = new (allocator) HBasicBlock(graph);
+
+ graph->AddBlock(loop_header);
+ graph->AddBlock(loop_body);
+ graph->AddBlock(exit);
+ block->AddSuccessor(loop_header);
+ loop_header->AddSuccessor(exit); // true successor
+ loop_header->AddSuccessor(loop_body); // false successor
+ loop_body->AddSuccessor(loop_header);
+
+ HPhi* phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
+ phi->AddInput(constant_initial);
+ HInstruction* null_check = new (allocator) HNullCheck(parameter, 0);
+ HInstruction* array_length = new (allocator) HArrayLength(null_check);
+ HInstruction* cmp = nullptr;
+ if (cond == kCondGE) {
+ cmp = new (allocator) HGreaterThanOrEqual(phi, array_length);
+ } else {
+ DCHECK(cond == kCondGT);
+ cmp = new (allocator) HGreaterThan(phi, array_length);
+ }
+ HInstruction* if_inst = new (allocator) HIf(cmp);
+ loop_header->AddPhi(phi);
+ loop_header->AddInstruction(null_check);
+ loop_header->AddInstruction(array_length);
+ loop_header->AddInstruction(cmp);
+ loop_header->AddInstruction(if_inst);
+
+ null_check = new (allocator) HNullCheck(parameter, 0);
+ array_length = new (allocator) HArrayLength(null_check);
+ *bounds_check = new (allocator) HBoundsCheck(phi, array_length, 0);
+ HInstruction* array_set = new (allocator) HArraySet(
+ null_check, *bounds_check, constant_10, Primitive::kPrimInt, 0);
+
+ HInstruction* add = new (allocator) HAdd(Primitive::kPrimInt, phi, constant_increment);
+ loop_body->AddInstruction(null_check);
+ loop_body->AddInstruction(array_length);
+ loop_body->AddInstruction(*bounds_check);
+ loop_body->AddInstruction(array_set);
+ loop_body->AddInstruction(add);
+ loop_body->AddInstruction(new (allocator) HGoto());
+ phi->AddInput(add);
+
+ exit->AddInstruction(new (allocator) HExit());
+
+ return graph;
+}
+
+TEST(BoundsCheckEliminationTest, LoopArrayBoundsElimination1) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ // for (int i=0; i<array.length; i++) { array[i] = 10; // Can eliminate with gvn. }
+ HInstruction* bounds_check = nullptr;
+ HGraph* graph = BuildSSAGraph1(&allocator, &bounds_check, 0, 1);
+ graph->BuildDominatorTree();
+ BoundsCheckElimination bounds_check_elimination(graph);
+ bounds_check_elimination.Run();
+ ASSERT_FALSE(IsRemoved(bounds_check));
+
+ // This time add gvn. Need gvn to eliminate the second
+ // HArrayLength which uses the null check as its input.
+ graph = BuildSSAGraph1(&allocator, &bounds_check, 0, 1);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_after_gvn(graph);
+ bounds_check_elimination_after_gvn.Run();
+ ASSERT_TRUE(IsRemoved(bounds_check));
+
+ // for (int i=1; i<array.length; i++) { array[i] = 10; // Can eliminate. }
+ graph = BuildSSAGraph1(&allocator, &bounds_check, 1, 1);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_with_initial_1(graph);
+ bounds_check_elimination_with_initial_1.Run();
+ ASSERT_TRUE(IsRemoved(bounds_check));
+
+ // for (int i=-1; i<array.length; i++) { array[i] = 10; // Can't eliminate. }
+ graph = BuildSSAGraph1(&allocator, &bounds_check, -1, 1);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_with_initial_minus_1(graph);
+ bounds_check_elimination_with_initial_minus_1.Run();
+ ASSERT_FALSE(IsRemoved(bounds_check));
+
+ // for (int i=0; i<=array.length; i++) { array[i] = 10; // Can't eliminate. }
+ graph = BuildSSAGraph1(&allocator, &bounds_check, 0, 1, kCondGT);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_with_greater_than(graph);
+ bounds_check_elimination_with_greater_than.Run();
+ ASSERT_FALSE(IsRemoved(bounds_check));
+
+ // for (int i=0; i<array.length; i += 2) {
+ // array[i] = 10; // Can't eliminate due to overflow concern. }
+ graph = BuildSSAGraph1(&allocator, &bounds_check, 0, 2);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_with_increment_2(graph);
+ bounds_check_elimination_with_increment_2.Run();
+ ASSERT_FALSE(IsRemoved(bounds_check));
+
+ // for (int i=1; i<array.length; i += 2) { array[i] = 10; // Can eliminate. }
+ graph = BuildSSAGraph1(&allocator, &bounds_check, 1, 2);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_with_increment_2_from_1(graph);
+ bounds_check_elimination_with_increment_2_from_1.Run();
+ ASSERT_TRUE(IsRemoved(bounds_check));
+}
+
+// for (int i=array.length; i>0; i+=increment) { array[i-1] = 10; }
+static HGraph* BuildSSAGraph2(ArenaAllocator* allocator,
+ HInstruction** bounds_check,
+ int initial,
+ int increment = -1,
+ IfCondition cond = kCondLE) {
+ HGraph* graph = new (allocator) HGraph(allocator);
+
+ HBasicBlock* entry = new (allocator) HBasicBlock(graph);
+ graph->AddBlock(entry);
+ graph->SetEntryBlock(entry);
+ HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimNot);
+ HInstruction* constant_initial = new (allocator) HIntConstant(initial);
+ HInstruction* constant_increment = new (allocator) HIntConstant(increment);
+ HInstruction* constant_minus_1 = new (allocator) HIntConstant(-1);
+ HInstruction* constant_10 = new (allocator) HIntConstant(10);
+ entry->AddInstruction(parameter);
+ entry->AddInstruction(constant_initial);
+ entry->AddInstruction(constant_increment);
+ entry->AddInstruction(constant_minus_1);
+ entry->AddInstruction(constant_10);
+
+ HBasicBlock* block = new (allocator) HBasicBlock(graph);
+ graph->AddBlock(block);
+ entry->AddSuccessor(block);
+ HInstruction* null_check = new (allocator) HNullCheck(parameter, 0);
+ HInstruction* array_length = new (allocator) HArrayLength(null_check);
+ block->AddInstruction(null_check);
+ block->AddInstruction(array_length);
+ block->AddInstruction(new (allocator) HGoto());
+
+ HBasicBlock* loop_header = new (allocator) HBasicBlock(graph);
+ HBasicBlock* loop_body = new (allocator) HBasicBlock(graph);
+ HBasicBlock* exit = new (allocator) HBasicBlock(graph);
+
+ graph->AddBlock(loop_header);
+ graph->AddBlock(loop_body);
+ graph->AddBlock(exit);
+ block->AddSuccessor(loop_header);
+ loop_header->AddSuccessor(exit); // true successor
+ loop_header->AddSuccessor(loop_body); // false successor
+ loop_body->AddSuccessor(loop_header);
+
+ HPhi* phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
+ phi->AddInput(array_length);
+ HInstruction* cmp = nullptr;
+ if (cond == kCondLE) {
+ cmp = new (allocator) HLessThanOrEqual(phi, constant_initial);
+ } else {
+ DCHECK(cond == kCondLT);
+ cmp = new (allocator) HLessThan(phi, constant_initial);
+ }
+ HInstruction* if_inst = new (allocator) HIf(cmp);
+ loop_header->AddPhi(phi);
+ loop_header->AddInstruction(cmp);
+ loop_header->AddInstruction(if_inst);
+
+ HInstruction* add = new (allocator) HAdd(Primitive::kPrimInt, phi, constant_minus_1);
+ null_check = new (allocator) HNullCheck(parameter, 0);
+ array_length = new (allocator) HArrayLength(null_check);
+ *bounds_check = new (allocator) HBoundsCheck(add, array_length, 0);
+ HInstruction* array_set = new (allocator) HArraySet(
+ null_check, *bounds_check, constant_10, Primitive::kPrimInt, 0);
+ HInstruction* add_phi = new (allocator) HAdd(Primitive::kPrimInt, phi, constant_increment);
+ loop_body->AddInstruction(add);
+ loop_body->AddInstruction(null_check);
+ loop_body->AddInstruction(array_length);
+ loop_body->AddInstruction(*bounds_check);
+ loop_body->AddInstruction(array_set);
+ loop_body->AddInstruction(add_phi);
+ loop_body->AddInstruction(new (allocator) HGoto());
+ phi->AddInput(add);
+
+ exit->AddInstruction(new (allocator) HExit());
+
+ return graph;
+}
+
+TEST(BoundsCheckEliminationTest, LoopArrayBoundsElimination2) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ // for (int i=array.length; i>0; i--) { array[i-1] = 10; // Can eliminate with gvn. }
+ HInstruction* bounds_check = nullptr;
+ HGraph* graph = BuildSSAGraph2(&allocator, &bounds_check, 0);
+ graph->BuildDominatorTree();
+ BoundsCheckElimination bounds_check_elimination(graph);
+ bounds_check_elimination.Run();
+ ASSERT_FALSE(IsRemoved(bounds_check));
+
+ // This time add gvn. Need gvn to eliminate the second
+ // HArrayLength which uses the null check as its input.
+ graph = BuildSSAGraph2(&allocator, &bounds_check, 0);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_after_gvn(graph);
+ bounds_check_elimination_after_gvn.Run();
+ ASSERT_TRUE(IsRemoved(bounds_check));
+
+ // for (int i=array.length; i>1; i--) { array[i-1] = 10; // Can eliminate. }
+ graph = BuildSSAGraph2(&allocator, &bounds_check, 1);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_with_initial_1(graph);
+ bounds_check_elimination_with_initial_1.Run();
+ ASSERT_TRUE(IsRemoved(bounds_check));
+
+ // for (int i=array.length; i>-1; i--) { array[i-1] = 10; // Can't eliminate. }
+ graph = BuildSSAGraph2(&allocator, &bounds_check, -1);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_with_initial_minus_1(graph);
+ bounds_check_elimination_with_initial_minus_1.Run();
+ ASSERT_FALSE(IsRemoved(bounds_check));
+
+ // for (int i=array.length; i>=0; i--) { array[i-1] = 10; // Can't eliminate. }
+ graph = BuildSSAGraph2(&allocator, &bounds_check, 0, -1, kCondLT);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_with_less_than(graph);
+ bounds_check_elimination_with_less_than.Run();
+ ASSERT_FALSE(IsRemoved(bounds_check));
+
+ // for (int i=array.length; i>0; i-=2) { array[i-1] = 10; // Can eliminate. }
+ graph = BuildSSAGraph2(&allocator, &bounds_check, 0, -2);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_increment_minus_2(graph);
+ bounds_check_elimination_increment_minus_2.Run();
+ ASSERT_TRUE(IsRemoved(bounds_check));
+}
+
+// int[] array = new array[10];
+// for (int i=0; i<10; i+=increment) { array[i] = 10; }
+static HGraph* BuildSSAGraph3(ArenaAllocator* allocator,
+ HInstruction** bounds_check,
+ int initial,
+ int increment,
+ IfCondition cond) {
+ HGraph* graph = new (allocator) HGraph(allocator);
+
+ HBasicBlock* entry = new (allocator) HBasicBlock(graph);
+ graph->AddBlock(entry);
+ graph->SetEntryBlock(entry);
+ HInstruction* constant_10 = new (allocator) HIntConstant(10);
+ HInstruction* constant_initial = new (allocator) HIntConstant(initial);
+ HInstruction* constant_increment = new (allocator) HIntConstant(increment);
+ entry->AddInstruction(constant_10);
+ entry->AddInstruction(constant_initial);
+ entry->AddInstruction(constant_increment);
+
+ HBasicBlock* block = new (allocator) HBasicBlock(graph);
+ graph->AddBlock(block);
+ entry->AddSuccessor(block);
+ HInstruction* new_array = new (allocator)
+ HNewArray(constant_10, 0, Primitive::kPrimInt, kQuickAllocArray);
+ block->AddInstruction(new_array);
+ block->AddInstruction(new (allocator) HGoto());
+
+ HBasicBlock* loop_header = new (allocator) HBasicBlock(graph);
+ HBasicBlock* loop_body = new (allocator) HBasicBlock(graph);
+ HBasicBlock* exit = new (allocator) HBasicBlock(graph);
+
+ graph->AddBlock(loop_header);
+ graph->AddBlock(loop_body);
+ graph->AddBlock(exit);
+ block->AddSuccessor(loop_header);
+ loop_header->AddSuccessor(exit); // true successor
+ loop_header->AddSuccessor(loop_body); // false successor
+ loop_body->AddSuccessor(loop_header);
+
+ HPhi* phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
+ phi->AddInput(constant_initial);
+ HInstruction* cmp = nullptr;
+ if (cond == kCondGE) {
+ cmp = new (allocator) HGreaterThanOrEqual(phi, constant_10);
+ } else {
+ DCHECK(cond == kCondGT);
+ cmp = new (allocator) HGreaterThan(phi, constant_10);
+ }
+ HInstruction* if_inst = new (allocator) HIf(cmp);
+ loop_header->AddPhi(phi);
+ loop_header->AddInstruction(cmp);
+ loop_header->AddInstruction(if_inst);
+
+ HNullCheck* null_check = new (allocator) HNullCheck(new_array, 0);
+ HArrayLength* array_length = new (allocator) HArrayLength(null_check);
+ *bounds_check = new (allocator) HBoundsCheck(phi, array_length, 0);
+ HInstruction* array_set = new (allocator) HArraySet(
+ null_check, *bounds_check, constant_10, Primitive::kPrimInt, 0);
+ HInstruction* add = new (allocator) HAdd(Primitive::kPrimInt, phi, constant_increment);
+ loop_body->AddInstruction(null_check);
+ loop_body->AddInstruction(array_length);
+ loop_body->AddInstruction(*bounds_check);
+ loop_body->AddInstruction(array_set);
+ loop_body->AddInstruction(add);
+ loop_body->AddInstruction(new (allocator) HGoto());
+ phi->AddInput(add);
+
+ exit->AddInstruction(new (allocator) HExit());
+
+ return graph;
+}
+
+TEST(BoundsCheckEliminationTest, LoopArrayBoundsElimination3) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ // int[] array = new array[10];
+ // for (int i=0; i<10; i++) { array[i] = 10; // Can eliminate. }
+ HInstruction* bounds_check = nullptr;
+ HGraph* graph = BuildSSAGraph3(&allocator, &bounds_check, 0, 1, kCondGE);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_after_gvn(graph);
+ bounds_check_elimination_after_gvn.Run();
+ ASSERT_TRUE(IsRemoved(bounds_check));
+
+ // int[] array = new array[10];
+ // for (int i=1; i<10; i++) { array[i] = 10; // Can eliminate. }
+ graph = BuildSSAGraph3(&allocator, &bounds_check, 1, 1, kCondGE);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_with_initial_1(graph);
+ bounds_check_elimination_with_initial_1.Run();
+ ASSERT_TRUE(IsRemoved(bounds_check));
+
+ // int[] array = new array[10];
+ // for (int i=0; i<=10; i++) { array[i] = 10; // Can't eliminate. }
+ graph = BuildSSAGraph3(&allocator, &bounds_check, 0, 1, kCondGT);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_with_greater_than(graph);
+ bounds_check_elimination_with_greater_than.Run();
+ ASSERT_FALSE(IsRemoved(bounds_check));
+
+ // int[] array = new array[10];
+ // for (int i=1; i<10; i+=8) { array[i] = 10; // Can eliminate. }
+ graph = BuildSSAGraph3(&allocator, &bounds_check, 1, 8, kCondGE);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_increment_8(graph);
+ bounds_check_elimination_increment_8.Run();
+ ASSERT_TRUE(IsRemoved(bounds_check));
+}
+
+// for (int i=initial; i<array.length; i++) { array[array.length-i-1] = 10; }
+static HGraph* BuildSSAGraph4(ArenaAllocator* allocator,
+ HInstruction** bounds_check,
+ int initial,
+ IfCondition cond = kCondGE) {
+ HGraph* graph = new (allocator) HGraph(allocator);
+
+ HBasicBlock* entry = new (allocator) HBasicBlock(graph);
+ graph->AddBlock(entry);
+ graph->SetEntryBlock(entry);
+ HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimNot);
+ HInstruction* constant_initial = new (allocator) HIntConstant(initial);
+ HInstruction* constant_1 = new (allocator) HIntConstant(1);
+ HInstruction* constant_10 = new (allocator) HIntConstant(10);
+ HInstruction* constant_minus_1 = new (allocator) HIntConstant(-1);
+ entry->AddInstruction(parameter);
+ entry->AddInstruction(constant_initial);
+ entry->AddInstruction(constant_1);
+ entry->AddInstruction(constant_10);
+ entry->AddInstruction(constant_minus_1);
+
+ HBasicBlock* block = new (allocator) HBasicBlock(graph);
+ graph->AddBlock(block);
+ entry->AddSuccessor(block);
+ block->AddInstruction(new (allocator) HGoto());
+
+ HBasicBlock* loop_header = new (allocator) HBasicBlock(graph);
+ HBasicBlock* loop_body = new (allocator) HBasicBlock(graph);
+ HBasicBlock* exit = new (allocator) HBasicBlock(graph);
+
+ graph->AddBlock(loop_header);
+ graph->AddBlock(loop_body);
+ graph->AddBlock(exit);
+ block->AddSuccessor(loop_header);
+ loop_header->AddSuccessor(exit); // true successor
+ loop_header->AddSuccessor(loop_body); // false successor
+ loop_body->AddSuccessor(loop_header);
+
+ HPhi* phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
+ phi->AddInput(constant_initial);
+ HInstruction* null_check = new (allocator) HNullCheck(parameter, 0);
+ HInstruction* array_length = new (allocator) HArrayLength(null_check);
+ HInstruction* cmp = nullptr;
+ if (cond == kCondGE) {
+ cmp = new (allocator) HGreaterThanOrEqual(phi, array_length);
+ } else if (cond == kCondGT) {
+ cmp = new (allocator) HGreaterThan(phi, array_length);
+ }
+ HInstruction* if_inst = new (allocator) HIf(cmp);
+ loop_header->AddPhi(phi);
+ loop_header->AddInstruction(null_check);
+ loop_header->AddInstruction(array_length);
+ loop_header->AddInstruction(cmp);
+ loop_header->AddInstruction(if_inst);
+
+ null_check = new (allocator) HNullCheck(parameter, 0);
+ array_length = new (allocator) HArrayLength(null_check);
+ HInstruction* sub = new (allocator) HSub(Primitive::kPrimInt, array_length, phi);
+ HInstruction* add_minus_1 = new (allocator)
+ HAdd(Primitive::kPrimInt, sub, constant_minus_1);
+ *bounds_check = new (allocator) HBoundsCheck(add_minus_1, array_length, 0);
+ HInstruction* array_set = new (allocator) HArraySet(
+ null_check, *bounds_check, constant_10, Primitive::kPrimInt, 0);
+ HInstruction* add = new (allocator) HAdd(Primitive::kPrimInt, phi, constant_1);
+ loop_body->AddInstruction(null_check);
+ loop_body->AddInstruction(array_length);
+ loop_body->AddInstruction(sub);
+ loop_body->AddInstruction(add_minus_1);
+ loop_body->AddInstruction(*bounds_check);
+ loop_body->AddInstruction(array_set);
+ loop_body->AddInstruction(add);
+ loop_body->AddInstruction(new (allocator) HGoto());
+ phi->AddInput(add);
+
+ exit->AddInstruction(new (allocator) HExit());
+
+ return graph;
+}
+
+TEST(BoundsCheckEliminationTest, LoopArrayBoundsElimination4) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ // for (int i=0; i<array.length; i++) { array[array.length-i-1] = 10; // Can eliminate with gvn. }
+ HInstruction* bounds_check = nullptr;
+ HGraph* graph = BuildSSAGraph4(&allocator, &bounds_check, 0);
+ graph->BuildDominatorTree();
+ BoundsCheckElimination bounds_check_elimination(graph);
+ bounds_check_elimination.Run();
+ ASSERT_FALSE(IsRemoved(bounds_check));
+
+ // This time add gvn. Need gvn to eliminate the second
+ // HArrayLength which uses the null check as its input.
+ graph = BuildSSAGraph4(&allocator, &bounds_check, 0);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_after_gvn(graph);
+ bounds_check_elimination_after_gvn.Run();
+ ASSERT_TRUE(IsRemoved(bounds_check));
+
+ // for (int i=1; i<array.length; i++) { array[array.length-i-1] = 10; // Can eliminate. }
+ graph = BuildSSAGraph4(&allocator, &bounds_check, 1);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_with_initial_1(graph);
+ bounds_check_elimination_with_initial_1.Run();
+ ASSERT_TRUE(IsRemoved(bounds_check));
+
+ // for (int i=0; i<=array.length; i++) { array[array.length-i] = 10; // Can't eliminate. }
+ graph = BuildSSAGraph4(&allocator, &bounds_check, 0, kCondGT);
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ BoundsCheckElimination bounds_check_elimination_with_greater_than(graph);
+ bounds_check_elimination_with_greater_than.Run();
+ ASSERT_FALSE(IsRemoved(bounds_check));
+}
+
+// Bubble sort:
+// (Every array access bounds-check can be eliminated.)
+// for (int i=0; i<array.length-1; i++) {
+// for (int j=0; j<array.length-i-1; j++) {
+// if (array[j] > array[j+1]) {
+// int temp = array[j+1];
+// array[j+1] = array[j];
+// array[j] = temp;
+// }
+// }
+// }
+TEST(BoundsCheckEliminationTest, BubbleSortArrayBoundsElimination) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+
+ HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(entry);
+ graph->SetEntryBlock(entry);
+ HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
+ HInstruction* constant_0 = new (&allocator) HIntConstant(0);
+ HInstruction* constant_minus_1 = new (&allocator) HIntConstant(-1);
+ HInstruction* constant_1 = new (&allocator) HIntConstant(1);
+ entry->AddInstruction(parameter);
+ entry->AddInstruction(constant_0);
+ entry->AddInstruction(constant_minus_1);
+ entry->AddInstruction(constant_1);
+
+ HBasicBlock* block = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block);
+ entry->AddSuccessor(block);
+ block->AddInstruction(new (&allocator) HGoto());
+
+ HBasicBlock* exit = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(exit);
+ exit->AddInstruction(new (&allocator) HExit());
+
+ HBasicBlock* outer_header = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(outer_header);
+ HPhi* phi_i = new (&allocator) HPhi(&allocator, 0, 0, Primitive::kPrimInt);
+ phi_i->AddInput(constant_0);
+ HNullCheck* null_check = new (&allocator) HNullCheck(parameter, 0);
+ HArrayLength* array_length = new (&allocator) HArrayLength(null_check);
+ HAdd* add = new (&allocator) HAdd(Primitive::kPrimInt, array_length, constant_minus_1);
+ HInstruction* cmp = new (&allocator) HGreaterThanOrEqual(phi_i, add);
+ HIf* if_inst = new (&allocator) HIf(cmp);
+ outer_header->AddPhi(phi_i);
+ outer_header->AddInstruction(null_check);
+ outer_header->AddInstruction(array_length);
+ outer_header->AddInstruction(add);
+ outer_header->AddInstruction(cmp);
+ outer_header->AddInstruction(if_inst);
+
+ HBasicBlock* inner_header = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(inner_header);
+ HPhi* phi_j = new (&allocator) HPhi(&allocator, 0, 0, Primitive::kPrimInt);
+ phi_j->AddInput(constant_0);
+ null_check = new (&allocator) HNullCheck(parameter, 0);
+ array_length = new (&allocator) HArrayLength(null_check);
+ HSub* sub = new (&allocator) HSub(Primitive::kPrimInt, array_length, phi_i);
+ add = new (&allocator) HAdd(Primitive::kPrimInt, sub, constant_minus_1);
+ cmp = new (&allocator) HGreaterThanOrEqual(phi_j, add);
+ if_inst = new (&allocator) HIf(cmp);
+ inner_header->AddPhi(phi_j);
+ inner_header->AddInstruction(null_check);
+ inner_header->AddInstruction(array_length);
+ inner_header->AddInstruction(sub);
+ inner_header->AddInstruction(add);
+ inner_header->AddInstruction(cmp);
+ inner_header->AddInstruction(if_inst);
+
+ HBasicBlock* inner_body_compare = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(inner_body_compare);
+ null_check = new (&allocator) HNullCheck(parameter, 0);
+ array_length = new (&allocator) HArrayLength(null_check);
+ HBoundsCheck* bounds_check1 = new (&allocator) HBoundsCheck(phi_j, array_length, 0);
+ HArrayGet* array_get_j = new (&allocator)
+ HArrayGet(null_check, bounds_check1, Primitive::kPrimInt);
+ inner_body_compare->AddInstruction(null_check);
+ inner_body_compare->AddInstruction(array_length);
+ inner_body_compare->AddInstruction(bounds_check1);
+ inner_body_compare->AddInstruction(array_get_j);
+ HInstruction* j_plus_1 = new (&allocator) HAdd(Primitive::kPrimInt, phi_j, constant_1);
+ null_check = new (&allocator) HNullCheck(parameter, 0);
+ array_length = new (&allocator) HArrayLength(null_check);
+ HBoundsCheck* bounds_check2 = new (&allocator) HBoundsCheck(j_plus_1, array_length, 0);
+ HArrayGet* array_get_j_plus_1 = new (&allocator)
+ HArrayGet(null_check, bounds_check2, Primitive::kPrimInt);
+ cmp = new (&allocator) HGreaterThanOrEqual(array_get_j, array_get_j_plus_1);
+ if_inst = new (&allocator) HIf(cmp);
+ inner_body_compare->AddInstruction(j_plus_1);
+ inner_body_compare->AddInstruction(null_check);
+ inner_body_compare->AddInstruction(array_length);
+ inner_body_compare->AddInstruction(bounds_check2);
+ inner_body_compare->AddInstruction(array_get_j_plus_1);
+ inner_body_compare->AddInstruction(cmp);
+ inner_body_compare->AddInstruction(if_inst);
+
+ HBasicBlock* inner_body_swap = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(inner_body_swap);
+ j_plus_1 = new (&allocator) HAdd(Primitive::kPrimInt, phi_j, constant_1);
+ // temp = array[j+1]
+ null_check = new (&allocator) HNullCheck(parameter, 0);
+ array_length = new (&allocator) HArrayLength(null_check);
+ HInstruction* bounds_check3 = new (&allocator) HBoundsCheck(j_plus_1, array_length, 0);
+ array_get_j_plus_1 = new (&allocator)
+ HArrayGet(null_check, bounds_check3, Primitive::kPrimInt);
+ inner_body_swap->AddInstruction(j_plus_1);
+ inner_body_swap->AddInstruction(null_check);
+ inner_body_swap->AddInstruction(array_length);
+ inner_body_swap->AddInstruction(bounds_check3);
+ inner_body_swap->AddInstruction(array_get_j_plus_1);
+ // array[j+1] = array[j]
+ null_check = new (&allocator) HNullCheck(parameter, 0);
+ array_length = new (&allocator) HArrayLength(null_check);
+ HInstruction* bounds_check4 = new (&allocator) HBoundsCheck(phi_j, array_length, 0);
+ array_get_j = new (&allocator)
+ HArrayGet(null_check, bounds_check4, Primitive::kPrimInt);
+ inner_body_swap->AddInstruction(null_check);
+ inner_body_swap->AddInstruction(array_length);
+ inner_body_swap->AddInstruction(bounds_check4);
+ inner_body_swap->AddInstruction(array_get_j);
+ null_check = new (&allocator) HNullCheck(parameter, 0);
+ array_length = new (&allocator) HArrayLength(null_check);
+ HInstruction* bounds_check5 = new (&allocator) HBoundsCheck(j_plus_1, array_length, 0);
+ HArraySet* array_set_j_plus_1 = new (&allocator)
+ HArraySet(null_check, bounds_check5, array_get_j, Primitive::kPrimInt, 0);
+ inner_body_swap->AddInstruction(null_check);
+ inner_body_swap->AddInstruction(array_length);
+ inner_body_swap->AddInstruction(bounds_check5);
+ inner_body_swap->AddInstruction(array_set_j_plus_1);
+ // array[j] = temp
+ null_check = new (&allocator) HNullCheck(parameter, 0);
+ array_length = new (&allocator) HArrayLength(null_check);
+ HInstruction* bounds_check6 = new (&allocator) HBoundsCheck(phi_j, array_length, 0);
+ HArraySet* array_set_j = new (&allocator)
+ HArraySet(null_check, bounds_check6, array_get_j_plus_1, Primitive::kPrimInt, 0);
+ inner_body_swap->AddInstruction(null_check);
+ inner_body_swap->AddInstruction(array_length);
+ inner_body_swap->AddInstruction(bounds_check6);
+ inner_body_swap->AddInstruction(array_set_j);
+ inner_body_swap->AddInstruction(new (&allocator) HGoto());
+
+ HBasicBlock* inner_body_add = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(inner_body_add);
+ add = new (&allocator) HAdd(Primitive::kPrimInt, phi_j, constant_1);
+ inner_body_add->AddInstruction(add);
+ inner_body_add->AddInstruction(new (&allocator) HGoto());
+ phi_j->AddInput(add);
+
+ HBasicBlock* outer_body_add = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(outer_body_add);
+ add = new (&allocator) HAdd(Primitive::kPrimInt, phi_i, constant_1);
+ outer_body_add->AddInstruction(add);
+ outer_body_add->AddInstruction(new (&allocator) HGoto());
+ phi_i->AddInput(add);
+
+ block->AddSuccessor(outer_header);
+ outer_header->AddSuccessor(exit);
+ outer_header->AddSuccessor(inner_header);
+ inner_header->AddSuccessor(outer_body_add);
+ inner_header->AddSuccessor(inner_body_compare);
+ inner_body_compare->AddSuccessor(inner_body_add);
+ inner_body_compare->AddSuccessor(inner_body_swap);
+ inner_body_swap->AddSuccessor(inner_body_add);
+ inner_body_add->AddSuccessor(inner_header);
+ outer_body_add->AddSuccessor(outer_header);
+
+ graph->BuildDominatorTree();
+ RunSimplifierAndGvn(graph);
+ // gvn should remove the same bounds check.
+ ASSERT_FALSE(IsRemoved(bounds_check1));
+ ASSERT_FALSE(IsRemoved(bounds_check2));
+ ASSERT_TRUE(IsRemoved(bounds_check3));
+ ASSERT_TRUE(IsRemoved(bounds_check4));
+ ASSERT_TRUE(IsRemoved(bounds_check5));
+ ASSERT_TRUE(IsRemoved(bounds_check6));
+
+ BoundsCheckElimination bounds_check_elimination(graph);
+ bounds_check_elimination.Run();
+ ASSERT_TRUE(IsRemoved(bounds_check1));
+ ASSERT_TRUE(IsRemoved(bounds_check2));
+ ASSERT_TRUE(IsRemoved(bounds_check3));
+ ASSERT_TRUE(IsRemoved(bounds_check4));
+ ASSERT_TRUE(IsRemoved(bounds_check5));
+ ASSERT_TRUE(IsRemoved(bounds_check6));
+}
+
+} // namespace art
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index eb6181c..20a1b03 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -16,6 +16,7 @@
#include "builder.h"
+#include "base/logging.h"
#include "class_linker.h"
#include "dex_file.h"
#include "dex_file-inl.h"
@@ -68,6 +69,74 @@
size_t index_;
};
+class SwitchTable : public ValueObject {
+ public:
+ SwitchTable(const Instruction& instruction, uint32_t dex_pc, bool sparse)
+ : instruction_(instruction), dex_pc_(dex_pc), sparse_(sparse) {
+ int32_t table_offset = instruction.VRegB_31t();
+ const uint16_t* table = reinterpret_cast<const uint16_t*>(&instruction) + table_offset;
+ if (sparse) {
+ CHECK_EQ(table[0], static_cast<uint16_t>(Instruction::kSparseSwitchSignature));
+ } else {
+ CHECK_EQ(table[0], static_cast<uint16_t>(Instruction::kPackedSwitchSignature));
+ }
+ num_entries_ = table[1];
+ values_ = reinterpret_cast<const int32_t*>(&table[2]);
+ }
+
+ uint16_t GetNumEntries() const {
+ return num_entries_;
+ }
+
+ void CheckIndex(size_t index) const {
+ if (sparse_) {
+ // In a sparse table, we have num_entries_ keys and num_entries_ values, in that order.
+ DCHECK_LT(index, 2 * static_cast<size_t>(num_entries_));
+ } else {
+ // In a packed table, we have the starting key and num_entries_ values.
+ DCHECK_LT(index, 1 + static_cast<size_t>(num_entries_));
+ }
+ }
+
+ int32_t GetEntryAt(size_t index) const {
+ CheckIndex(index);
+ return values_[index];
+ }
+
+ uint32_t GetDexPcForIndex(size_t index) const {
+ CheckIndex(index);
+ return dex_pc_ +
+ (reinterpret_cast<const int16_t*>(values_ + index) -
+ reinterpret_cast<const int16_t*>(&instruction_));
+ }
+
+ // Index of the first value in the table.
+ size_t GetFirstValueIndex() const {
+ if (sparse_) {
+ // In a sparse table, we have num_entries_ keys and num_entries_ values, in that order.
+ return num_entries_;
+ } else {
+ // In a packed table, we have the starting key and num_entries_ values.
+ return 1;
+ }
+ }
+
+ private:
+ const Instruction& instruction_;
+ const uint32_t dex_pc_;
+
+ // Whether this is a sparse-switch table (or a packed-switch one).
+ const bool sparse_;
+
+ // This can't be const as it needs to be computed off of the given instruction, and complicated
+ // expressions in the initializer list seemed very ugly.
+ uint16_t num_entries_;
+
+ const int32_t* values_;
+
+ DISALLOW_COPY_AND_ASSIGN(SwitchTable);
+};
+
void HGraphBuilder::InitializeLocals(uint16_t count) {
graph_->SetNumberOfVRegs(count);
locals_.SetSize(count);
@@ -92,7 +161,7 @@
if (!dex_compilation_unit_->IsStatic()) {
// Add the implicit 'this' argument, not expressed in the signature.
HParameterValue* parameter =
- new (arena_) HParameterValue(parameter_index++, Primitive::kPrimNot);
+ new (arena_) HParameterValue(parameter_index++, Primitive::kPrimNot, true);
entry_block_->AddInstruction(parameter);
HLocal* local = GetLocalAt(locals_index++);
entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter));
@@ -121,78 +190,83 @@
template<typename T>
void HGraphBuilder::If_22t(const Instruction& instruction, uint32_t dex_pc) {
int32_t target_offset = instruction.GetTargetOffset();
- PotentiallyAddSuspendCheck(target_offset, dex_pc);
+ HBasicBlock* branch_target = FindBlockStartingAt(dex_pc + target_offset);
+ HBasicBlock* fallthrough_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
+ DCHECK(branch_target != nullptr);
+ DCHECK(fallthrough_target != nullptr);
+ PotentiallyAddSuspendCheck(branch_target, dex_pc);
HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
T* comparison = new (arena_) T(first, second);
current_block_->AddInstruction(comparison);
HInstruction* ifinst = new (arena_) HIf(comparison);
current_block_->AddInstruction(ifinst);
- HBasicBlock* target = FindBlockStartingAt(dex_pc + target_offset);
- DCHECK(target != nullptr);
- current_block_->AddSuccessor(target);
- target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
- DCHECK(target != nullptr);
- current_block_->AddSuccessor(target);
+ current_block_->AddSuccessor(branch_target);
+ current_block_->AddSuccessor(fallthrough_target);
current_block_ = nullptr;
}
template<typename T>
void HGraphBuilder::If_21t(const Instruction& instruction, uint32_t dex_pc) {
int32_t target_offset = instruction.GetTargetOffset();
- PotentiallyAddSuspendCheck(target_offset, dex_pc);
+ HBasicBlock* branch_target = FindBlockStartingAt(dex_pc + target_offset);
+ HBasicBlock* fallthrough_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
+ DCHECK(branch_target != nullptr);
+ DCHECK(fallthrough_target != nullptr);
+ PotentiallyAddSuspendCheck(branch_target, dex_pc);
HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
T* comparison = new (arena_) T(value, GetIntConstant(0));
current_block_->AddInstruction(comparison);
HInstruction* ifinst = new (arena_) HIf(comparison);
current_block_->AddInstruction(ifinst);
- HBasicBlock* target = FindBlockStartingAt(dex_pc + target_offset);
- DCHECK(target != nullptr);
- current_block_->AddSuccessor(target);
- target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
- DCHECK(target != nullptr);
- current_block_->AddSuccessor(target);
+ current_block_->AddSuccessor(branch_target);
+ current_block_->AddSuccessor(fallthrough_target);
current_block_ = nullptr;
}
-static bool ShouldSkipCompilation(const CompilerDriver& compiler_driver,
- const DexCompilationUnit& dex_compilation_unit,
- size_t number_of_dex_instructions,
- size_t number_of_blocks ATTRIBUTE_UNUSED,
- size_t number_of_branches) {
- const CompilerOptions& compiler_options = compiler_driver.GetCompilerOptions();
+void HGraphBuilder::MaybeRecordStat(MethodCompilationStat compilation_stat) {
+ if (compilation_stats_ != nullptr) {
+ compilation_stats_->RecordStat(compilation_stat);
+ }
+}
+
+bool HGraphBuilder::SkipCompilation(size_t number_of_dex_instructions,
+ size_t number_of_blocks ATTRIBUTE_UNUSED,
+ size_t number_of_branches) {
+ const CompilerOptions& compiler_options = compiler_driver_->GetCompilerOptions();
CompilerOptions::CompilerFilter compiler_filter = compiler_options.GetCompilerFilter();
if (compiler_filter == CompilerOptions::kEverything) {
return false;
}
if (compiler_options.IsHugeMethod(number_of_dex_instructions)) {
- LOG(INFO) << "Skip compilation of huge method "
- << PrettyMethod(dex_compilation_unit.GetDexMethodIndex(),
- *dex_compilation_unit.GetDexFile())
- << ": " << number_of_dex_instructions << " dex instructions";
+ VLOG(compiler) << "Skip compilation of huge method "
+ << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
+ << ": " << number_of_dex_instructions << " dex instructions";
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledHugeMethod);
return true;
}
// If it's large and contains no branches, it's likely to be machine generated initialization.
if (compiler_options.IsLargeMethod(number_of_dex_instructions) && (number_of_branches == 0)) {
- LOG(INFO) << "Skip compilation of large method with no branch "
- << PrettyMethod(dex_compilation_unit.GetDexMethodIndex(),
- *dex_compilation_unit.GetDexFile())
- << ": " << number_of_dex_instructions << " dex instructions";
+ VLOG(compiler) << "Skip compilation of large method with no branch "
+ << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
+ << ": " << number_of_dex_instructions << " dex instructions";
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledLargeMethodNoBranches);
return true;
}
return false;
}
-HGraph* HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) {
+bool HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) {
+ DCHECK(graph_->GetBlocks().IsEmpty());
+
const uint16_t* code_ptr = code_item.insns_;
const uint16_t* code_end = code_item.insns_ + code_item.insns_size_in_code_units_;
code_start_ = code_ptr;
// Setup the graph with the entry block and exit block.
- graph_ = new (arena_) HGraph(arena_);
entry_block_ = new (arena_) HBasicBlock(graph_, 0);
graph_->AddBlock(entry_block_);
exit_block_ = new (arena_) HBasicBlock(graph_, kNoDexPc);
@@ -200,7 +274,7 @@
graph_->SetExitBlock(exit_block_);
InitializeLocals(code_item.registers_size_);
- graph_->UpdateMaximumNumberOfOutVRegs(code_item.outs_size_);
+ graph_->SetMaximumNumberOfOutVRegs(code_item.outs_size_);
// Compute the number of dex instructions, blocks, and branches. We will
// check these values against limits given to the compiler.
@@ -214,14 +288,9 @@
code_ptr, code_end, &number_of_dex_instructions, &number_of_blocks, &number_of_branches);
// Note that the compiler driver is null when unit testing.
- if (compiler_driver_ != nullptr) {
- if (ShouldSkipCompilation(*compiler_driver_,
- *dex_compilation_unit_,
- number_of_dex_instructions,
- number_of_blocks,
- number_of_branches)) {
- return nullptr;
- }
+ if ((compiler_driver_ != nullptr)
+ && SkipCompilation(number_of_dex_instructions, number_of_blocks, number_of_branches)) {
+ return false;
}
// Also create blocks for catch handlers.
@@ -250,7 +319,9 @@
// Update the current block if dex_pc starts a new block.
MaybeUpdateCurrentBlock(dex_pc);
const Instruction& instruction = *Instruction::At(code_ptr);
- if (!AnalyzeDexInstruction(instruction, dex_pc)) return nullptr;
+ if (!AnalyzeDexInstruction(instruction, dex_pc)) {
+ return false;
+ }
dex_pc += instruction.SizeInCodeUnits();
code_ptr += instruction.SizeInCodeUnits();
}
@@ -261,7 +332,8 @@
// Add the suspend check to the entry block.
entry_block_->AddInstruction(new (arena_) HSuspendCheck(0));
entry_block_->AddInstruction(new (arena_) HGoto());
- return graph_;
+
+ return true;
}
void HGraphBuilder::MaybeUpdateCurrentBlock(size_t index) {
@@ -286,7 +358,6 @@
size_t* number_of_dex_instructions,
size_t* number_of_blocks,
size_t* number_of_branches) {
- // TODO: Support switch instructions.
branch_targets_.SetSize(code_end - code_ptr);
// Create the first block for the dex instructions, single successor of the entry block.
@@ -296,7 +367,7 @@
// Iterate over all instructions and find branching instructions. Create blocks for
// the locations these instructions branch to.
- size_t dex_pc = 0;
+ uint32_t dex_pc = 0;
while (code_ptr < code_end) {
(*number_of_dex_instructions)++;
const Instruction& instruction = *Instruction::At(code_ptr);
@@ -316,6 +387,41 @@
branch_targets_.Put(dex_pc, block);
(*number_of_blocks)++;
}
+ } else if (instruction.IsSwitch()) {
+ SwitchTable table(instruction, dex_pc, instruction.Opcode() == Instruction::SPARSE_SWITCH);
+
+ uint16_t num_entries = table.GetNumEntries();
+
+ // In a packed-switch, the entry at index 0 is the starting key. In a sparse-switch, the
+ // entry at index 0 is the first key, and values are after *all* keys.
+ size_t offset = table.GetFirstValueIndex();
+
+ // Use a larger loop counter type to avoid overflow issues.
+ for (size_t i = 0; i < num_entries; ++i) {
+ // The target of the case.
+ uint32_t target = dex_pc + table.GetEntryAt(i + offset);
+ if (FindBlockStartingAt(target) == nullptr) {
+ block = new (arena_) HBasicBlock(graph_, target);
+ branch_targets_.Put(target, block);
+ (*number_of_blocks)++;
+ }
+
+ // The next case gets its own block.
+ if (i < num_entries) {
+ block = new (arena_) HBasicBlock(graph_, target);
+ branch_targets_.Put(table.GetDexPcForIndex(i), block);
+ (*number_of_blocks)++;
+ }
+ }
+
+ // Fall-through. Add a block if there is more code afterwards.
+ dex_pc += instruction.SizeInCodeUnits();
+ code_ptr += instruction.SizeInCodeUnits();
+ if ((code_ptr < code_end) && (FindBlockStartingAt(dex_pc) == nullptr)) {
+ block = new (arena_) HBasicBlock(graph_, dex_pc);
+ branch_targets_.Put(dex_pc, block);
+ (*number_of_blocks)++;
+ }
} else {
code_ptr += instruction.SizeInCodeUnits();
dex_pc += instruction.SizeInCodeUnits();
@@ -337,9 +443,10 @@
void HGraphBuilder::Conversion_12x(const Instruction& instruction,
Primitive::Type input_type,
- Primitive::Type result_type) {
+ Primitive::Type result_type,
+ uint32_t dex_pc) {
HInstruction* first = LoadLocal(instruction.VRegB(), input_type);
- current_block_->AddInstruction(new (arena_) HTypeConversion(result_type, first));
+ current_block_->AddInstruction(new (arena_) HTypeConversion(result_type, first, dex_pc));
UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
}
@@ -489,8 +596,9 @@
if (!compiler_driver_->ComputeInvokeInfo(dex_compilation_unit_, dex_pc, true, true,
&optimized_invoke_type, &target_method, &table_index,
&direct_code, &direct_method)) {
- LOG(INFO) << "Did not compile " << PrettyMethod(method_idx, *dex_file_)
- << " because a method call could not be resolved";
+ VLOG(compiler) << "Did not compile " << PrettyMethod(method_idx, *dex_file_)
+ << " because a method call could not be resolved";
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedMethod);
return false;
}
DCHECK(optimized_invoke_type != kSuper);
@@ -498,7 +606,7 @@
HInvoke* invoke = nullptr;
if (optimized_invoke_type == kVirtual) {
invoke = new (arena_) HInvokeVirtual(
- arena_, number_of_arguments, return_type, dex_pc, table_index);
+ arena_, number_of_arguments, return_type, dex_pc, method_idx, table_index);
} else if (optimized_invoke_type == kInterface) {
invoke = new (arena_) HInvokeInterface(
arena_, number_of_arguments, return_type, dex_pc, method_idx, table_index);
@@ -507,9 +615,12 @@
// Sharpening to kDirect only works if we compile PIC.
DCHECK((optimized_invoke_type == invoke_type) || (optimized_invoke_type != kDirect)
|| compiler_driver_->GetCompilerOptions().GetCompilePic());
- // Treat invoke-direct like static calls for now.
- invoke = new (arena_) HInvokeStatic(
- arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index);
+ bool is_recursive =
+ (target_method.dex_method_index == outer_compilation_unit_->GetDexMethodIndex());
+ DCHECK(!is_recursive || (target_method.dex_file == outer_compilation_unit_->GetDexFile()));
+ invoke = new (arena_) HInvokeStaticOrDirect(
+ arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index,
+ is_recursive, optimized_invoke_type);
}
size_t start_index = 0;
@@ -532,6 +643,7 @@
LOG(WARNING) << "Non sequential register pair in " << dex_compilation_unit_->GetSymbol()
<< " at " << dex_pc;
// We do not implement non sequential register pair.
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledNonSequentialRegPair);
return false;
}
HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type);
@@ -560,9 +672,7 @@
compiler_driver_->ComputeInstanceFieldInfo(field_index, dex_compilation_unit_, is_put, soa)));
if (resolved_field.Get() == nullptr) {
- return false;
- }
- if (resolved_field->IsVolatile()) {
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedField);
return false;
}
@@ -580,60 +690,68 @@
null_check,
value,
field_type,
- resolved_field->GetOffset()));
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile()));
} else {
current_block_->AddInstruction(new (arena_) HInstanceFieldGet(
current_block_->GetLastInstruction(),
field_type,
- resolved_field->GetOffset()));
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile()));
UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
}
return true;
}
-
-
bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction,
uint32_t dex_pc,
bool is_put) {
uint32_t source_or_dest_reg = instruction.VRegA_21c();
uint16_t field_index = instruction.VRegB_21c();
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<4> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->FindDexCache(*dex_compilation_unit_->GetDexFile())));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+ Handle<mirror::ArtField> resolved_field(hs.NewHandle(compiler_driver_->ResolveField(
+ soa, dex_cache, class_loader, dex_compilation_unit_, field_index, true)));
+
+ if (resolved_field.Get() == nullptr) {
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedField);
+ return false;
+ }
+
+ Handle<mirror::Class> referrer_class(hs.NewHandle(compiler_driver_->ResolveCompilingMethodsClass(
+ soa, dex_cache, class_loader, outer_compilation_unit_)));
+
+ // The index at which the field's class is stored in the DexCache's type array.
uint32_t storage_index;
- bool is_referrers_class;
- bool is_initialized;
- bool is_volatile;
- MemberOffset field_offset(0u);
- Primitive::Type field_type;
-
- bool fast_path = compiler_driver_->ComputeStaticFieldInfo(field_index,
- dex_compilation_unit_,
- is_put,
- &field_offset,
- &storage_index,
- &is_referrers_class,
- &is_volatile,
- &is_initialized,
- &field_type);
- if (!fast_path) {
+ std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField(
+ dex_cache.Get(), referrer_class.Get(), resolved_field.Get(), field_index, &storage_index);
+ bool can_easily_access = is_put ? pair.second : pair.first;
+ if (!can_easily_access) {
return false;
}
- if (is_volatile) {
- return false;
- }
+ // TODO: find out why this check is needed.
+ bool is_in_dex_cache = compiler_driver_->CanAssumeTypeIsPresentInDexCache(
+ *outer_compilation_unit_->GetDexFile(), storage_index);
+ bool is_initialized = resolved_field->GetDeclaringClass()->IsInitialized() && is_in_dex_cache;
+ bool is_referrer_class = (referrer_class.Get() == resolved_field->GetDeclaringClass());
- HLoadClass* constant = new (arena_) HLoadClass(
- storage_index, is_referrers_class, dex_pc);
+ HLoadClass* constant = new (arena_) HLoadClass(storage_index, is_referrer_class, dex_pc);
current_block_->AddInstruction(constant);
HInstruction* cls = constant;
- if (!is_initialized) {
+ if (!is_initialized && !is_referrer_class) {
cls = new (arena_) HClinitCheck(constant, dex_pc);
current_block_->AddInstruction(cls);
}
+ Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
if (is_put) {
// We need to keep the class alive before loading the value.
Temporaries temps(graph_);
@@ -641,9 +759,12 @@
HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
DCHECK_EQ(value->GetType(), field_type);
current_block_->AddInstruction(
- new (arena_) HStaticFieldSet(cls, value, field_type, field_offset));
+ new (arena_) HStaticFieldSet(cls, value, field_type, resolved_field->GetOffset(),
+ resolved_field->IsVolatile()));
} else {
- current_block_->AddInstruction(new (arena_) HStaticFieldGet(cls, field_type, field_offset));
+ current_block_->AddInstruction(
+ new (arena_) HStaticFieldGet(cls, field_type, resolved_field->GetOffset(),
+ resolved_field->IsVolatile()));
UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
}
return true;
@@ -728,7 +849,10 @@
uint32_t* args,
uint32_t register_index) {
HInstruction* length = GetIntConstant(number_of_vreg_arguments);
- HInstruction* object = new (arena_) HNewArray(length, dex_pc, type_index);
+ QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
+ ? kQuickAllocArrayWithAccessCheck
+ : kQuickAllocArray;
+ HInstruction* object = new (arena_) HNewArray(length, dex_pc, type_index, entrypoint);
current_block_->AddInstruction(object);
const char* descriptor = dex_file_->StringByTypeIdx(type_index);
@@ -838,15 +962,20 @@
uint32_t dex_pc) {
bool type_known_final;
bool type_known_abstract;
- bool is_referrers_class;
+ // `CanAccessTypeWithoutChecks` will tell whether the method being
+ // built is trying to access its own class, so that the generated
+ // code can optimize for this case. However, the optimization does not
+ // work for inlining, so we use `IsCompilingClass` instead.
+ bool dont_use_is_referrers_class;
bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
- &type_known_final, &type_known_abstract, &is_referrers_class);
+ &type_known_final, &type_known_abstract, &dont_use_is_referrers_class);
if (!can_access) {
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType);
return false;
}
HInstruction* object = LoadLocal(reference, Primitive::kPrimNot);
- HLoadClass* cls = new (arena_) HLoadClass(type_index, is_referrers_class, dex_pc);
+ HLoadClass* cls = new (arena_) HLoadClass(type_index, IsCompilingClass(type_index), dex_pc);
current_block_->AddInstruction(cls);
// The class needs a temporary before being used by the type check.
Temporaries temps(graph_);
@@ -863,10 +992,101 @@
return true;
}
-void HGraphBuilder::PotentiallyAddSuspendCheck(int32_t target_offset, uint32_t dex_pc) {
+bool HGraphBuilder::NeedsAccessCheck(uint32_t type_index) const {
+ return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks(
+ dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index);
+}
+
+void HGraphBuilder::BuildPackedSwitch(const Instruction& instruction, uint32_t dex_pc) {
+ SwitchTable table(instruction, dex_pc, false);
+
+ // Value to test against.
+ HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
+
+ uint16_t num_entries = table.GetNumEntries();
+ // There should be at least one entry here.
+ DCHECK_GT(num_entries, 0U);
+
+ // Chained cmp-and-branch, starting from starting_key.
+ int32_t starting_key = table.GetEntryAt(0);
+
+ for (size_t i = 1; i <= num_entries; i++) {
+ BuildSwitchCaseHelper(instruction, i, i == num_entries, table, value, starting_key + i - 1,
+ table.GetEntryAt(i), dex_pc);
+ }
+}
+
+void HGraphBuilder::BuildSparseSwitch(const Instruction& instruction, uint32_t dex_pc) {
+ SwitchTable table(instruction, dex_pc, true);
+
+ // Value to test against.
+ HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
+
+ uint16_t num_entries = table.GetNumEntries();
+ // There should be at least one entry here.
+ DCHECK_GT(num_entries, 0U);
+
+ for (size_t i = 0; i < num_entries; i++) {
+ BuildSwitchCaseHelper(instruction, i, i == static_cast<size_t>(num_entries) - 1, table, value,
+ table.GetEntryAt(i), table.GetEntryAt(i + num_entries), dex_pc);
+ }
+}
+
+void HGraphBuilder::BuildSwitchCaseHelper(const Instruction& instruction, size_t index,
+ bool is_last_case, const SwitchTable& table,
+ HInstruction* value, int32_t case_value_int,
+ int32_t target_offset, uint32_t dex_pc) {
+ HBasicBlock* case_target = FindBlockStartingAt(dex_pc + target_offset);
+ DCHECK(case_target != nullptr);
+ PotentiallyAddSuspendCheck(case_target, dex_pc);
+
+ // The current case's value.
+ HInstruction* this_case_value = GetIntConstant(case_value_int);
+
+ // Compare value and this_case_value.
+ HEqual* comparison = new (arena_) HEqual(value, this_case_value);
+ current_block_->AddInstruction(comparison);
+ HInstruction* ifinst = new (arena_) HIf(comparison);
+ current_block_->AddInstruction(ifinst);
+
+ // Case hit: use the target offset to determine where to go.
+ current_block_->AddSuccessor(case_target);
+
+ // Case miss: go to the next case (or default fall-through).
+ // When there is a next case, we use the block stored with the table offset representing this
+ // case (that is where we registered them in ComputeBranchTargets).
+ // When there is no next case, we use the following instruction.
+ // TODO: Find a good way to peel the last iteration to avoid conditional, but still have re-use.
+ if (!is_last_case) {
+ HBasicBlock* next_case_target = FindBlockStartingAt(table.GetDexPcForIndex(index));
+ DCHECK(next_case_target != nullptr);
+ current_block_->AddSuccessor(next_case_target);
+
+ // Need to manually add the block, as there is no dex-pc transition for the cases.
+ graph_->AddBlock(next_case_target);
+
+ current_block_ = next_case_target;
+ } else {
+ HBasicBlock* default_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
+ DCHECK(default_target != nullptr);
+ current_block_->AddSuccessor(default_target);
+ current_block_ = nullptr;
+ }
+}
+
+void HGraphBuilder::PotentiallyAddSuspendCheck(HBasicBlock* target, uint32_t dex_pc) {
+ int32_t target_offset = target->GetDexPc() - dex_pc;
if (target_offset <= 0) {
- // Unconditionnally add a suspend check to backward branches. We can remove
- // them after we recognize loops in the graph.
+ // DX generates back edges to the first encountered return. We can save
+ // time of later passes by not adding redundant suspend checks.
+ HInstruction* last_in_target = target->GetLastInstruction();
+ if (last_in_target != nullptr &&
+ (last_in_target->IsReturn() || last_in_target->IsReturnVoid())) {
+ return;
+ }
+
+ // Add a suspend check to backward branches which may potentially loop. We
+ // can remove them after we recognize loops in the graph.
current_block_->AddInstruction(new (arena_) HSuspendCheck(dex_pc));
}
}
@@ -988,9 +1208,9 @@
case Instruction::GOTO_16:
case Instruction::GOTO_32: {
int32_t offset = instruction.GetTargetOffset();
- PotentiallyAddSuspendCheck(offset, dex_pc);
HBasicBlock* target = FindBlockStartingAt(offset + dex_pc);
DCHECK(target != nullptr);
+ PotentiallyAddSuspendCheck(target, dex_pc);
current_block_->AddInstruction(new (arena_) HGoto());
current_block_->AddSuccessor(target);
current_block_ = nullptr;
@@ -1079,52 +1299,77 @@
}
case Instruction::INT_TO_LONG: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimLong);
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::INT_TO_FLOAT: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimFloat);
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimFloat, dex_pc);
break;
}
case Instruction::INT_TO_DOUBLE: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimDouble);
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimDouble, dex_pc);
break;
}
case Instruction::LONG_TO_INT: {
- Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimInt);
+ Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::LONG_TO_FLOAT: {
- Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimFloat);
+ Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimFloat, dex_pc);
break;
}
case Instruction::LONG_TO_DOUBLE: {
- Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimDouble);
+ Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimDouble, dex_pc);
break;
}
case Instruction::FLOAT_TO_INT: {
- Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimInt);
+ Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::FLOAT_TO_LONG: {
+ Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::FLOAT_TO_DOUBLE: {
+ Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::DOUBLE_TO_INT: {
+ Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::DOUBLE_TO_LONG: {
+ Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::DOUBLE_TO_FLOAT: {
+ Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimFloat, dex_pc);
break;
}
case Instruction::INT_TO_BYTE: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimByte);
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimByte, dex_pc);
break;
}
case Instruction::INT_TO_SHORT: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimShort);
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimShort, dex_pc);
break;
}
case Instruction::INT_TO_CHAR: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimChar);
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimChar, dex_pc);
break;
}
@@ -1227,6 +1472,16 @@
break;
}
+ case Instruction::REM_FLOAT: {
+ Binop_23x<HRem>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::REM_DOUBLE: {
+ Binop_23x<HRem>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
case Instruction::AND_INT: {
Binop_23x<HAnd>(instruction, Primitive::kPrimInt);
break;
@@ -1366,6 +1621,16 @@
break;
}
+ case Instruction::REM_FLOAT_2ADDR: {
+ Binop_12x<HRem>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::REM_DOUBLE_2ADDR: {
+ Binop_12x<HRem>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
case Instruction::SHL_INT_2ADDR: {
Binop_12x_shift<HShl>(instruction, Primitive::kPrimInt);
break;
@@ -1526,16 +1791,24 @@
}
case Instruction::NEW_INSTANCE: {
- current_block_->AddInstruction(
- new (arena_) HNewInstance(dex_pc, instruction.VRegB_21c()));
+ uint16_t type_index = instruction.VRegB_21c();
+ QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
+ ? kQuickAllocObjectWithAccessCheck
+ : kQuickAllocObject;
+
+ current_block_->AddInstruction(new (arena_) HNewInstance(dex_pc, type_index, entrypoint));
UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
break;
}
case Instruction::NEW_ARRAY: {
+ uint16_t type_index = instruction.VRegC_22c();
HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt);
+ QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
+ ? kQuickAllocArrayWithAccessCheck
+ : kQuickAllocArray;
current_block_->AddInstruction(
- new (arena_) HNewArray(length, dex_pc, instruction.VRegC_22c()));
+ new (arena_) HNewArray(length, dex_pc, type_index, entrypoint));
UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction());
break;
}
@@ -1695,15 +1968,20 @@
uint16_t type_index = instruction.VRegB_21c();
bool type_known_final;
bool type_known_abstract;
- bool is_referrers_class;
+ bool dont_use_is_referrers_class;
+ // `CanAccessTypeWithoutChecks` will tell whether the method being
+ // built is trying to access its own class, so that the generated
+ // code can optimize for this case. However, the optimization does not
+ // work for inlining, so we use `IsCompilingClass` instead.
bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
- &type_known_final, &type_known_abstract, &is_referrers_class);
+ &type_known_final, &type_known_abstract, &dont_use_is_referrers_class);
if (!can_access) {
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType);
return false;
}
current_block_->AddInstruction(
- new (arena_) HLoadClass(type_index, is_referrers_class, dex_pc));
+ new (arena_) HLoadClass(type_index, IsCompilingClass(type_index), dex_pc));
UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
break;
}
@@ -1760,7 +2038,22 @@
break;
}
+ case Instruction::PACKED_SWITCH: {
+ BuildPackedSwitch(instruction, dex_pc);
+ break;
+ }
+
+ case Instruction::SPARSE_SWITCH: {
+ BuildSparseSwitch(instruction, dex_pc);
+ break;
+ }
+
default:
+ VLOG(compiler) << "Did not compile "
+ << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
+ << " because of unhandled instruction "
+ << instruction.Name();
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledUnhandledInstruction);
return false;
}
return true;
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 8519bcb..c510136 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -21,6 +21,7 @@
#include "dex_file-inl.h"
#include "driver/compiler_driver.h"
#include "driver/dex_compilation_unit.h"
+#include "optimizing_compiler_stats.h"
#include "primitive.h"
#include "utils/arena_object.h"
#include "utils/growable_array.h"
@@ -29,48 +30,55 @@
namespace art {
class Instruction;
+class SwitchTable;
class HGraphBuilder : public ValueObject {
public:
- HGraphBuilder(ArenaAllocator* arena,
+ HGraphBuilder(HGraph* graph,
DexCompilationUnit* dex_compilation_unit,
+ const DexCompilationUnit* const outer_compilation_unit,
const DexFile* dex_file,
- CompilerDriver* driver)
- : arena_(arena),
- branch_targets_(arena, 0),
- locals_(arena, 0),
+ CompilerDriver* driver,
+ OptimizingCompilerStats* compiler_stats)
+ : arena_(graph->GetArena()),
+ branch_targets_(graph->GetArena(), 0),
+ locals_(graph->GetArena(), 0),
entry_block_(nullptr),
exit_block_(nullptr),
current_block_(nullptr),
- graph_(nullptr),
+ graph_(graph),
constant0_(nullptr),
constant1_(nullptr),
dex_file_(dex_file),
dex_compilation_unit_(dex_compilation_unit),
compiler_driver_(driver),
+ outer_compilation_unit_(outer_compilation_unit),
return_type_(Primitive::GetType(dex_compilation_unit_->GetShorty()[0])),
code_start_(nullptr),
- latest_result_(nullptr) {}
+ latest_result_(nullptr),
+ compilation_stats_(compiler_stats) {}
// Only for unit testing.
- HGraphBuilder(ArenaAllocator* arena, Primitive::Type return_type = Primitive::kPrimInt)
- : arena_(arena),
- branch_targets_(arena, 0),
- locals_(arena, 0),
+ HGraphBuilder(HGraph* graph, Primitive::Type return_type = Primitive::kPrimInt)
+ : arena_(graph->GetArena()),
+ branch_targets_(graph->GetArena(), 0),
+ locals_(graph->GetArena(), 0),
entry_block_(nullptr),
exit_block_(nullptr),
current_block_(nullptr),
- graph_(nullptr),
+ graph_(graph),
constant0_(nullptr),
constant1_(nullptr),
dex_file_(nullptr),
dex_compilation_unit_(nullptr),
compiler_driver_(nullptr),
+ outer_compilation_unit_(nullptr),
return_type_(return_type),
code_start_(nullptr),
- latest_result_(nullptr) {}
+ latest_result_(nullptr),
+ compilation_stats_(nullptr) {}
- HGraph* BuildGraph(const DexFile::CodeItem& code);
+ bool BuildGraph(const DexFile::CodeItem& code);
private:
// Analyzes the dex instruction and adds HInstruction to the graph
@@ -98,8 +106,9 @@
HLocal* GetLocalAt(int register_index) const;
void UpdateLocal(int register_index, HInstruction* instruction) const;
HInstruction* LoadLocal(int register_index, Primitive::Type type) const;
- void PotentiallyAddSuspendCheck(int32_t target_offset, uint32_t dex_pc);
+ void PotentiallyAddSuspendCheck(HBasicBlock* target, uint32_t dex_pc);
void InitializeParameters(uint16_t number_of_parameters);
+ bool NeedsAccessCheck(uint32_t type_index) const;
template<typename T>
void Unop_12x(const Instruction& instruction, Primitive::Type type);
@@ -135,7 +144,8 @@
void Conversion_12x(const Instruction& instruction,
Primitive::Type input_type,
- Primitive::Type result_type);
+ Primitive::Type result_type,
+ uint32_t dex_pc);
void BuildCheckedDivRem(uint16_t out_reg,
uint16_t first_reg,
@@ -202,6 +212,31 @@
uint16_t type_index,
uint32_t dex_pc);
+ // Builds an instruction sequence for a packed switch statement.
+ void BuildPackedSwitch(const Instruction& instruction, uint32_t dex_pc);
+
+ // Builds an instruction sequence for a sparse switch statement.
+ void BuildSparseSwitch(const Instruction& instruction, uint32_t dex_pc);
+
+ void BuildSwitchCaseHelper(const Instruction& instruction, size_t index,
+ bool is_last_case, const SwitchTable& table,
+ HInstruction* value, int32_t case_value_int,
+ int32_t target_offset, uint32_t dex_pc);
+
+ bool SkipCompilation(size_t number_of_dex_instructions,
+ size_t number_of_blocks,
+ size_t number_of_branches);
+
+ void MaybeRecordStat(MethodCompilationStat compilation_stat);
+
+ // Returns whether `type_index` points to the outer-most compiling method's class.
+ bool IsCompilingClass(uint16_t type_index) const {
+ uint32_t referrer_index = outer_compilation_unit_->GetDexMethodIndex();
+ const DexFile::MethodId& method_id =
+ outer_compilation_unit_->GetDexFile()->GetMethodId(referrer_index);
+ return method_id.class_idx_ == type_index;
+ }
+
ArenaAllocator* const arena_;
// A list of the size of the dex code holding block information for
@@ -214,14 +249,26 @@
HBasicBlock* entry_block_;
HBasicBlock* exit_block_;
HBasicBlock* current_block_;
- HGraph* graph_;
+ HGraph* const graph_;
HIntConstant* constant0_;
HIntConstant* constant1_;
+ // The dex file where the method being compiled is.
const DexFile* const dex_file_;
+
+ // The compilation unit of the current method being compiled. Note that
+ // it can be an inlined method.
DexCompilationUnit* const dex_compilation_unit_;
+
CompilerDriver* const compiler_driver_;
+
+ // The compilation unit of the outermost method being compiled. That is the
+ // method being compiled (and not inlined), and potentially inlining other
+ // methods.
+ const DexCompilationUnit* const outer_compilation_unit_;
+
+ // The return type of the method being compiled.
const Primitive::Type return_type_;
// The pointer in the dex file where the instructions of the code item
@@ -232,6 +279,8 @@
// used by move-result instructions.
HInstruction* latest_result_;
+ OptimizingCompilerStats* compilation_stats_;
+
DISALLOW_COPY_AND_ASSIGN(HGraphBuilder);
};
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index e581af2..d0739a6 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -41,59 +41,52 @@
}
void CodeGenerator::CompileBaseline(CodeAllocator* allocator, bool is_leaf) {
- const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
- DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock());
- DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1)));
Initialize();
-
- DCHECK_EQ(frame_size_, kUninitializedFrameSize);
if (!is_leaf) {
MarkNotLeaf();
}
- ComputeFrameSize(GetGraph()->GetNumberOfLocalVRegs()
- + GetGraph()->GetTemporariesVRegSlots()
- + 1 /* filler */,
- 0, /* the baseline compiler does not have live registers at slow path */
- GetGraph()->GetMaximumNumberOfOutVRegs()
- + 1 /* current method */);
- GenerateFrameEntry();
+ InitializeCodeGeneration(GetGraph()->GetNumberOfLocalVRegs()
+ + GetGraph()->GetTemporariesVRegSlots()
+ + 1 /* filler */,
+ 0, /* the baseline compiler does not have live registers at slow path */
+ 0, /* the baseline compiler does not have live registers at slow path */
+ GetGraph()->GetMaximumNumberOfOutVRegs()
+ + 1 /* current method */,
+ GetGraph()->GetBlocks());
+ CompileInternal(allocator, /* is_baseline */ true);
+}
- HGraphVisitor* location_builder = GetLocationBuilder();
+void CodeGenerator::CompileInternal(CodeAllocator* allocator, bool is_baseline) {
HGraphVisitor* instruction_visitor = GetInstructionVisitor();
- for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
- HBasicBlock* block = blocks.Get(i);
+ DCHECK_EQ(current_block_index_, 0u);
+ GenerateFrameEntry();
+ for (size_t e = block_order_->Size(); current_block_index_ < e; ++current_block_index_) {
+ HBasicBlock* block = block_order_->Get(current_block_index_);
Bind(block);
for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* current = it.Current();
- current->Accept(location_builder);
- InitLocations(current);
+ if (is_baseline) {
+ InitLocationsBaseline(current);
+ }
current->Accept(instruction_visitor);
}
}
- GenerateSlowPaths();
+
+ // Generate the slow paths.
+ for (size_t i = 0, e = slow_paths_.Size(); i < e; ++i) {
+ slow_paths_.Get(i)->EmitNativeCode(this);
+ }
+
+ // Finalize instructions in assember;
Finalize(allocator);
}
void CodeGenerator::CompileOptimized(CodeAllocator* allocator) {
- // The frame size has already been computed during register allocation.
- DCHECK_NE(frame_size_, kUninitializedFrameSize);
- const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
- DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock());
- DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1)));
+ // The register allocator already called `InitializeCodeGeneration`,
+ // where the frame size has been computed.
+ DCHECK(block_order_ != nullptr);
Initialize();
-
- GenerateFrameEntry();
- HGraphVisitor* instruction_visitor = GetInstructionVisitor();
- for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
- HBasicBlock* block = blocks.Get(i);
- Bind(block);
- for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
- HInstruction* current = it.Current();
- current->Accept(instruction_visitor);
- }
- }
- GenerateSlowPaths();
- Finalize(allocator);
+ CompileInternal(allocator, /* is_baseline */ false);
}
void CodeGenerator::Finalize(CodeAllocator* allocator) {
@@ -104,12 +97,6 @@
GetAssembler()->FinalizeInstructions(code);
}
-void CodeGenerator::GenerateSlowPaths() {
- for (size_t i = 0, e = slow_paths_.Size(); i < e; ++i) {
- slow_paths_.Get(i)->EmitNativeCode(this);
- }
-}
-
size_t CodeGenerator::FindFreeEntry(bool* array, size_t length) {
for (size_t i = 0; i < length; ++i) {
if (!array[i]) {
@@ -135,17 +122,33 @@
return -1;
}
-void CodeGenerator::ComputeFrameSize(size_t number_of_spill_slots,
- size_t maximum_number_of_live_registers,
- size_t number_of_out_slots) {
+void CodeGenerator::InitializeCodeGeneration(size_t number_of_spill_slots,
+ size_t maximum_number_of_live_core_registers,
+ size_t maximum_number_of_live_fp_registers,
+ size_t number_of_out_slots,
+ const GrowableArray<HBasicBlock*>& block_order) {
+ block_order_ = &block_order;
+ DCHECK(block_order_->Get(0) == GetGraph()->GetEntryBlock());
+ DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), block_order_->Get(1)));
+ ComputeSpillMask();
first_register_slot_in_slow_path_ = (number_of_out_slots + number_of_spill_slots) * kVRegSize;
- SetFrameSize(RoundUp(
- number_of_spill_slots * kVRegSize
- + number_of_out_slots * kVRegSize
- + maximum_number_of_live_registers * GetWordSize()
- + FrameEntrySpillSize(),
- kStackAlignment));
+ if (number_of_spill_slots == 0
+ && !HasAllocatedCalleeSaveRegisters()
+ && IsLeafMethod()
+ && !RequiresCurrentMethod()) {
+ DCHECK_EQ(maximum_number_of_live_core_registers, 0u);
+ DCHECK_EQ(maximum_number_of_live_fp_registers, 0u);
+ SetFrameSize(CallPushesPC() ? GetWordSize() : 0);
+ } else {
+ SetFrameSize(RoundUp(
+ number_of_spill_slots * kVRegSize
+ + number_of_out_slots * kVRegSize
+ + maximum_number_of_live_core_registers * GetWordSize()
+ + maximum_number_of_live_fp_registers * GetFloatingPointSpillSlotSize()
+ + FrameEntrySpillSize(),
+ kStackAlignment));
+ }
}
Location CodeGenerator::GetTemporaryLocation(HTemporary* temp) const {
@@ -233,7 +236,8 @@
}
}
- SetupBlockedRegisters();
+ static constexpr bool kBaseline = true;
+ SetupBlockedRegisters(kBaseline);
// Allocate all unallocated input locations.
for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
@@ -290,11 +294,12 @@
result_location = locations->InAt(0);
break;
}
- locations->SetOut(result_location);
+ locations->UpdateOut(result_location);
}
}
-void CodeGenerator::InitLocations(HInstruction* instruction) {
+void CodeGenerator::InitLocationsBaseline(HInstruction* instruction) {
+ AllocateLocations(instruction);
if (instruction->GetLocations() == nullptr) {
if (instruction->IsTemporary()) {
HInstruction* previous = instruction->GetPrevious();
@@ -320,29 +325,46 @@
}
}
-bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const {
- // We currently iterate over the block in insertion order.
- return current->GetBlockId() + 1 == next->GetBlockId();
+void CodeGenerator::AllocateLocations(HInstruction* instruction) {
+ instruction->Accept(GetLocationBuilder());
+ LocationSummary* locations = instruction->GetLocations();
+ if (!instruction->IsSuspendCheckEntry()) {
+ if (locations != nullptr && locations->CanCall()) {
+ MarkNotLeaf();
+ }
+ if (instruction->NeedsCurrentMethod()) {
+ SetRequiresCurrentMethod();
+ }
+ }
}
-CodeGenerator* CodeGenerator::Create(ArenaAllocator* allocator,
- HGraph* graph,
- InstructionSet instruction_set) {
+bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const {
+ DCHECK_EQ(block_order_->Get(current_block_index_), current);
+ return (current_block_index_ < block_order_->Size() - 1)
+ && (block_order_->Get(current_block_index_ + 1) == next);
+}
+
+CodeGenerator* CodeGenerator::Create(HGraph* graph,
+ InstructionSet instruction_set,
+ const InstructionSetFeatures& isa_features,
+ const CompilerOptions& compiler_options) {
switch (instruction_set) {
case kArm:
case kThumb2: {
- return new (allocator) arm::CodeGeneratorARM(graph);
+ return new arm::CodeGeneratorARM(graph,
+ *isa_features.AsArmInstructionSetFeatures(),
+ compiler_options);
}
case kArm64: {
- return new (allocator) arm64::CodeGeneratorARM64(graph);
+ return new arm64::CodeGeneratorARM64(graph, compiler_options);
}
case kMips:
return nullptr;
case kX86: {
- return new (allocator) x86::CodeGeneratorX86(graph);
+ return new x86::CodeGeneratorX86(graph, compiler_options);
}
case kX86_64: {
- return new (allocator) x86_64::CodeGeneratorX86_64(graph);
+ return new x86_64::CodeGeneratorX86_64(graph, compiler_options);
}
default:
return nullptr;
@@ -369,12 +391,12 @@
uint32_t native_offset = pc_info.native_pc;
uint32_t dex_pc = pc_info.dex_pc;
const uint8_t* references = dex_gc_map.FindBitMap(dex_pc, false);
- CHECK(references != NULL) << "Missing ref for dex pc 0x" << std::hex << dex_pc;
+ CHECK(references != nullptr) << "Missing ref for dex pc 0x" << std::hex << dex_pc;
builder.AddEntry(native_offset, references);
}
}
-void CodeGenerator::BuildMappingTable(std::vector<uint8_t>* data, SrcMap* src_map) const {
+void CodeGenerator::BuildMappingTable(std::vector<uint8_t>* data, DefaultSrcMap* src_map) const {
uint32_t pc2dex_data_size = 0u;
uint32_t pc2dex_entries = pc_infos_.Size();
uint32_t pc2dex_offset = 0u;
@@ -499,6 +521,29 @@
}
void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc) {
+ if (instruction != nullptr) {
+ // The code generated for some type conversions may call the
+ // runtime, thus normally requiring a subsequent call to this
+ // method. However, the method verifier does not produce PC
+ // information for certain instructions, which are considered "atomic"
+ // (they cannot join a GC).
+ // Therefore we do not currently record PC information for such
+ // instructions. As this may change later, we added this special
+ // case so that code generators may nevertheless call
+ // CodeGenerator::RecordPcInfo without triggering an error in
+ // CodeGenerator::BuildNativeGCMap ("Missing ref for dex pc 0x")
+ // thereafter.
+ if (instruction->IsTypeConversion()) {
+ return;
+ }
+ if (instruction->IsRem()) {
+ Primitive::Type type = instruction->AsRem()->GetResultType();
+ if ((type == Primitive::kPrimFloat) || (type == Primitive::kPrimDouble)) {
+ return;
+ }
+ }
+ }
+
// Collect PC infos for the mapping table.
struct PcInfo pc_info;
pc_info.dex_pc = dex_pc;
@@ -518,8 +563,18 @@
size_t environment_size = instruction->EnvironmentSize();
- size_t register_mask = 0;
size_t inlining_depth = 0;
+ uint32_t register_mask = locations->GetRegisterMask();
+ if (locations->OnlyCallsOnSlowPath()) {
+ // In case of slow path, we currently set the location of caller-save registers
+ // to register (instead of their stack location when pushed before the slow-path
+ // call). Therefore register_mask contains both callee-save and caller-save
+ // registers that hold objects. We must remove the caller-save from the mask, since
+ // they will be overwritten by the callee.
+ register_mask &= core_callee_save_mask_;
+ }
+ // The register mask must be a subset of callee-save registers.
+ DCHECK_EQ(register_mask & core_callee_save_mask_, register_mask);
stack_map_stream_.AddStackMapEntry(
dex_pc, pc_info.native_pc, register_mask,
locations->GetStackMask(), environment_size, inlining_depth);
@@ -542,10 +597,19 @@
stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, High32Bits(value));
++i;
DCHECK_LT(i, environment_size);
- } else {
- DCHECK(current->IsIntConstant());
+ } else if (current->IsDoubleConstant()) {
+ int64_t value = bit_cast<double, int64_t>(current->AsDoubleConstant()->GetValue());
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, Low32Bits(value));
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, High32Bits(value));
+ ++i;
+ DCHECK_LT(i, environment_size);
+ } else if (current->IsIntConstant()) {
int32_t value = current->AsIntConstant()->GetValue();
stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, value);
+ } else {
+ DCHECK(current->IsFloatConstant());
+ int32_t value = bit_cast<float, int32_t>(current->AsFloatConstant()->GetValue());
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, value);
}
break;
}
@@ -586,30 +650,84 @@
break;
}
+ case Location::kFpuRegisterPair : {
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInFpuRegister, location.low());
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInFpuRegister, location.high());
+ ++i;
+ DCHECK_LT(i, environment_size);
+ break;
+ }
+
+ case Location::kRegisterPair : {
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, location.low());
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, location.high());
+ ++i;
+ DCHECK_LT(i, environment_size);
+ break;
+ }
+
default:
LOG(FATAL) << "Unexpected kind " << location.GetKind();
}
}
}
+bool CodeGenerator::CanMoveNullCheckToUser(HNullCheck* null_check) {
+ HInstruction* first_next_not_move = null_check->GetNextDisregardingMoves();
+ return (first_next_not_move != nullptr) && first_next_not_move->CanDoImplicitNullCheck();
+}
+
+void CodeGenerator::MaybeRecordImplicitNullCheck(HInstruction* instr) {
+ // If we are from a static path don't record the pc as we can't throw NPE.
+ // NB: having the checks here makes the code much less verbose in the arch
+ // specific code generators.
+ if (instr->IsStaticFieldSet() || instr->IsStaticFieldGet()) {
+ return;
+ }
+
+ if (!compiler_options_.GetImplicitNullChecks()) {
+ return;
+ }
+
+ if (!instr->CanDoImplicitNullCheck()) {
+ return;
+ }
+
+ // Find the first previous instruction which is not a move.
+ HInstruction* first_prev_not_move = instr->GetPreviousDisregardingMoves();
+
+ // If the instruction is a null check it means that `instr` is the first user
+ // and needs to record the pc.
+ if (first_prev_not_move != nullptr && first_prev_not_move->IsNullCheck()) {
+ HNullCheck* null_check = first_prev_not_move->AsNullCheck();
+ // TODO: The parallel moves modify the environment. Their changes need to be reverted
+ // otherwise the stack maps at the throw point will not be correct.
+ RecordPcInfo(null_check, null_check->GetDexPc());
+ }
+}
+
void CodeGenerator::SaveLiveRegisters(LocationSummary* locations) {
RegisterSet* register_set = locations->GetLiveRegisters();
size_t stack_offset = first_register_slot_in_slow_path_;
for (size_t i = 0, e = GetNumberOfCoreRegisters(); i < e; ++i) {
- if (register_set->ContainsCoreRegister(i)) {
- // If the register holds an object, update the stack mask.
- if (locations->RegisterContainsObject(i)) {
- locations->SetStackBit(stack_offset / kVRegSize);
+ if (!IsCoreCalleeSaveRegister(i)) {
+ if (register_set->ContainsCoreRegister(i)) {
+ // If the register holds an object, update the stack mask.
+ if (locations->RegisterContainsObject(i)) {
+ locations->SetStackBit(stack_offset / kVRegSize);
+ }
+ DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
+ stack_offset += SaveCoreRegister(stack_offset, i);
}
- DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
- stack_offset += SaveCoreRegister(stack_offset, i);
}
}
for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) {
- if (register_set->ContainsFloatingPointRegister(i)) {
- DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
- stack_offset += SaveFloatingPointRegister(stack_offset, i);
+ if (!IsFloatingPointCalleeSaveRegister(i)) {
+ if (register_set->ContainsFloatingPointRegister(i)) {
+ DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
+ stack_offset += SaveFloatingPointRegister(stack_offset, i);
+ }
}
}
}
@@ -618,16 +736,20 @@
RegisterSet* register_set = locations->GetLiveRegisters();
size_t stack_offset = first_register_slot_in_slow_path_;
for (size_t i = 0, e = GetNumberOfCoreRegisters(); i < e; ++i) {
- if (register_set->ContainsCoreRegister(i)) {
- DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
- stack_offset += RestoreCoreRegister(stack_offset, i);
+ if (!IsCoreCalleeSaveRegister(i)) {
+ if (register_set->ContainsCoreRegister(i)) {
+ DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
+ stack_offset += RestoreCoreRegister(stack_offset, i);
+ }
}
}
for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) {
- if (register_set->ContainsFloatingPointRegister(i)) {
- DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
- stack_offset += RestoreFloatingPointRegister(stack_offset, i);
+ if (!IsFloatingPointCalleeSaveRegister(i)) {
+ if (register_set->ContainsFloatingPointRegister(i)) {
+ DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
+ stack_offset += RestoreFloatingPointRegister(stack_offset, i);
+ }
}
}
}
@@ -652,11 +774,9 @@
}
void CodeGenerator::EmitParallelMoves(Location from1, Location to1, Location from2, Location to2) {
- MoveOperands move1(from1, to1, nullptr);
- MoveOperands move2(from2, to2, nullptr);
HParallelMove parallel_move(GetGraph()->GetArena());
- parallel_move.AddMove(&move1);
- parallel_move.AddMove(&move2);
+ parallel_move.AddMove(from1, to1, nullptr);
+ parallel_move.AddMove(from2, to2, nullptr);
GetMoveResolver()->EmitNativeCode(¶llel_move);
}
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 7c8f6a2..efd0c84 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -18,7 +18,9 @@
#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_H_
#include "arch/instruction_set.h"
+#include "arch/instruction_set_features.h"
#include "base/bit_field.h"
+#include "driver/compiler_options.h"
#include "globals.h"
#include "locations.h"
#include "memory_region.h"
@@ -28,7 +30,6 @@
namespace art {
static size_t constexpr kVRegSize = 4;
-static size_t constexpr kUninitializedFrameSize = 0;
// Binary encoding of 2^32 for type double.
static int64_t constexpr k2Pow32EncodingForDouble = INT64_C(0x41F0000000000000);
@@ -37,12 +38,17 @@
// Maximum value for a primitive integer.
static int32_t constexpr kPrimIntMax = 0x7fffffff;
+// Maximum value for a primitive long.
+static int64_t constexpr kPrimLongMax = 0x7fffffffffffffff;
class Assembler;
class CodeGenerator;
class DexCompilationUnit;
class ParallelMoveResolver;
+class SrcMapElem;
+template <class Alloc>
class SrcMap;
+using DefaultSrcMap = SrcMap<std::allocator<SrcMapElem>>;
class CodeAllocator {
public:
@@ -71,15 +77,17 @@
DISALLOW_COPY_AND_ASSIGN(SlowPathCode);
};
-class CodeGenerator : public ArenaObject<kArenaAllocMisc> {
+class CodeGenerator {
public:
// Compiles the graph to executable instructions. Returns whether the compilation
// succeeded.
void CompileBaseline(CodeAllocator* allocator, bool is_leaf = false);
void CompileOptimized(CodeAllocator* allocator);
- static CodeGenerator* Create(ArenaAllocator* allocator,
- HGraph* graph,
- InstructionSet instruction_set);
+ static CodeGenerator* Create(HGraph* graph,
+ InstructionSet instruction_set,
+ const InstructionSetFeatures& isa_features,
+ const CompilerOptions& compiler_options);
+ virtual ~CodeGenerator() {}
HGraph* GetGraph() const { return graph_; }
@@ -98,29 +106,47 @@
virtual void GenerateFrameExit() = 0;
virtual void Bind(HBasicBlock* block) = 0;
virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) = 0;
- virtual HGraphVisitor* GetLocationBuilder() = 0;
- virtual HGraphVisitor* GetInstructionVisitor() = 0;
virtual Assembler* GetAssembler() = 0;
virtual size_t GetWordSize() const = 0;
+ virtual size_t GetFloatingPointSpillSlotSize() const = 0;
virtual uintptr_t GetAddressOf(HBasicBlock* block) const = 0;
- void ComputeFrameSize(size_t number_of_spill_slots,
- size_t maximum_number_of_live_registers,
- size_t number_of_out_slots);
- virtual size_t FrameEntrySpillSize() const = 0;
+ void InitializeCodeGeneration(size_t number_of_spill_slots,
+ size_t maximum_number_of_live_core_registers,
+ size_t maximum_number_of_live_fp_registers,
+ size_t number_of_out_slots,
+ const GrowableArray<HBasicBlock*>& block_order);
int32_t GetStackSlot(HLocal* local) const;
Location GetTemporaryLocation(HTemporary* temp) const;
uint32_t GetFrameSize() const { return frame_size_; }
void SetFrameSize(uint32_t size) { frame_size_ = size; }
uint32_t GetCoreSpillMask() const { return core_spill_mask_; }
+ uint32_t GetFpuSpillMask() const { return fpu_spill_mask_; }
size_t GetNumberOfCoreRegisters() const { return number_of_core_registers_; }
size_t GetNumberOfFloatingPointRegisters() const { return number_of_fpu_registers_; }
- virtual void SetupBlockedRegisters() const = 0;
+ virtual void SetupBlockedRegisters(bool is_baseline) const = 0;
+
+ virtual void ComputeSpillMask() {
+ core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
+ DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved";
+ fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
+ }
+
+ static uint32_t ComputeRegisterMask(const int* registers, size_t length) {
+ uint32_t mask = 0;
+ for (size_t i = 0, e = length; i < e; ++i) {
+ mask |= (1 << registers[i]);
+ }
+ return mask;
+ }
virtual void DumpCoreRegister(std::ostream& stream, int reg) const = 0;
virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const = 0;
virtual InstructionSet GetInstructionSet() const = 0;
+
+ const CompilerOptions& GetCompilerOptions() const { return compiler_options_; }
+
// Saves the register in the stack. Returns the size taken on stack.
virtual size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) = 0;
// Restores the register from the stack. Returns the size taken on stack.
@@ -135,16 +161,25 @@
UNIMPLEMENTED(FATAL);
UNREACHABLE();
}
+ virtual bool NeedsTwoRegisters(Primitive::Type type) const = 0;
+
+ bool IsCoreCalleeSaveRegister(int reg) const {
+ return (core_callee_save_mask_ & (1 << reg)) != 0;
+ }
+
+ bool IsFloatingPointCalleeSaveRegister(int reg) const {
+ return (fpu_callee_save_mask_ & (1 << reg)) != 0;
+ }
void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc);
+ bool CanMoveNullCheckToUser(HNullCheck* null_check);
+ void MaybeRecordImplicitNullCheck(HInstruction* instruction);
void AddSlowPath(SlowPathCode* slow_path) {
slow_paths_.Add(slow_path);
}
- void GenerateSlowPaths();
-
- void BuildMappingTable(std::vector<uint8_t>* vector, SrcMap* src_map) const;
+ void BuildMappingTable(std::vector<uint8_t>* vector, DefaultSrcMap* src_map) const;
void BuildVMapTable(std::vector<uint8_t>* vector) const;
void BuildNativeGCMap(
std::vector<uint8_t>* vector, const DexCompilationUnit& dex_compilation_unit) const;
@@ -158,6 +193,15 @@
void MarkNotLeaf() {
is_leaf_ = false;
+ requires_current_method_ = true;
+ }
+
+ void SetRequiresCurrentMethod() {
+ requires_current_method_ = true;
+ }
+
+ bool RequiresCurrentMethod() const {
+ return requires_current_method_;
}
// Clears the spill slots taken by loop phis in the `LocationSummary` of the
@@ -186,13 +230,23 @@
return type == Primitive::kPrimNot && !value->IsIntConstant();
}
+ void AddAllocatedRegister(Location location) {
+ allocated_registers_.Add(location);
+ }
+
+ void AllocateLocations(HInstruction* instruction);
+
protected:
CodeGenerator(HGraph* graph,
size_t number_of_core_registers,
size_t number_of_fpu_registers,
- size_t number_of_register_pairs)
- : frame_size_(kUninitializedFrameSize),
+ size_t number_of_register_pairs,
+ uint32_t core_callee_save_mask,
+ uint32_t fpu_callee_save_mask,
+ const CompilerOptions& compiler_options)
+ : frame_size_(0),
core_spill_mask_(0),
+ fpu_spill_mask_(0),
first_register_slot_in_slow_path_(0),
blocked_core_registers_(graph->GetArena()->AllocArray<bool>(number_of_core_registers)),
blocked_fpu_registers_(graph->GetArena()->AllocArray<bool>(number_of_fpu_registers)),
@@ -200,12 +254,17 @@
number_of_core_registers_(number_of_core_registers),
number_of_fpu_registers_(number_of_fpu_registers),
number_of_register_pairs_(number_of_register_pairs),
+ core_callee_save_mask_(core_callee_save_mask),
+ fpu_callee_save_mask_(fpu_callee_save_mask),
graph_(graph),
+ compiler_options_(compiler_options),
pc_infos_(graph->GetArena(), 32),
slow_paths_(graph->GetArena(), 8),
+ block_order_(nullptr),
+ current_block_index_(0),
is_leaf_(true),
+ requires_current_method_(false),
stack_map_stream_(graph->GetArena()) {}
- ~CodeGenerator() {}
// Register allocation logic.
void AllocateRegistersLocally(HInstruction* instruction) const;
@@ -219,12 +278,51 @@
virtual Location GetStackLocation(HLoadLocal* load) const = 0;
virtual ParallelMoveResolver* GetMoveResolver() = 0;
+ virtual HGraphVisitor* GetLocationBuilder() = 0;
+ virtual HGraphVisitor* GetInstructionVisitor() = 0;
+
+ // Returns the location of the first spilled entry for floating point registers,
+ // relative to the stack pointer.
+ uint32_t GetFpuSpillStart() const {
+ return GetFrameSize() - FrameEntrySpillSize();
+ }
+
+ uint32_t GetFpuSpillSize() const {
+ return POPCOUNT(fpu_spill_mask_) * GetFloatingPointSpillSlotSize();
+ }
+
+ uint32_t GetCoreSpillSize() const {
+ return POPCOUNT(core_spill_mask_) * GetWordSize();
+ }
+
+ uint32_t FrameEntrySpillSize() const {
+ return GetFpuSpillSize() + GetCoreSpillSize();
+ }
+
+ bool HasAllocatedCalleeSaveRegisters() const {
+ // We check the core registers against 1 because it always comprises the return PC.
+ return (POPCOUNT(allocated_registers_.GetCoreRegisters() & core_callee_save_mask_) != 1)
+ || (POPCOUNT(allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_) != 0);
+ }
+
+ bool CallPushesPC() const {
+ InstructionSet instruction_set = GetInstructionSet();
+ return instruction_set == kX86 || instruction_set == kX86_64;
+ }
+
+ bool HasEmptyFrame() const {
+ return GetFrameSize() == (CallPushesPC() ? GetWordSize() : 0);
+ }
// Frame size required for this method.
uint32_t frame_size_;
uint32_t core_spill_mask_;
+ uint32_t fpu_spill_mask_;
uint32_t first_register_slot_in_slow_path_;
+ // Registers that were allocated during linear scan.
+ RegisterSet allocated_registers_;
+
// Arrays used when doing register allocation to know which
// registers we can allocate. `SetupBlockedRegisters` updates the
// arrays.
@@ -234,18 +332,33 @@
size_t number_of_core_registers_;
size_t number_of_fpu_registers_;
size_t number_of_register_pairs_;
+ const uint32_t core_callee_save_mask_;
+ const uint32_t fpu_callee_save_mask_;
private:
- void InitLocations(HInstruction* instruction);
+ void InitLocationsBaseline(HInstruction* instruction);
size_t GetStackOffsetOfSavedRegister(size_t index);
+ void CompileInternal(CodeAllocator* allocator, bool is_baseline);
HGraph* const graph_;
+ const CompilerOptions& compiler_options_;
GrowableArray<PcInfo> pc_infos_;
GrowableArray<SlowPathCode*> slow_paths_;
+ // The order to use for code generation.
+ const GrowableArray<HBasicBlock*>* block_order_;
+
+ // The current block index in `block_order_` of the block
+ // we are generating code for.
+ size_t current_block_index_;
+
+ // Whether the method is a leaf method.
bool is_leaf_;
+ // Whether an instruction in the graph accesses the current method.
+ bool requires_current_method_;
+
StackMapStream stack_map_stream_;
DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 448a5a0..e5de2ab 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -16,8 +16,11 @@
#include "code_generator_arm.h"
+#include "arch/arm/instruction_set_features_arm.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "gc/accounting/card_table.h"
+#include "intrinsics.h"
+#include "intrinsics_arm.h"
#include "mirror/array-inl.h"
#include "mirror/art_method.h"
#include "mirror/class.h"
@@ -31,21 +34,26 @@
namespace arm {
-static DRegister FromLowSToD(SRegister reg) {
- DCHECK_EQ(reg % 2, 0);
- return static_cast<DRegister>(reg / 2);
+static bool ExpectedPairLayout(Location location) {
+ // We expected this for both core and fpu register pairs.
+ return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
}
-static constexpr bool kExplicitStackOverflowCheck = false;
-
-static constexpr int kNumberOfPushedRegistersAtEntry = 1 + 2; // LR, R6, R7
static constexpr int kCurrentMethodStackOffset = 0;
static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1, R2, R3 };
static constexpr size_t kRuntimeParameterCoreRegistersLength =
arraysize(kRuntimeParameterCoreRegisters);
-static constexpr SRegister kRuntimeParameterFpuRegisters[] = { };
-static constexpr size_t kRuntimeParameterFpuRegistersLength = 0;
+static constexpr SRegister kRuntimeParameterFpuRegisters[] = { S0, S1, S2, S3 };
+static constexpr size_t kRuntimeParameterFpuRegistersLength =
+ arraysize(kRuntimeParameterFpuRegisters);
+// We unconditionally allocate R5 to ensure we can do long operations
+// with baseline.
+static constexpr Register kCoreSavedRegisterForBaseline = R5;
+static constexpr Register kCoreCalleeSaves[] =
+ { R5, R6, R7, R8, R10, R11, PC };
+static constexpr SRegister kFpuCalleeSaves[] =
+ { S16, S17, S18, S19, S20, S21, S22, S23, S24, S25, S26, S27, S28, S29, S30, S31 };
class InvokeRuntimeCallingConvention : public CallingConvention<Register, SRegister> {
public:
@@ -62,20 +70,6 @@
#define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())->
#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmWordSize, x).Int32Value()
-class SlowPathCodeARM : public SlowPathCode {
- public:
- SlowPathCodeARM() : entry_label_(), exit_label_() {}
-
- Label* GetEntryLabel() { return &entry_label_; }
- Label* GetExitLabel() { return &exit_label_; }
-
- private:
- Label entry_label_;
- Label exit_label_;
-
- DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM);
-};
-
class NullCheckSlowPathARM : public SlowPathCodeARM {
public:
explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {}
@@ -108,20 +102,6 @@
DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM);
};
-class StackOverflowCheckSlowPathARM : public SlowPathCodeARM {
- public:
- StackOverflowCheckSlowPathARM() {}
-
- void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
- __ Bind(GetEntryLabel());
- __ LoadFromOffset(kLoadWord, PC, TR,
- QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowStackOverflow).Int32Value());
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM);
-};
-
class SuspendCheckSlowPathARM : public SlowPathCodeARM {
public:
SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor)
@@ -254,8 +234,8 @@
codegen->SaveLiveRegisters(locations);
InvokeRuntimeCallingConvention calling_convention;
- arm_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(0));
- __ LoadImmediate(calling_convention.GetRegisterAt(1), instruction_->GetStringIndex());
+ arm_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+ __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction_->GetStringIndex());
arm_codegen->InvokeRuntime(
QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc());
arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0));
@@ -371,16 +351,36 @@
return kArmWordSize;
}
-CodeGeneratorARM::CodeGeneratorARM(HGraph* graph)
- : CodeGenerator(graph, kNumberOfCoreRegisters, kNumberOfSRegisters, kNumberOfRegisterPairs),
+size_t CodeGeneratorARM::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
+ __ StoreSToOffset(static_cast<SRegister>(reg_id), SP, stack_index);
+ return kArmWordSize;
+}
+
+size_t CodeGeneratorARM::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
+ __ LoadSFromOffset(static_cast<SRegister>(reg_id), SP, stack_index);
+ return kArmWordSize;
+}
+
+CodeGeneratorARM::CodeGeneratorARM(HGraph* graph,
+ const ArmInstructionSetFeatures& isa_features,
+ const CompilerOptions& compiler_options)
+ : CodeGenerator(graph,
+ kNumberOfCoreRegisters,
+ kNumberOfSRegisters,
+ kNumberOfRegisterPairs,
+ ComputeRegisterMask(reinterpret_cast<const int*>(kCoreCalleeSaves),
+ arraysize(kCoreCalleeSaves)),
+ ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves),
+ arraysize(kFpuCalleeSaves)),
+ compiler_options),
block_labels_(graph->GetArena(), 0),
location_builder_(graph, this),
instruction_visitor_(graph, this),
move_resolver_(graph->GetArena(), this),
- assembler_(true) {}
-
-size_t CodeGeneratorARM::FrameEntrySpillSize() const {
- return kNumberOfPushedRegistersAtEntry * kArmWordSize;
+ assembler_(true),
+ isa_features_(isa_features) {
+ // Save the PC register to mimic Quick.
+ AddAllocatedRegister(Location::RegisterLocation(PC));
}
Location CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type) const {
@@ -434,7 +434,7 @@
return Location();
}
-void CodeGeneratorARM::SetupBlockedRegisters() const {
+void CodeGeneratorARM::SetupBlockedRegisters(bool is_baseline) const {
// Don't allocate the dalvik style register pair passing.
blocked_register_pairs_[R1_R2] = true;
@@ -449,31 +449,17 @@
// Reserve temp register.
blocked_core_registers_[IP] = true;
- // TODO: We currently don't use Quick's callee saved registers.
- // We always save and restore R6 and R7 to make sure we can use three
- // register pairs for long operations.
- blocked_core_registers_[R4] = true;
- blocked_core_registers_[R5] = true;
- blocked_core_registers_[R8] = true;
- blocked_core_registers_[R10] = true;
- blocked_core_registers_[R11] = true;
+ if (is_baseline) {
+ for (size_t i = 0; i < arraysize(kCoreCalleeSaves); ++i) {
+ blocked_core_registers_[kCoreCalleeSaves[i]] = true;
+ }
- blocked_fpu_registers_[S16] = true;
- blocked_fpu_registers_[S17] = true;
- blocked_fpu_registers_[S18] = true;
- blocked_fpu_registers_[S19] = true;
- blocked_fpu_registers_[S20] = true;
- blocked_fpu_registers_[S21] = true;
- blocked_fpu_registers_[S22] = true;
- blocked_fpu_registers_[S23] = true;
- blocked_fpu_registers_[S24] = true;
- blocked_fpu_registers_[S25] = true;
- blocked_fpu_registers_[S26] = true;
- blocked_fpu_registers_[S27] = true;
- blocked_fpu_registers_[S28] = true;
- blocked_fpu_registers_[S29] = true;
- blocked_fpu_registers_[S30] = true;
- blocked_fpu_registers_[S31] = true;
+ blocked_core_registers_[kCoreSavedRegisterForBaseline] = false;
+
+ for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
+ blocked_fpu_registers_[kFpuCalleeSaves[i]] = true;
+ }
+ }
UpdateBlockedPairRegisters();
}
@@ -494,35 +480,70 @@
assembler_(codegen->GetAssembler()),
codegen_(codegen) {}
+static uint32_t LeastSignificantBit(uint32_t mask) {
+ // ffs starts at 1.
+ return ffs(mask) - 1;
+}
+
+void CodeGeneratorARM::ComputeSpillMask() {
+ core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
+ // Save one extra register for baseline. Note that on thumb2, there is no easy
+ // instruction to restore just the PC, so this actually helps both baseline
+ // and non-baseline to save and restore at least two registers at entry and exit.
+ core_spill_mask_ |= (1 << kCoreSavedRegisterForBaseline);
+ DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved";
+ fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
+ // We use vpush and vpop for saving and restoring floating point registers, which take
+ // a SRegister and the number of registers to save/restore after that SRegister. We
+ // therefore update the `fpu_spill_mask_` to also contain those registers not allocated,
+ // but in the range.
+ if (fpu_spill_mask_ != 0) {
+ uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_);
+ uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_);
+ for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) {
+ fpu_spill_mask_ |= (1 << i);
+ }
+ }
+}
+
void CodeGeneratorARM::GenerateFrameEntry() {
bool skip_overflow_check =
IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
- if (!skip_overflow_check) {
- if (kExplicitStackOverflowCheck) {
- SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM();
- AddSlowPath(slow_path);
+ DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
+ __ Bind(&frame_entry_label_);
- __ LoadFromOffset(kLoadWord, IP, TR, Thread::StackEndOffset<kArmWordSize>().Int32Value());
- __ cmp(SP, ShifterOperand(IP));
- __ b(slow_path->GetEntryLabel(), CC);
- } else {
- __ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
- __ LoadFromOffset(kLoadWord, IP, IP, 0);
- RecordPcInfo(nullptr, 0);
- }
+ if (HasEmptyFrame()) {
+ return;
}
- core_spill_mask_ |= (1 << LR | 1 << R6 | 1 << R7);
- __ PushList(1 << LR | 1 << R6 | 1 << R7);
+ if (!skip_overflow_check) {
+ __ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
+ __ LoadFromOffset(kLoadWord, IP, IP, 0);
+ RecordPcInfo(nullptr, 0);
+ }
- // The return PC has already been pushed on the stack.
- __ AddConstant(SP, -(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize));
+ // PC is in the list of callee-save to mimic Quick, but we need to push
+ // LR at entry instead.
+ __ PushList((core_spill_mask_ & (~(1 << PC))) | 1 << LR);
+ if (fpu_spill_mask_ != 0) {
+ SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_));
+ __ vpushs(start_register, POPCOUNT(fpu_spill_mask_));
+ }
+ __ AddConstant(SP, -(GetFrameSize() - FrameEntrySpillSize()));
__ StoreToOffset(kStoreWord, R0, SP, 0);
}
void CodeGeneratorARM::GenerateFrameExit() {
- __ AddConstant(SP, GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize);
- __ PopList(1 << PC | 1 << R6 | 1 << R7);
+ if (HasEmptyFrame()) {
+ __ bx(LR);
+ return;
+ }
+ __ AddConstant(SP, GetFrameSize() - FrameEntrySpillSize());
+ if (fpu_spill_mask_ != 0) {
+ SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_));
+ __ vpops(start_register, POPCOUNT(fpu_spill_mask_));
+ }
+ __ PopList(core_spill_mask_);
}
void CodeGeneratorARM::Bind(HBasicBlock* block) {
@@ -576,11 +597,17 @@
gp_index_ += 2;
stack_index_ += 2;
if (index + 1 < calling_convention.GetNumberOfRegisters()) {
- ArmManagedRegister pair = ArmManagedRegister::FromRegisterPair(
- calling_convention.GetRegisterPairAt(index));
- return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh());
- } else if (index + 1 == calling_convention.GetNumberOfRegisters()) {
- return Location::QuickParameter(index, stack_index);
+ if (calling_convention.GetRegisterAt(index) == R1) {
+ // Skip R1, and use R2_R3 instead.
+ gp_index_++;
+ index++;
+ }
+ }
+ if (index + 1 < calling_convention.GetNumberOfRegisters()) {
+ DCHECK_EQ(calling_convention.GetRegisterAt(index) + 1,
+ calling_convention.GetRegisterAt(index + 1));
+ return Location::RegisterPairLocation(calling_convention.GetRegisterAt(index),
+ calling_convention.GetRegisterAt(index + 1));
} else {
return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
}
@@ -605,9 +632,11 @@
if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) {
uint32_t index = double_index_;
double_index_ += 2;
- return Location::FpuRegisterPairLocation(
+ Location result = Location::FpuRegisterPairLocation(
calling_convention.GetFpuRegisterAt(index),
calling_convention.GetFpuRegisterAt(index + 1));
+ DCHECK(ExpectedPairLayout(result));
+ return result;
} else {
return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
}
@@ -697,27 +726,11 @@
Location::RegisterLocation(destination.AsRegisterPairLow<Register>()));
} else if (source.IsFpuRegister()) {
UNIMPLEMENTED(FATAL);
- } else if (source.IsQuickParameter()) {
- uint16_t register_index = source.GetQuickParameterRegisterIndex();
- uint16_t stack_index = source.GetQuickParameterStackIndex();
- InvokeDexCallingConvention calling_convention;
- EmitParallelMoves(
- Location::RegisterLocation(calling_convention.GetRegisterAt(register_index)),
- Location::RegisterLocation(destination.AsRegisterPairLow<Register>()),
- Location::StackSlot(
- calling_convention.GetStackOffsetOf(stack_index + 1) + GetFrameSize()),
- Location::RegisterLocation(destination.AsRegisterPairHigh<Register>()));
} else {
- // No conflict possible, so just do the moves.
DCHECK(source.IsDoubleStackSlot());
- if (destination.AsRegisterPairLow<Register>() == R1) {
- DCHECK_EQ(destination.AsRegisterPairHigh<Register>(), R2);
- __ LoadFromOffset(kLoadWord, R1, SP, source.GetStackIndex());
- __ LoadFromOffset(kLoadWord, R2, SP, source.GetHighStackIndex(kArmWordSize));
- } else {
- __ LoadFromOffset(kLoadWordPair, destination.AsRegisterPairLow<Register>(),
- SP, source.GetStackIndex());
- }
+ DCHECK(ExpectedPairLayout(destination));
+ __ LoadFromOffset(kLoadWordPair, destination.AsRegisterPairLow<Register>(),
+ SP, source.GetStackIndex());
}
} else if (destination.IsFpuRegisterPair()) {
if (source.IsDoubleStackSlot()) {
@@ -727,22 +740,6 @@
} else {
UNIMPLEMENTED(FATAL);
}
- } else if (destination.IsQuickParameter()) {
- InvokeDexCallingConvention calling_convention;
- uint16_t register_index = destination.GetQuickParameterRegisterIndex();
- uint16_t stack_index = destination.GetQuickParameterStackIndex();
- if (source.IsRegisterPair()) {
- UNIMPLEMENTED(FATAL);
- } else if (source.IsFpuRegister()) {
- UNIMPLEMENTED(FATAL);
- } else {
- DCHECK(source.IsDoubleStackSlot());
- EmitParallelMoves(
- Location::StackSlot(source.GetStackIndex()),
- Location::RegisterLocation(calling_convention.GetRegisterAt(register_index)),
- Location::StackSlot(source.GetHighStackIndex(kArmWordSize)),
- Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index + 1)));
- }
} else {
DCHECK(destination.IsDoubleStackSlot());
if (source.IsRegisterPair()) {
@@ -755,17 +752,6 @@
__ StoreToOffset(kStoreWordPair, source.AsRegisterPairLow<Register>(),
SP, destination.GetStackIndex());
}
- } else if (source.IsQuickParameter()) {
- InvokeDexCallingConvention calling_convention;
- uint16_t register_index = source.GetQuickParameterRegisterIndex();
- uint16_t stack_index = source.GetQuickParameterStackIndex();
- // Just move the low part. The only time a source is a quick parameter is
- // when moving the parameter to its stack locations. And the (Java) caller
- // of this method has already done that.
- __ StoreToOffset(kStoreWord, calling_convention.GetRegisterAt(register_index),
- SP, destination.GetStackIndex());
- DCHECK_EQ(calling_convention.GetStackOffsetOf(stack_index + 1) + GetFrameSize(),
- static_cast<size_t>(destination.GetHighStackIndex(kArmWordSize)));
} else if (source.IsFpuRegisterPair()) {
__ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()),
SP,
@@ -798,7 +784,8 @@
__ LoadImmediate(IP, value);
__ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex());
}
- } else if (const_to_move->IsLongConstant()) {
+ } else {
+ DCHECK(const_to_move->IsLongConstant()) << const_to_move->DebugName();
int64_t value = const_to_move->AsLongConstant()->GetValue();
if (location.IsRegisterPair()) {
__ LoadImmediate(location.AsRegisterPairLow<Register>(), Low32Bits(value));
@@ -874,6 +861,7 @@
|| instruction->IsBoundsCheck()
|| instruction->IsNullCheck()
|| instruction->IsDivZeroCheck()
+ || instruction->GetLocations()->CanCall()
|| !IsLeafMethod());
}
@@ -949,6 +937,7 @@
// Condition has not been materialized, use its inputs as the
// comparison and its condition as the branch condition.
LocationSummary* locations = cond->GetLocations();
+ DCHECK(locations->InAt(0).IsRegister()) << locations->InAt(0);
Register left = locations->InAt(0).AsRegister<Register>();
if (locations->InAt(1).IsRegister()) {
__ cmp(left, ShifterOperand(locations->InAt(1).AsRegister<Register>()));
@@ -1169,41 +1158,38 @@
codegen_->GenerateFrameExit();
}
-void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) {
+void LocationsBuilderARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+ IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(),
+ codegen_->GetInstructionSetFeatures());
+ if (intrinsic.TryDispatch(invoke)) {
+ return;
+ }
+
HandleInvoke(invoke);
}
void CodeGeneratorARM::LoadCurrentMethod(Register reg) {
+ DCHECK(RequiresCurrentMethod());
__ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset);
}
-void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) {
+static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM* codegen) {
+ if (invoke->GetLocations()->Intrinsified()) {
+ IntrinsicCodeGeneratorARM intrinsic(codegen);
+ intrinsic.Dispatch(invoke);
+ return true;
+ }
+ return false;
+}
+
+void InstructionCodeGeneratorARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+ if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+ return;
+ }
+
Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
- // TODO: Implement all kinds of calls:
- // 1) boot -> boot
- // 2) app -> boot
- // 3) app -> app
- //
- // Currently we implement the app -> app logic, which looks up in the resolve cache.
-
- // temp = method;
- codegen_->LoadCurrentMethod(temp);
- // temp = temp->dex_cache_resolved_methods_;
- __ LoadFromOffset(
- kLoadWord, temp, temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
- // temp = temp[index_in_cache]
- __ LoadFromOffset(
- kLoadWord, temp, temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache()));
- // LR = temp[offset_of_quick_compiled_code]
- __ LoadFromOffset(kLoadWord, LR, temp,
- mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
- kArmWordSize).Int32Value());
- // LR()
- __ blx(LR);
-
- codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
- DCHECK(!codegen_->IsLeafMethod());
+ codegen_->GenerateStaticOrDirectCall(invoke, temp);
}
void LocationsBuilderARM::HandleInvoke(HInvoke* invoke) {
@@ -1221,10 +1207,20 @@
}
void LocationsBuilderARM::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+ IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(),
+ codegen_->GetInstructionSetFeatures());
+ if (intrinsic.TryDispatch(invoke)) {
+ return;
+ }
+
HandleInvoke(invoke);
}
void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+ if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+ return;
+ }
+
Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
uint32_t method_offset = mirror::Class::EmbeddedVTableOffset().Uint32Value() +
invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry);
@@ -1238,6 +1234,7 @@
} else {
__ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
}
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
// temp = temp->GetMethodAt(method_offset);
uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
kArmWordSize).Int32Value();
@@ -1276,6 +1273,7 @@
} else {
__ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
}
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
// temp = temp->GetImtEntryAt(method_offset);
uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
kArmWordSize).Int32Value();
@@ -1292,11 +1290,14 @@
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
switch (neg->GetResultType()) {
- case Primitive::kPrimInt:
- case Primitive::kPrimLong: {
- bool output_overlaps = (neg->GetResultType() == Primitive::kPrimLong);
+ case Primitive::kPrimInt: {
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), output_overlaps);
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ break;
+ }
+ case Primitive::kPrimLong: {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
break;
}
@@ -1359,11 +1360,20 @@
}
void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(conversion, LocationSummary::kNoCall);
Primitive::Type result_type = conversion->GetResultType();
Primitive::Type input_type = conversion->GetInputType();
DCHECK_NE(result_type, input_type);
+
+ // The float-to-long and double-to-long type conversions rely on a
+ // call to the runtime.
+ LocationSummary::CallKind call_kind =
+ ((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble)
+ && result_type == Primitive::kPrimLong)
+ ? LocationSummary::kCall
+ : LocationSummary::kNoCall;
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
+
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
@@ -1413,8 +1423,10 @@
break;
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ // Processing a Dex `double-to-int' instruction.
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresFpuRegister());
break;
default:
@@ -1434,11 +1446,24 @@
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
break;
- case Primitive::kPrimFloat:
- case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type << " to "
- << result_type << " not yet implemented";
+ case Primitive::kPrimFloat: {
+ // Processing a Dex `float-to-long' instruction.
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, Location::FpuRegisterLocation(
+ calling_convention.GetFpuRegisterAt(0)));
+ locations->SetOut(Location::RegisterPairLocation(R0, R1));
break;
+ }
+
+ case Primitive::kPrimDouble: {
+ // Processing a Dex `double-to-long' instruction.
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, Location::FpuRegisterPairLocation(
+ calling_convention.GetFpuRegisterAt(0),
+ calling_convention.GetFpuRegisterAt(1)));
+ locations->SetOut(Location::RegisterPairLocation(R0, R1));
+ break;
+ }
default:
LOG(FATAL) << "Unexpected type conversion from " << input_type
@@ -1484,8 +1509,9 @@
break;
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ // Processing a Dex `double-to-float' instruction.
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
break;
default:
@@ -1515,8 +1541,9 @@
break;
case Primitive::kPrimFloat:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ // Processing a Dex `float-to-double' instruction.
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
break;
default:
@@ -1595,10 +1622,15 @@
break;
}
- case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ case Primitive::kPrimDouble: {
+ // Processing a Dex `double-to-int' instruction.
+ SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>();
+ DRegister temp_d = FromLowSToD(temp_s);
+ __ vmovd(temp_d, FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
+ __ vcvtid(temp_s, temp_d);
+ __ vmovrs(out.AsRegister<Register>(), temp_s);
break;
+ }
default:
LOG(FATAL) << "Unexpected type conversion from " << input_type
@@ -1623,9 +1655,17 @@
break;
case Primitive::kPrimFloat:
+ // Processing a Dex `float-to-long' instruction.
+ codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pF2l),
+ conversion,
+ conversion->GetDexPc());
+ break;
+
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type << " to "
- << result_type << " not yet implemented";
+ // Processing a Dex `double-to-long' instruction.
+ codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pD2l),
+ conversion,
+ conversion->GetDexPc());
break;
default:
@@ -1704,8 +1744,9 @@
}
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ // Processing a Dex `double-to-float' instruction.
+ __ vcvtsd(out.AsFpuRegister<SRegister>(),
+ FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
break;
default:
@@ -1760,8 +1801,9 @@
}
case Primitive::kPrimFloat:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ // Processing a Dex `float-to-double' instruction.
+ __ vcvtds(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
+ in.AsFpuRegister<SRegister>());
break;
default:
@@ -1780,12 +1822,17 @@
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
switch (add->GetResultType()) {
- case Primitive::kPrimInt:
- case Primitive::kPrimLong: {
- bool output_overlaps = (add->GetResultType() == Primitive::kPrimLong);
+ case Primitive::kPrimInt: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
- locations->SetOut(Location::RequiresRegister(), output_overlaps);
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
break;
}
@@ -1820,7 +1867,8 @@
}
break;
- case Primitive::kPrimLong:
+ case Primitive::kPrimLong: {
+ DCHECK(second.IsRegisterPair());
__ adds(out.AsRegisterPairLow<Register>(),
first.AsRegisterPairLow<Register>(),
ShifterOperand(second.AsRegisterPairLow<Register>()));
@@ -1828,6 +1876,7 @@
first.AsRegisterPairHigh<Register>(),
ShifterOperand(second.AsRegisterPairHigh<Register>()));
break;
+ }
case Primitive::kPrimFloat:
__ vadds(out.AsFpuRegister<SRegister>(),
@@ -1850,12 +1899,17 @@
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
switch (sub->GetResultType()) {
- case Primitive::kPrimInt:
- case Primitive::kPrimLong: {
- bool output_overlaps = (sub->GetResultType() == Primitive::kPrimLong);
+ case Primitive::kPrimInt: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
- locations->SetOut(Location::RequiresRegister(), output_overlaps);
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
break;
}
case Primitive::kPrimFloat:
@@ -1890,6 +1944,7 @@
}
case Primitive::kPrimLong: {
+ DCHECK(second.IsRegisterPair());
__ subs(out.AsRegisterPairLow<Register>(),
first.AsRegisterPairLow<Register>(),
ShifterOperand(second.AsRegisterPairLow<Register>()));
@@ -2025,8 +2080,7 @@
calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
locations->SetInAt(1, Location::RegisterPairLocation(
calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
- // The runtime helper puts the output in R0,R2.
- locations->SetOut(Location::RegisterPairLocation(R0, R2));
+ locations->SetOut(Location::RegisterPairLocation(R0, R1));
break;
}
case Primitive::kPrimFloat:
@@ -2063,7 +2117,7 @@
DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegisterPairLow<Register>());
DCHECK_EQ(calling_convention.GetRegisterAt(3), second.AsRegisterPairHigh<Register>());
DCHECK_EQ(R0, out.AsRegisterPairLow<Register>());
- DCHECK_EQ(R2, out.AsRegisterPairHigh<Register>());
+ DCHECK_EQ(R1, out.AsRegisterPairHigh<Register>());
codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLdiv), div, div->GetDexPc());
break;
@@ -2089,12 +2143,13 @@
}
void LocationsBuilderARM::VisitRem(HRem* rem) {
- LocationSummary::CallKind call_kind = rem->GetResultType() == Primitive::kPrimLong
- ? LocationSummary::kCall
- : LocationSummary::kNoCall;
+ Primitive::Type type = rem->GetResultType();
+ LocationSummary::CallKind call_kind = type == Primitive::kPrimInt
+ ? LocationSummary::kNoCall
+ : LocationSummary::kCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind);
- switch (rem->GetResultType()) {
+ switch (type) {
case Primitive::kPrimInt: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
@@ -2112,14 +2167,26 @@
locations->SetOut(Location::RegisterPairLocation(R2, R3));
break;
}
- case Primitive::kPrimFloat:
+ case Primitive::kPrimFloat: {
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
+ locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
+ locations->SetOut(Location::FpuRegisterLocation(S0));
+ break;
+ }
+
case Primitive::kPrimDouble: {
- LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType();
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, Location::FpuRegisterPairLocation(
+ calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1)));
+ locations->SetInAt(1, Location::FpuRegisterPairLocation(
+ calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3)));
+ locations->SetOut(Location::Location::FpuRegisterPairLocation(S0, S1));
break;
}
default:
- LOG(FATAL) << "Unexpected rem type " << rem->GetResultType();
+ LOG(FATAL) << "Unexpected rem type " << type;
}
}
@@ -2129,7 +2196,8 @@
Location first = locations->InAt(0);
Location second = locations->InAt(1);
- switch (rem->GetResultType()) {
+ Primitive::Type type = rem->GetResultType();
+ switch (type) {
case Primitive::kPrimInt: {
Register reg1 = first.AsRegister<Register>();
Register reg2 = second.AsRegister<Register>();
@@ -2145,26 +2213,22 @@
}
case Primitive::kPrimLong: {
- InvokeRuntimeCallingConvention calling_convention;
- DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegisterPairLow<Register>());
- DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>());
- DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegisterPairLow<Register>());
- DCHECK_EQ(calling_convention.GetRegisterAt(3), second.AsRegisterPairHigh<Register>());
- DCHECK_EQ(R2, out.AsRegisterPairLow<Register>());
- DCHECK_EQ(R3, out.AsRegisterPairHigh<Register>());
-
codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLmod), rem, rem->GetDexPc());
break;
}
- case Primitive::kPrimFloat:
+ case Primitive::kPrimFloat: {
+ codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pFmodf), rem, rem->GetDexPc());
+ break;
+ }
+
case Primitive::kPrimDouble: {
- LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType();
+ codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pFmod), rem, rem->GetDexPc());
break;
}
default:
- LOG(FATAL) << "Unexpected rem type " << rem->GetResultType();
+ LOG(FATAL) << "Unexpected rem type " << type;
}
}
@@ -2228,7 +2292,7 @@
case Primitive::kPrimInt: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(op->InputAt(1)));
- locations->SetOut(Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
break;
}
case Primitive::kPrimLong: {
@@ -2236,8 +2300,8 @@
locations->SetInAt(0, Location::RegisterPairLocation(
calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
- // The runtime helper puts the output in R0,R2.
- locations->SetOut(Location::RegisterPairLocation(R0, R2));
+ // The runtime helper puts the output in R0,R1.
+ locations->SetOut(Location::RegisterPairLocation(R0, R1));
break;
}
default:
@@ -2291,7 +2355,7 @@
DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>());
DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegister<Register>());
DCHECK_EQ(R0, out.AsRegisterPairLow<Register>());
- DCHECK_EQ(R2, out.AsRegisterPairHigh<Register>());
+ DCHECK_EQ(R1, out.AsRegisterPairHigh<Register>());
int32_t entry_point_offset;
if (op->IsShl()) {
@@ -2347,8 +2411,9 @@
InvokeRuntimeCallingConvention calling_convention;
codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
__ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
- codegen_->InvokeRuntime(
- QUICK_ENTRY_POINT(pAllocObjectWithAccessCheck), instruction, instruction->GetDexPc());
+ codegen_->InvokeRuntime(GetThreadOffset<kArmWordSize>(instruction->GetEntrypoint()).Int32Value(),
+ instruction,
+ instruction->GetDexPc());
}
void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) {
@@ -2356,17 +2421,18 @@
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
- locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+ locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
locations->SetOut(Location::RegisterLocation(R0));
- locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+ locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
}
void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) {
InvokeRuntimeCallingConvention calling_convention;
- codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+ codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(2));
__ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
- codegen_->InvokeRuntime(
- QUICK_ENTRY_POINT(pAllocArrayWithAccessCheck), instruction, instruction->GetDexPc());
+ codegen_->InvokeRuntime(GetThreadOffset<kArmWordSize>(instruction->GetEntrypoint()).Int32Value(),
+ instruction,
+ instruction->GetDexPc());
}
void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
@@ -2398,10 +2464,6 @@
Location out = locations->Out();
Location in = locations->InAt(0);
switch (not_->InputAt(0)->GetType()) {
- case Primitive::kPrimBoolean:
- __ eor(out.AsRegister<Register>(), in.AsRegister<Register>(), ShifterOperand(1));
- break;
-
case Primitive::kPrimInt:
__ mvn(out.AsRegister<Register>(), ShifterOperand(in.AsRegister<Register>()));
break;
@@ -2425,7 +2487,8 @@
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ // Output overlaps because it is written before doing the low comparison.
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
break;
}
case Primitive::kPrimFloat:
@@ -2503,68 +2566,172 @@
LOG(FATAL) << "Unreachable";
}
-void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+void InstructionCodeGeneratorARM::GenerateMemoryBarrier(MemBarrierKind kind) {
+ // TODO (ported from quick): revisit Arm barrier kinds
+ DmbOptions flavour = DmbOptions::ISH; // quiet c++ warnings
+ switch (kind) {
+ case MemBarrierKind::kAnyStore:
+ case MemBarrierKind::kLoadAny:
+ case MemBarrierKind::kAnyAny: {
+ flavour = DmbOptions::ISH;
+ break;
+ }
+ case MemBarrierKind::kStoreStore: {
+ flavour = DmbOptions::ISHST;
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected memory barrier " << kind;
+ }
+ __ dmb(flavour);
+}
+
+void InstructionCodeGeneratorARM::GenerateWideAtomicLoad(Register addr,
+ uint32_t offset,
+ Register out_lo,
+ Register out_hi) {
+ if (offset != 0) {
+ __ LoadImmediate(out_lo, offset);
+ __ add(IP, addr, ShifterOperand(out_lo));
+ addr = IP;
+ }
+ __ ldrexd(out_lo, out_hi, addr);
+}
+
+void InstructionCodeGeneratorARM::GenerateWideAtomicStore(Register addr,
+ uint32_t offset,
+ Register value_lo,
+ Register value_hi,
+ Register temp1,
+ Register temp2,
+ HInstruction* instruction) {
+ Label fail;
+ if (offset != 0) {
+ __ LoadImmediate(temp1, offset);
+ __ add(IP, addr, ShifterOperand(temp1));
+ addr = IP;
+ }
+ __ Bind(&fail);
+ // We need a load followed by store. (The address used in a STREX instruction must
+ // be the same as the address in the most recently executed LDREX instruction.)
+ __ ldrexd(temp1, temp2, addr);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ __ strexd(temp1, value_lo, value_hi, addr);
+ __ cmp(temp1, ShifterOperand(0));
+ __ b(&fail, NE);
+}
+
+void LocationsBuilderARM::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info) {
+ DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
+
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- bool needs_write_barrier =
- CodeGenerator::StoreNeedsWriteBarrier(instruction->GetFieldType(), instruction->GetValue());
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
+
+
+ Primitive::Type field_type = field_info.GetFieldType();
+ bool is_wide = field_type == Primitive::kPrimLong || field_type == Primitive::kPrimDouble;
+ bool generate_volatile = field_info.IsVolatile()
+ && is_wide
+ && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
// Temporary registers for the write barrier.
- if (needs_write_barrier) {
+ // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
+ } else if (generate_volatile) {
+ // Arm encoding have some additional constraints for ldrexd/strexd:
+ // - registers need to be consecutive
+ // - the first register should be even but not R14.
+ // We don't test for Arm yet, and the assertion makes sure that we revisit this if we ever
+ // enable Arm encoding.
+ DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
+
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ if (field_type == Primitive::kPrimDouble) {
+ // For doubles we need two more registers to copy the value.
+ locations->AddTemp(Location::RegisterLocation(R2));
+ locations->AddTemp(Location::RegisterLocation(R3));
+ }
}
}
-void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+void InstructionCodeGeneratorARM::HandleFieldSet(HInstruction* instruction,
+ const FieldInfo& field_info) {
+ DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
+
LocationSummary* locations = instruction->GetLocations();
- Register obj = locations->InAt(0).AsRegister<Register>();
- uint32_t offset = instruction->GetFieldOffset().Uint32Value();
- Primitive::Type field_type = instruction->GetFieldType();
+ Register base = locations->InAt(0).AsRegister<Register>();
+ Location value = locations->InAt(1);
+
+ bool is_volatile = field_info.IsVolatile();
+ bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
+ Primitive::Type field_type = field_info.GetFieldType();
+ uint32_t offset = field_info.GetFieldOffset().Uint32Value();
+
+ if (is_volatile) {
+ GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
+ }
switch (field_type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte: {
- Register value = locations->InAt(1).AsRegister<Register>();
- __ StoreToOffset(kStoreByte, value, obj, offset);
+ __ StoreToOffset(kStoreByte, value.AsRegister<Register>(), base, offset);
break;
}
case Primitive::kPrimShort:
case Primitive::kPrimChar: {
- Register value = locations->InAt(1).AsRegister<Register>();
- __ StoreToOffset(kStoreHalfword, value, obj, offset);
+ __ StoreToOffset(kStoreHalfword, value.AsRegister<Register>(), base, offset);
break;
}
case Primitive::kPrimInt:
case Primitive::kPrimNot: {
- Register value = locations->InAt(1).AsRegister<Register>();
- __ StoreToOffset(kStoreWord, value, obj, offset);
- if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue())) {
- Register temp = locations->GetTemp(0).AsRegister<Register>();
- Register card = locations->GetTemp(1).AsRegister<Register>();
- codegen_->MarkGCCard(temp, card, obj, value);
- }
+ __ StoreToOffset(kStoreWord, value.AsRegister<Register>(), base, offset);
break;
}
case Primitive::kPrimLong: {
- Location value = locations->InAt(1);
- __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), obj, offset);
+ if (is_volatile && !atomic_ldrd_strd) {
+ GenerateWideAtomicStore(base, offset,
+ value.AsRegisterPairLow<Register>(),
+ value.AsRegisterPairHigh<Register>(),
+ locations->GetTemp(0).AsRegister<Register>(),
+ locations->GetTemp(1).AsRegister<Register>(),
+ instruction);
+ } else {
+ __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), base, offset);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
break;
}
case Primitive::kPrimFloat: {
- SRegister value = locations->InAt(1).AsFpuRegister<SRegister>();
- __ StoreSToOffset(value, obj, offset);
+ __ StoreSToOffset(value.AsFpuRegister<SRegister>(), base, offset);
break;
}
case Primitive::kPrimDouble: {
- DRegister value = FromLowSToD(locations->InAt(1).AsFpuRegisterPairLow<SRegister>());
- __ StoreDToOffset(value, obj, offset);
+ DRegister value_reg = FromLowSToD(value.AsFpuRegisterPairLow<SRegister>());
+ if (is_volatile && !atomic_ldrd_strd) {
+ Register value_reg_lo = locations->GetTemp(0).AsRegister<Register>();
+ Register value_reg_hi = locations->GetTemp(1).AsRegister<Register>();
+
+ __ vmovrrd(value_reg_lo, value_reg_hi, value_reg);
+
+ GenerateWideAtomicStore(base, offset,
+ value_reg_lo,
+ value_reg_hi,
+ locations->GetTemp(2).AsRegister<Register>(),
+ locations->GetTemp(3).AsRegister<Register>(),
+ instruction);
+ } else {
+ __ StoreDToOffset(value_reg, base, offset);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
break;
}
@@ -2572,75 +2739,162 @@
LOG(FATAL) << "Unreachable type " << field_type;
UNREACHABLE();
}
+
+ // Longs and doubles are handled in the switch.
+ if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
+
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+ Register card = locations->GetTemp(1).AsRegister<Register>();
+ codegen_->MarkGCCard(temp, card, base, value.AsRegister<Register>());
+ }
+
+ if (is_volatile) {
+ GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+ }
}
-void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
+void LocationsBuilderARM::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) {
+ DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+
+ bool volatile_for_double = field_info.IsVolatile()
+ && (field_info.GetFieldType() == Primitive::kPrimDouble)
+ && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
+ bool overlap = field_info.IsVolatile() && (field_info.GetFieldType() == Primitive::kPrimLong);
+ locations->SetOut(Location::RequiresRegister(),
+ (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap));
+ if (volatile_for_double) {
+ // Arm encoding have some additional constraints for ldrexd/strexd:
+ // - registers need to be consecutive
+ // - the first register should be even but not R14.
+ // We don't test for Arm yet, and the assertion makes sure that we revisit this if we ever
+ // enable Arm encoding.
+ DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ }
}
-void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
- LocationSummary* locations = instruction->GetLocations();
- Register obj = locations->InAt(0).AsRegister<Register>();
- uint32_t offset = instruction->GetFieldOffset().Uint32Value();
+void InstructionCodeGeneratorARM::HandleFieldGet(HInstruction* instruction,
+ const FieldInfo& field_info) {
+ DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
- switch (instruction->GetType()) {
+ LocationSummary* locations = instruction->GetLocations();
+ Register base = locations->InAt(0).AsRegister<Register>();
+ Location out = locations->Out();
+ bool is_volatile = field_info.IsVolatile();
+ bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
+ Primitive::Type field_type = field_info.GetFieldType();
+ uint32_t offset = field_info.GetFieldOffset().Uint32Value();
+
+ switch (field_type) {
case Primitive::kPrimBoolean: {
- Register out = locations->Out().AsRegister<Register>();
- __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset);
+ __ LoadFromOffset(kLoadUnsignedByte, out.AsRegister<Register>(), base, offset);
break;
}
case Primitive::kPrimByte: {
- Register out = locations->Out().AsRegister<Register>();
- __ LoadFromOffset(kLoadSignedByte, out, obj, offset);
+ __ LoadFromOffset(kLoadSignedByte, out.AsRegister<Register>(), base, offset);
break;
}
case Primitive::kPrimShort: {
- Register out = locations->Out().AsRegister<Register>();
- __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset);
+ __ LoadFromOffset(kLoadSignedHalfword, out.AsRegister<Register>(), base, offset);
break;
}
case Primitive::kPrimChar: {
- Register out = locations->Out().AsRegister<Register>();
- __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset);
+ __ LoadFromOffset(kLoadUnsignedHalfword, out.AsRegister<Register>(), base, offset);
break;
}
case Primitive::kPrimInt:
case Primitive::kPrimNot: {
- Register out = locations->Out().AsRegister<Register>();
- __ LoadFromOffset(kLoadWord, out, obj, offset);
+ __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), base, offset);
break;
}
case Primitive::kPrimLong: {
- // TODO: support volatile.
- Location out = locations->Out();
- __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), obj, offset);
+ if (is_volatile && !atomic_ldrd_strd) {
+ GenerateWideAtomicLoad(base, offset,
+ out.AsRegisterPairLow<Register>(),
+ out.AsRegisterPairHigh<Register>());
+ } else {
+ __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), base, offset);
+ }
break;
}
case Primitive::kPrimFloat: {
- SRegister out = locations->Out().AsFpuRegister<SRegister>();
- __ LoadSFromOffset(out, obj, offset);
+ __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), base, offset);
break;
}
case Primitive::kPrimDouble: {
- DRegister out = FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>());
- __ LoadDFromOffset(out, obj, offset);
+ DRegister out_reg = FromLowSToD(out.AsFpuRegisterPairLow<SRegister>());
+ if (is_volatile && !atomic_ldrd_strd) {
+ Register lo = locations->GetTemp(0).AsRegister<Register>();
+ Register hi = locations->GetTemp(1).AsRegister<Register>();
+ GenerateWideAtomicLoad(base, offset, lo, hi);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ __ vmovdrr(out_reg, lo, hi);
+ } else {
+ __ LoadDFromOffset(out_reg, base, offset);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
break;
}
case Primitive::kPrimVoid:
- LOG(FATAL) << "Unreachable type " << instruction->GetType();
+ LOG(FATAL) << "Unreachable type " << field_type;
UNREACHABLE();
}
+
+ // Doubles are handled in the switch.
+ if (field_type != Primitive::kPrimDouble) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
+
+ if (is_volatile) {
+ GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
+ }
+}
+
+void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+ HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+ HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
+
+void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
+ HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
+ HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
+
+void LocationsBuilderARM::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+ HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorARM::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+ HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
+
+void LocationsBuilderARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+ HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+ HandleFieldSet(instruction, instruction->GetFieldInfo());
}
void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
@@ -2652,20 +2906,32 @@
}
}
-void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
+void InstructionCodeGeneratorARM::GenerateImplicitNullCheck(HNullCheck* instruction) {
+ if (codegen_->CanMoveNullCheckToUser(instruction)) {
+ return;
+ }
+ Location obj = instruction->GetLocations()->InAt(0);
+
+ __ LoadFromOffset(kLoadWord, IP, obj.AsRegister<Register>(), 0);
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+}
+
+void InstructionCodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruction) {
SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction);
codegen_->AddSlowPath(slow_path);
LocationSummary* locations = instruction->GetLocations();
Location obj = locations->InAt(0);
- if (obj.IsRegister()) {
- __ cmp(obj.AsRegister<Register>(), ShifterOperand(0));
- __ b(slow_path->GetEntryLabel(), EQ);
+ __ cmp(obj.AsRegister<Register>(), ShifterOperand(0));
+ __ b(slow_path->GetEntryLabel(), EQ);
+}
+
+void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
+ if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
+ GenerateImplicitNullCheck(instruction);
} else {
- DCHECK(obj.IsConstant()) << obj;
- DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0);
- __ b(slow_path->GetEntryLabel());
+ GenerateExplicitNullCheck(instruction);
}
}
@@ -2769,14 +3035,39 @@
break;
}
- case Primitive::kPrimFloat:
- case Primitive::kPrimDouble:
- LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
- UNREACHABLE();
+ case Primitive::kPrimFloat: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
+ Location out = locations->Out();
+ DCHECK(out.IsFpuRegister());
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), obj, offset);
+ } else {
+ __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
+ __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), IP, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimDouble: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
+ Location out = locations->Out();
+ DCHECK(out.IsFpuRegisterPair());
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ __ LoadDFromOffset(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), obj, offset);
+ } else {
+ __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
+ __ LoadDFromOffset(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), IP, data_offset);
+ }
+ break;
+ }
+
case Primitive::kPrimVoid:
LOG(FATAL) << "Unreachable type " << instruction->GetType();
UNREACHABLE();
}
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
}
void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) {
@@ -2860,6 +3151,7 @@
__ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
__ StoreToOffset(kStoreWord, value, IP, data_offset);
}
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
if (needs_write_barrier) {
DCHECK_EQ(value_type, Primitive::kPrimNot);
Register temp = locations->GetTemp(0).AsRegister<Register>();
@@ -2889,14 +3181,44 @@
break;
}
- case Primitive::kPrimFloat:
- case Primitive::kPrimDouble:
- LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
- UNREACHABLE();
+ case Primitive::kPrimFloat: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
+ Location value = locations->InAt(2);
+ DCHECK(value.IsFpuRegister());
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ __ StoreSToOffset(value.AsFpuRegister<SRegister>(), obj, offset);
+ } else {
+ __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
+ __ StoreSToOffset(value.AsFpuRegister<SRegister>(), IP, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimDouble: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
+ Location value = locations->InAt(2);
+ DCHECK(value.IsFpuRegisterPair());
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), obj, offset);
+ } else {
+ __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
+ __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), IP, data_offset);
+ }
+
+ break;
+ }
+
case Primitive::kPrimVoid:
- LOG(FATAL) << "Unreachable type " << instruction->GetType();
+ LOG(FATAL) << "Unreachable type " << value_type;
UNREACHABLE();
}
+
+ // Ints and objects are handled in the switch.
+ if (value_type != Primitive::kPrimInt && value_type != Primitive::kPrimNot) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
}
void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) {
@@ -2912,6 +3234,7 @@
Register obj = locations->InAt(0).AsRegister<Register>();
Register out = locations->Out().AsRegister<Register>();
__ LoadFromOffset(kLoadWord, out, obj, offset);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
}
void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) {
@@ -3022,21 +3345,102 @@
if (destination.IsRegister()) {
__ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(),
SP, source.GetStackIndex());
+ } else if (destination.IsFpuRegister()) {
+ __ LoadSFromOffset(destination.AsFpuRegister<SRegister>(), SP, source.GetStackIndex());
} else {
DCHECK(destination.IsStackSlot());
__ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex());
__ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
}
- } else {
- DCHECK(source.IsConstant());
- DCHECK(source.GetConstant()->IsIntConstant());
- int32_t value = source.GetConstant()->AsIntConstant()->GetValue();
- if (destination.IsRegister()) {
- __ LoadImmediate(destination.AsRegister<Register>(), value);
+ } else if (source.IsFpuRegister()) {
+ if (destination.IsFpuRegister()) {
+ __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>());
} else {
DCHECK(destination.IsStackSlot());
- __ LoadImmediate(IP, value);
+ __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex());
+ }
+ } else if (source.IsDoubleStackSlot()) {
+ if (destination.IsDoubleStackSlot()) {
+ __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex());
__ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
+ __ LoadFromOffset(kLoadWord, IP, SP, source.GetHighStackIndex(kArmWordSize));
+ __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize));
+ } else if (destination.IsRegisterPair()) {
+ DCHECK(ExpectedPairLayout(destination));
+ __ LoadFromOffset(
+ kLoadWordPair, destination.AsRegisterPairLow<Register>(), SP, source.GetStackIndex());
+ } else {
+ DCHECK(destination.IsFpuRegisterPair()) << destination;
+ __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
+ SP,
+ source.GetStackIndex());
+ }
+ } else if (source.IsRegisterPair()) {
+ if (destination.IsRegisterPair()) {
+ __ Mov(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>());
+ __ Mov(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>());
+ } else {
+ DCHECK(destination.IsDoubleStackSlot()) << destination;
+ DCHECK(ExpectedPairLayout(source));
+ __ StoreToOffset(
+ kStoreWordPair, source.AsRegisterPairLow<Register>(), SP, destination.GetStackIndex());
+ }
+ } else if (source.IsFpuRegisterPair()) {
+ if (destination.IsFpuRegisterPair()) {
+ __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
+ FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
+ } else {
+ DCHECK(destination.IsDoubleStackSlot()) << destination;
+ __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()),
+ SP,
+ destination.GetStackIndex());
+ }
+ } else {
+ DCHECK(source.IsConstant()) << source;
+ HInstruction* constant = source.GetConstant();
+ if (constant->IsIntConstant()) {
+ int32_t value = constant->AsIntConstant()->GetValue();
+ if (destination.IsRegister()) {
+ __ LoadImmediate(destination.AsRegister<Register>(), value);
+ } else {
+ DCHECK(destination.IsStackSlot());
+ __ LoadImmediate(IP, value);
+ __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
+ }
+ } else if (constant->IsLongConstant()) {
+ int64_t value = constant->AsLongConstant()->GetValue();
+ if (destination.IsRegisterPair()) {
+ __ LoadImmediate(destination.AsRegisterPairLow<Register>(), Low32Bits(value));
+ __ LoadImmediate(destination.AsRegisterPairHigh<Register>(), High32Bits(value));
+ } else {
+ DCHECK(destination.IsDoubleStackSlot()) << destination;
+ __ LoadImmediate(IP, Low32Bits(value));
+ __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
+ __ LoadImmediate(IP, High32Bits(value));
+ __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize));
+ }
+ } else if (constant->IsDoubleConstant()) {
+ double value = constant->AsDoubleConstant()->GetValue();
+ if (destination.IsFpuRegisterPair()) {
+ __ LoadDImmediate(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), value);
+ } else {
+ DCHECK(destination.IsDoubleStackSlot()) << destination;
+ uint64_t int_value = bit_cast<uint64_t, double>(value);
+ __ LoadImmediate(IP, Low32Bits(int_value));
+ __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
+ __ LoadImmediate(IP, High32Bits(int_value));
+ __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize));
+ }
+ } else {
+ DCHECK(constant->IsFloatConstant()) << constant->DebugName();
+ float value = constant->AsFloatConstant()->GetValue();
+ if (destination.IsFpuRegister()) {
+ __ LoadSImmediate(destination.AsFpuRegister<SRegister>(), value);
+ } else {
+ DCHECK(destination.IsStackSlot());
+ __ LoadImmediate(IP, bit_cast<int32_t, float>(value));
+ __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
+ }
}
}
}
@@ -3075,8 +3479,80 @@
Exchange(destination.AsRegister<Register>(), source.GetStackIndex());
} else if (source.IsStackSlot() && destination.IsStackSlot()) {
Exchange(source.GetStackIndex(), destination.GetStackIndex());
+ } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
+ __ vmovrs(IP, source.AsFpuRegister<SRegister>());
+ __ vmovs(source.AsFpuRegister<SRegister>(), destination.AsFpuRegister<SRegister>());
+ __ vmovsr(destination.AsFpuRegister<SRegister>(), IP);
+ } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
+ __ Mov(IP, source.AsRegisterPairLow<Register>());
+ __ Mov(source.AsRegisterPairLow<Register>(), destination.AsRegisterPairLow<Register>());
+ __ Mov(destination.AsRegisterPairLow<Register>(), IP);
+ __ Mov(IP, source.AsRegisterPairHigh<Register>());
+ __ Mov(source.AsRegisterPairHigh<Register>(), destination.AsRegisterPairHigh<Register>());
+ __ Mov(destination.AsRegisterPairHigh<Register>(), IP);
+ } else if (source.IsRegisterPair() || destination.IsRegisterPair()) {
+ // TODO: Find a D register available in the parallel moves,
+ // or reserve globally a D register.
+ DRegister tmp = D0;
+ Register low_reg = source.IsRegisterPair()
+ ? source.AsRegisterPairLow<Register>()
+ : destination.AsRegisterPairLow<Register>();
+ int mem = source.IsRegisterPair()
+ ? destination.GetStackIndex()
+ : source.GetStackIndex();
+ DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination));
+ // Make room for the pushed DRegister.
+ mem += 8;
+ __ vpushd(tmp, 1);
+ __ vmovdrr(tmp, low_reg, static_cast<Register>(low_reg + 1));
+ __ LoadFromOffset(kLoadWordPair, low_reg, SP, mem);
+ __ StoreDToOffset(tmp, SP, mem);
+ __ vpopd(tmp, 1);
+ } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
+ // TODO: Find a D register available in the parallel moves,
+ // or reserve globally a D register.
+ DRegister tmp = D0;
+ DRegister first = FromLowSToD(source.AsFpuRegisterPairLow<SRegister>());
+ DRegister second = FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>());
+ while (tmp == first || tmp == second) {
+ tmp = static_cast<DRegister>(tmp + 1);
+ }
+ __ vpushd(tmp, 1);
+ __ vmovd(tmp, first);
+ __ vmovd(first, second);
+ __ vmovd(second, tmp);
+ __ vpopd(tmp, 1);
+ } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
+ DRegister reg = source.IsFpuRegisterPair()
+ ? FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())
+ : FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>());
+ int mem = source.IsFpuRegisterPair()
+ ? destination.GetStackIndex()
+ : source.GetStackIndex();
+ // TODO: Find or reserve a D register.
+ DRegister tmp = reg == D0 ? D1 : D0;
+ // Make room for the pushed DRegister.
+ mem += 8;
+ __ vpushd(tmp, 1);
+ __ vmovd(tmp, reg);
+ __ LoadDFromOffset(reg, SP, mem);
+ __ StoreDToOffset(tmp, SP, mem);
+ __ vpopd(tmp, 1);
+ } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
+ SRegister reg = source.IsFpuRegister() ? source.AsFpuRegister<SRegister>()
+ : destination.AsFpuRegister<SRegister>();
+ int mem = source.IsFpuRegister()
+ ? destination.GetStackIndex()
+ : source.GetStackIndex();
+
+ __ vmovrs(IP, reg);
+ __ LoadSFromOffset(reg, SP, mem);
+ __ StoreToOffset(kStoreWord, IP, SP, mem);
+ } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
+ Exchange(source.GetStackIndex(), destination.GetStackIndex());
+ Exchange(source.GetHighStackIndex(kArmWordSize), destination.GetHighStackIndex(kArmWordSize));
} else {
- LOG(FATAL) << "Unimplemented";
+ LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
}
}
@@ -3153,146 +3629,6 @@
__ Bind(slow_path->GetExitLabel());
}
-void LocationsBuilderARM::VisitStaticFieldGet(HStaticFieldGet* instruction) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-void InstructionCodeGeneratorARM::VisitStaticFieldGet(HStaticFieldGet* instruction) {
- LocationSummary* locations = instruction->GetLocations();
- Register cls = locations->InAt(0).AsRegister<Register>();
- uint32_t offset = instruction->GetFieldOffset().Uint32Value();
-
- switch (instruction->GetType()) {
- case Primitive::kPrimBoolean: {
- Register out = locations->Out().AsRegister<Register>();
- __ LoadFromOffset(kLoadUnsignedByte, out, cls, offset);
- break;
- }
-
- case Primitive::kPrimByte: {
- Register out = locations->Out().AsRegister<Register>();
- __ LoadFromOffset(kLoadSignedByte, out, cls, offset);
- break;
- }
-
- case Primitive::kPrimShort: {
- Register out = locations->Out().AsRegister<Register>();
- __ LoadFromOffset(kLoadSignedHalfword, out, cls, offset);
- break;
- }
-
- case Primitive::kPrimChar: {
- Register out = locations->Out().AsRegister<Register>();
- __ LoadFromOffset(kLoadUnsignedHalfword, out, cls, offset);
- break;
- }
-
- case Primitive::kPrimInt:
- case Primitive::kPrimNot: {
- Register out = locations->Out().AsRegister<Register>();
- __ LoadFromOffset(kLoadWord, out, cls, offset);
- break;
- }
-
- case Primitive::kPrimLong: {
- // TODO: support volatile.
- Location out = locations->Out();
- __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), cls, offset);
- break;
- }
-
- case Primitive::kPrimFloat: {
- SRegister out = locations->Out().AsFpuRegister<SRegister>();
- __ LoadSFromOffset(out, cls, offset);
- break;
- }
-
- case Primitive::kPrimDouble: {
- DRegister out = FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>());
- __ LoadDFromOffset(out, cls, offset);
- break;
- }
-
- case Primitive::kPrimVoid:
- LOG(FATAL) << "Unreachable type " << instruction->GetType();
- UNREACHABLE();
- }
-}
-
-void LocationsBuilderARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- bool needs_write_barrier =
- CodeGenerator::StoreNeedsWriteBarrier(instruction->GetFieldType(), instruction->GetValue());
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- // Temporary registers for the write barrier.
- if (needs_write_barrier) {
- locations->AddTemp(Location::RequiresRegister());
- locations->AddTemp(Location::RequiresRegister());
- }
-}
-
-void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- LocationSummary* locations = instruction->GetLocations();
- Register cls = locations->InAt(0).AsRegister<Register>();
- uint32_t offset = instruction->GetFieldOffset().Uint32Value();
- Primitive::Type field_type = instruction->GetFieldType();
-
- switch (field_type) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte: {
- Register value = locations->InAt(1).AsRegister<Register>();
- __ StoreToOffset(kStoreByte, value, cls, offset);
- break;
- }
-
- case Primitive::kPrimShort:
- case Primitive::kPrimChar: {
- Register value = locations->InAt(1).AsRegister<Register>();
- __ StoreToOffset(kStoreHalfword, value, cls, offset);
- break;
- }
-
- case Primitive::kPrimInt:
- case Primitive::kPrimNot: {
- Register value = locations->InAt(1).AsRegister<Register>();
- __ StoreToOffset(kStoreWord, value, cls, offset);
- if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue())) {
- Register temp = locations->GetTemp(0).AsRegister<Register>();
- Register card = locations->GetTemp(1).AsRegister<Register>();
- codegen_->MarkGCCard(temp, card, cls, value);
- }
- break;
- }
-
- case Primitive::kPrimLong: {
- Location value = locations->InAt(1);
- __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), cls, offset);
- break;
- }
-
- case Primitive::kPrimFloat: {
- SRegister value = locations->InAt(1).AsFpuRegister<SRegister>();
- __ StoreSToOffset(value, cls, offset);
- break;
- }
-
- case Primitive::kPrimDouble: {
- DRegister value = FromLowSToD(locations->InAt(1).AsFpuRegisterPairLow<SRegister>());
- __ StoreDToOffset(value, cls, offset);
- break;
- }
-
- case Primitive::kPrimVoid:
- LOG(FATAL) << "Unreachable type " << field_type;
- UNREACHABLE();
- }
-}
-
void LocationsBuilderARM::VisitLoadString(HLoadString* load) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
@@ -3346,7 +3682,8 @@
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister());
+ // The out register is used as a temporary, so it overlaps with the inputs.
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
}
void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) {
@@ -3442,8 +3779,7 @@
|| instruction->GetResultType() == Primitive::kPrimLong);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
- bool output_overlaps = (instruction->GetResultType() == Primitive::kPrimLong);
- locations->SetOut(Location::RequiresRegister(), output_overlaps);
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
void InstructionCodeGeneratorARM::VisitAnd(HAnd* instruction) {
@@ -3504,5 +3840,38 @@
}
}
+void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Register temp) {
+ DCHECK_EQ(temp, kArtMethodRegister);
+
+ // TODO: Implement all kinds of calls:
+ // 1) boot -> boot
+ // 2) app -> boot
+ // 3) app -> app
+ //
+ // Currently we implement the app -> app logic, which looks up in the resolve cache.
+
+ // temp = method;
+ LoadCurrentMethod(temp);
+ if (!invoke->IsRecursive()) {
+ // temp = temp->dex_cache_resolved_methods_;
+ __ LoadFromOffset(
+ kLoadWord, temp, temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
+ // temp = temp[index_in_cache]
+ __ LoadFromOffset(
+ kLoadWord, temp, temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex()));
+ // LR = temp[offset_of_quick_compiled_code]
+ __ LoadFromOffset(kLoadWord, LR, temp,
+ mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kArmWordSize).Int32Value());
+ // LR()
+ __ blx(LR);
+ } else {
+ __ bl(GetFrameEntryLabel());
+ }
+
+ RecordPcInfo(invoke, invoke->GetDexPc());
+ DCHECK(!IsLeafMethod());
+}
+
} // namespace arm
} // namespace art
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 226e635..47d81ff 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -18,6 +18,8 @@
#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM_H_
#include "code_generator.h"
+#include "dex/compiler_enums.h"
+#include "driver/compiler_options.h"
#include "nodes.h"
#include "parallel_move_resolver.h"
#include "utils/arm/assembler_thumb2.h"
@@ -32,12 +34,19 @@
static constexpr size_t kArmWordSize = kArmPointerSize;
static constexpr Register kParameterCoreRegisters[] = { R1, R2, R3 };
-static constexpr RegisterPair kParameterCorePairRegisters[] = { R1_R2, R2_R3 };
static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters);
static constexpr SRegister kParameterFpuRegisters[] =
{ S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15 };
static constexpr size_t kParameterFpuRegistersLength = arraysize(kParameterFpuRegisters);
+static constexpr Register kArtMethodRegister = R0;
+
+static constexpr DRegister FromLowSToD(SRegister reg) {
+ return DCHECK_CONSTEXPR(reg % 2 == 0, , D0)
+ static_cast<DRegister>(reg / 2);
+}
+
+
class InvokeDexCallingConvention : public CallingConvention<Register, SRegister> {
public:
InvokeDexCallingConvention()
@@ -46,11 +55,6 @@
kParameterFpuRegisters,
kParameterFpuRegistersLength) {}
- RegisterPair GetRegisterPairAt(size_t argument_index) {
- DCHECK_LT(argument_index + 1, GetNumberOfRegisters());
- return kParameterCorePairRegisters[argument_index];
- }
-
private:
DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConvention);
};
@@ -94,6 +98,20 @@
DISALLOW_COPY_AND_ASSIGN(ParallelMoveResolverARM);
};
+class SlowPathCodeARM : public SlowPathCode {
+ public:
+ SlowPathCodeARM() : entry_label_(), exit_label_() {}
+
+ Label* GetEntryLabel() { return &entry_label_; }
+ Label* GetExitLabel() { return &exit_label_; }
+
+ private:
+ Label entry_label_;
+ Label exit_label_;
+
+ DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM);
+};
+
class LocationsBuilderARM : public HGraphVisitor {
public:
LocationsBuilderARM(HGraph* graph, CodeGeneratorARM* codegen)
@@ -110,6 +128,8 @@
void HandleInvoke(HInvoke* invoke);
void HandleBitwiseOperation(HBinaryOperation* operation);
void HandleShift(HBinaryOperation* operation);
+ void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+ void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
CodeGeneratorARM* const codegen_;
InvokeDexCallingConventionVisitor parameter_visitor_;
@@ -138,6 +158,17 @@
void GenerateClassInitializationCheck(SlowPathCodeARM* slow_path, Register class_reg);
void HandleBitwiseOperation(HBinaryOperation* operation);
void HandleShift(HBinaryOperation* operation);
+ void GenerateMemoryBarrier(MemBarrierKind kind);
+ void GenerateWideAtomicStore(Register addr, uint32_t offset,
+ Register value_lo, Register value_hi,
+ Register temp1, Register temp2,
+ HInstruction* instruction);
+ void GenerateWideAtomicLoad(Register addr, uint32_t offset,
+ Register out_lo, Register out_hi);
+ void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+ void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
+ void GenerateImplicitNullCheck(HNullCheck* instruction);
+ void GenerateExplicitNullCheck(HNullCheck* instruction);
ArmAssembler* const assembler_;
CodeGeneratorARM* const codegen_;
@@ -147,7 +178,9 @@
class CodeGeneratorARM : public CodeGenerator {
public:
- explicit CodeGeneratorARM(HGraph* graph);
+ CodeGeneratorARM(HGraph* graph,
+ const ArmInstructionSetFeatures& isa_features,
+ const CompilerOptions& compiler_options);
virtual ~CodeGeneratorARM() {}
void GenerateFrameEntry() OVERRIDE;
@@ -156,12 +189,17 @@
void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+ size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+ size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t GetWordSize() const OVERRIDE {
return kArmWordSize;
}
- size_t FrameEntrySpillSize() const OVERRIDE;
+ size_t GetFloatingPointSpillSlotSize() const OVERRIDE {
+ // Allocated in S registers, which are word sized.
+ return kArmWordSize;
+ }
HGraphVisitor* GetLocationBuilder() OVERRIDE {
return &location_builder_;
@@ -179,7 +217,7 @@
return GetLabelOf(block)->Position();
}
- void SetupBlockedRegisters() const OVERRIDE;
+ void SetupBlockedRegisters(bool is_baseline) const OVERRIDE;
Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE;
@@ -221,13 +259,29 @@
block_labels_.SetSize(GetGraph()->GetBlocks().Size());
}
+ const ArmInstructionSetFeatures& GetInstructionSetFeatures() const {
+ return isa_features_;
+ }
+
+ bool NeedsTwoRegisters(Primitive::Type type) const OVERRIDE {
+ return type == Primitive::kPrimDouble || type == Primitive::kPrimLong;
+ }
+
+ void ComputeSpillMask() OVERRIDE;
+
+ Label* GetFrameEntryLabel() { return &frame_entry_label_; }
+
+ void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Register temp);
+
private:
// Labels for each block that will be compiled.
GrowableArray<Label> block_labels_;
+ Label frame_entry_label_;
LocationsBuilderARM location_builder_;
InstructionCodeGeneratorARM instruction_visitor_;
ParallelMoveResolverARM move_resolver_;
Thumb2Assembler assembler_;
+ const ArmInstructionSetFeatures& isa_features_;
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM);
};
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index a61ef2d..46f1a9b 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -16,11 +16,16 @@
#include "code_generator_arm64.h"
+#include "common_arm64.h"
#include "entrypoints/quick/quick_entrypoints.h"
+#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "gc/accounting/card_table.h"
+#include "intrinsics.h"
+#include "intrinsics_arm64.h"
#include "mirror/array-inl.h"
#include "mirror/art_method.h"
#include "mirror/class.h"
+#include "offsets.h"
#include "thread.h"
#include "utils/arm64/assembler_arm64.h"
#include "utils/assembler.h"
@@ -33,164 +38,34 @@
#error "ARM64 Codegen VIXL macro-assembler macro already defined."
#endif
-
namespace art {
namespace arm64 {
-static constexpr bool kExplicitStackOverflowCheck = false;
+using helpers::CPURegisterFrom;
+using helpers::DRegisterFrom;
+using helpers::FPRegisterFrom;
+using helpers::HeapOperand;
+using helpers::HeapOperandFrom;
+using helpers::InputCPURegisterAt;
+using helpers::InputFPRegisterAt;
+using helpers::InputRegisterAt;
+using helpers::InputOperandAt;
+using helpers::Int64ConstantFrom;
+using helpers::LocationFrom;
+using helpers::OperandFromMemOperand;
+using helpers::OutputCPURegister;
+using helpers::OutputFPRegister;
+using helpers::OutputRegister;
+using helpers::RegisterFrom;
+using helpers::StackOperandFrom;
+using helpers::VIXLRegCodeFromART;
+using helpers::WRegisterFrom;
+using helpers::XRegisterFrom;
+
static constexpr size_t kHeapRefSize = sizeof(mirror::HeapReference<mirror::Object>);
static constexpr int kCurrentMethodStackOffset = 0;
-namespace {
-
-bool IsFPType(Primitive::Type type) {
- return type == Primitive::kPrimFloat || type == Primitive::kPrimDouble;
-}
-
-bool IsIntegralType(Primitive::Type type) {
- switch (type) {
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimInt:
- case Primitive::kPrimLong:
- return true;
- default:
- return false;
- }
-}
-
-bool Is64BitType(Primitive::Type type) {
- return type == Primitive::kPrimLong || type == Primitive::kPrimDouble;
-}
-
-// Convenience helpers to ease conversion to and from VIXL operands.
-static_assert((SP == 31) && (WSP == 31) && (XZR == 32) && (WZR == 32),
- "Unexpected values for register codes.");
-
-int VIXLRegCodeFromART(int code) {
- if (code == SP) {
- return vixl::kSPRegInternalCode;
- }
- if (code == XZR) {
- return vixl::kZeroRegCode;
- }
- return code;
-}
-
-int ARTRegCodeFromVIXL(int code) {
- if (code == vixl::kSPRegInternalCode) {
- return SP;
- }
- if (code == vixl::kZeroRegCode) {
- return XZR;
- }
- return code;
-}
-
-Register XRegisterFrom(Location location) {
- return Register::XRegFromCode(VIXLRegCodeFromART(location.reg()));
-}
-
-Register WRegisterFrom(Location location) {
- return Register::WRegFromCode(VIXLRegCodeFromART(location.reg()));
-}
-
-Register RegisterFrom(Location location, Primitive::Type type) {
- DCHECK(type != Primitive::kPrimVoid && !IsFPType(type));
- return type == Primitive::kPrimLong ? XRegisterFrom(location) : WRegisterFrom(location);
-}
-
-Register OutputRegister(HInstruction* instr) {
- return RegisterFrom(instr->GetLocations()->Out(), instr->GetType());
-}
-
-Register InputRegisterAt(HInstruction* instr, int input_index) {
- return RegisterFrom(instr->GetLocations()->InAt(input_index),
- instr->InputAt(input_index)->GetType());
-}
-
-FPRegister DRegisterFrom(Location location) {
- return FPRegister::DRegFromCode(location.reg());
-}
-
-FPRegister SRegisterFrom(Location location) {
- return FPRegister::SRegFromCode(location.reg());
-}
-
-FPRegister FPRegisterFrom(Location location, Primitive::Type type) {
- DCHECK(IsFPType(type));
- return type == Primitive::kPrimDouble ? DRegisterFrom(location) : SRegisterFrom(location);
-}
-
-FPRegister OutputFPRegister(HInstruction* instr) {
- return FPRegisterFrom(instr->GetLocations()->Out(), instr->GetType());
-}
-
-FPRegister InputFPRegisterAt(HInstruction* instr, int input_index) {
- return FPRegisterFrom(instr->GetLocations()->InAt(input_index),
- instr->InputAt(input_index)->GetType());
-}
-
-CPURegister OutputCPURegister(HInstruction* instr) {
- return IsFPType(instr->GetType()) ? static_cast<CPURegister>(OutputFPRegister(instr))
- : static_cast<CPURegister>(OutputRegister(instr));
-}
-
-CPURegister InputCPURegisterAt(HInstruction* instr, int index) {
- return IsFPType(instr->InputAt(index)->GetType())
- ? static_cast<CPURegister>(InputFPRegisterAt(instr, index))
- : static_cast<CPURegister>(InputRegisterAt(instr, index));
-}
-
-int64_t Int64ConstantFrom(Location location) {
- HConstant* instr = location.GetConstant();
- return instr->IsIntConstant() ? instr->AsIntConstant()->GetValue()
- : instr->AsLongConstant()->GetValue();
-}
-
-Operand OperandFrom(Location location, Primitive::Type type) {
- if (location.IsRegister()) {
- return Operand(RegisterFrom(location, type));
- } else {
- return Operand(Int64ConstantFrom(location));
- }
-}
-
-Operand InputOperandAt(HInstruction* instr, int input_index) {
- return OperandFrom(instr->GetLocations()->InAt(input_index),
- instr->InputAt(input_index)->GetType());
-}
-
-MemOperand StackOperandFrom(Location location) {
- return MemOperand(sp, location.GetStackIndex());
-}
-
-MemOperand HeapOperand(const Register& base, size_t offset = 0) {
- // A heap reference must be 32bit, so fit in a W register.
- DCHECK(base.IsW());
- return MemOperand(base.X(), offset);
-}
-
-MemOperand HeapOperand(const Register& base, Offset offset) {
- return HeapOperand(base, offset.SizeValue());
-}
-
-MemOperand HeapOperandFrom(Location location, Offset offset) {
- return HeapOperand(RegisterFrom(location, Primitive::kPrimNot), offset);
-}
-
-Location LocationFrom(const Register& reg) {
- return Location::RegisterLocation(ARTRegCodeFromVIXL(reg.code()));
-}
-
-Location LocationFrom(const FPRegister& fpreg) {
- return Location::FpuRegisterLocation(fpreg.code());
-}
-
-} // namespace
-
inline Condition ARM64Condition(IfCondition cond) {
switch (cond) {
case kCondEQ: return eq;
@@ -224,8 +99,9 @@
static const Register kRuntimeParameterCoreRegisters[] = { x0, x1, x2, x3, x4, x5, x6, x7 };
static constexpr size_t kRuntimeParameterCoreRegistersLength =
arraysize(kRuntimeParameterCoreRegisters);
-static const FPRegister kRuntimeParameterFpuRegisters[] = { };
-static constexpr size_t kRuntimeParameterFpuRegistersLength = 0;
+static const FPRegister kRuntimeParameterFpuRegisters[] = { d0, d1, d2, d3, d4, d5, d6, d7 };
+static constexpr size_t kRuntimeParameterFpuRegistersLength =
+ arraysize(kRuntimeParameterCoreRegisters);
class InvokeRuntimeCallingConvention : public CallingConvention<Register, FPRegister> {
public:
@@ -250,30 +126,35 @@
#define __ down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler()->
#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, x).Int32Value()
-class SlowPathCodeARM64 : public SlowPathCode {
- public:
- SlowPathCodeARM64() : entry_label_(), exit_label_() {}
-
- vixl::Label* GetEntryLabel() { return &entry_label_; }
- vixl::Label* GetExitLabel() { return &exit_label_; }
-
- private:
- vixl::Label entry_label_;
- vixl::Label exit_label_;
-
- DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM64);
-};
-
class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 {
public:
- BoundsCheckSlowPathARM64() {}
+ BoundsCheckSlowPathARM64(HBoundsCheck* instruction,
+ Location index_location,
+ Location length_location)
+ : instruction_(instruction),
+ index_location_(index_location),
+ length_location_(length_location) {}
+
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
__ Bind(GetEntryLabel());
- __ Brk(__LINE__); // TODO: Unimplemented BoundsCheckSlowPathARM64.
+ // We're moving two locations to locations that could overlap, so we need a parallel
+ // move resolver.
+ InvokeRuntimeCallingConvention calling_convention;
+ codegen->EmitParallelMoves(
+ index_location_, LocationFrom(calling_convention.GetRegisterAt(0)),
+ length_location_, LocationFrom(calling_convention.GetRegisterAt(1)));
+ arm64_codegen->InvokeRuntime(
+ QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc());
+ CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
}
private:
+ HBoundsCheck* const instruction_;
+ const Location index_location_;
+ const Location length_location_;
+
DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM64);
};
@@ -286,6 +167,7 @@
__ Bind(GetEntryLabel());
arm64_codegen->InvokeRuntime(
QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc());
+ CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
}
private:
@@ -316,13 +198,18 @@
int32_t entry_point_offset = do_clinit_ ? QUICK_ENTRY_POINT(pInitializeStaticStorage)
: QUICK_ENTRY_POINT(pInitializeType);
arm64_codegen->InvokeRuntime(entry_point_offset, at_, dex_pc_);
+ if (do_clinit_) {
+ CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t, mirror::ArtMethod*>();
+ } else {
+ CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t, mirror::ArtMethod*>();
+ }
// Move the class to the desired location.
Location out = locations->Out();
if (out.IsValid()) {
DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
Primitive::Type type = at_->GetType();
- arm64_codegen->MoveHelper(out, calling_convention.GetReturnLocation(type), type);
+ arm64_codegen->MoveLocation(out, calling_convention.GetReturnLocation(type), type);
}
codegen->RestoreLiveRegisters(locations);
@@ -359,12 +246,13 @@
codegen->SaveLiveRegisters(locations);
InvokeRuntimeCallingConvention calling_convention;
- arm64_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(0).W());
- __ Mov(calling_convention.GetRegisterAt(1).W(), instruction_->GetStringIndex());
+ arm64_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1).W());
+ __ Mov(calling_convention.GetRegisterAt(0).W(), instruction_->GetStringIndex());
arm64_codegen->InvokeRuntime(
QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc());
+ CheckEntrypointTypes<kQuickResolveString, void*, uint32_t, mirror::ArtMethod*>();
Primitive::Type type = instruction_->GetType();
- arm64_codegen->MoveHelper(locations->Out(), calling_convention.GetReturnLocation(type), type);
+ arm64_codegen->MoveLocation(locations->Out(), calling_convention.GetReturnLocation(type), type);
codegen->RestoreLiveRegisters(locations);
__ B(GetExitLabel());
@@ -385,6 +273,7 @@
__ Bind(GetEntryLabel());
arm64_codegen->InvokeRuntime(
QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc());
+ CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
}
private:
@@ -393,20 +282,6 @@
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM64);
};
-class StackOverflowCheckSlowPathARM64 : public SlowPathCodeARM64 {
- public:
- StackOverflowCheckSlowPathARM64() {}
-
- virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
- CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
- __ Bind(GetEntryLabel());
- arm64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowStackOverflow), nullptr, 0);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM64);
-};
-
class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 {
public:
explicit SuspendCheckSlowPathARM64(HSuspendCheck* instruction,
@@ -419,6 +294,7 @@
codegen->SaveLiveRegisters(instruction_->GetLocations());
arm64_codegen->InvokeRuntime(
QUICK_ENTRY_POINT(pTestSuspend), instruction_, instruction_->GetDexPc());
+ CheckEntrypointTypes<kQuickTestSuspend, void, void>();
codegen->RestoreLiveRegisters(instruction_->GetLocations());
if (successor_ == nullptr) {
__ B(GetReturnLabel());
@@ -445,15 +321,54 @@
class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 {
public:
- TypeCheckSlowPathARM64() {}
+ TypeCheckSlowPathARM64(HInstruction* instruction,
+ Location class_to_check,
+ Location object_class,
+ uint32_t dex_pc)
+ : instruction_(instruction),
+ class_to_check_(class_to_check),
+ object_class_(object_class),
+ dex_pc_(dex_pc) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ LocationSummary* locations = instruction_->GetLocations();
+ DCHECK(instruction_->IsCheckCast()
+ || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
+ CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
+
__ Bind(GetEntryLabel());
- __ Brk(__LINE__); // TODO: Unimplemented TypeCheckSlowPathARM64.
+ codegen->SaveLiveRegisters(locations);
+
+ // We're moving two locations to locations that could overlap, so we need a parallel
+ // move resolver.
+ InvokeRuntimeCallingConvention calling_convention;
+ codegen->EmitParallelMoves(
+ class_to_check_, LocationFrom(calling_convention.GetRegisterAt(0)),
+ object_class_, LocationFrom(calling_convention.GetRegisterAt(1)));
+
+ if (instruction_->IsInstanceOf()) {
+ arm64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial), instruction_, dex_pc_);
+ Primitive::Type ret_type = instruction_->GetType();
+ Location ret_loc = calling_convention.GetReturnLocation(ret_type);
+ arm64_codegen->MoveLocation(locations->Out(), ret_loc, ret_type);
+ CheckEntrypointTypes<kQuickInstanceofNonTrivial, uint32_t,
+ const mirror::Class*, const mirror::Class*>();
+ } else {
+ DCHECK(instruction_->IsCheckCast());
+ arm64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast), instruction_, dex_pc_);
+ CheckEntrypointTypes<kQuickCheckCast, void, const mirror::Class*, const mirror::Class*>();
+ }
+
+ codegen->RestoreLiveRegisters(locations);
__ B(GetExitLabel());
}
private:
+ HInstruction* const instruction_;
+ const Location class_to_check_;
+ const Location object_class_;
+ uint32_t dex_pc_;
+
DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM64);
};
@@ -465,29 +380,38 @@
LOG(FATAL) << "Unreachable type " << type;
}
- if (IsFPType(type) && (fp_index_ < calling_convention.GetNumberOfFpuRegisters())) {
+ if (Primitive::IsFloatingPointType(type) &&
+ (fp_index_ < calling_convention.GetNumberOfFpuRegisters())) {
next_location = LocationFrom(calling_convention.GetFpuRegisterAt(fp_index_++));
- } else if (!IsFPType(type) && (gp_index_ < calling_convention.GetNumberOfRegisters())) {
+ } else if (!Primitive::IsFloatingPointType(type) &&
+ (gp_index_ < calling_convention.GetNumberOfRegisters())) {
next_location = LocationFrom(calling_convention.GetRegisterAt(gp_index_++));
} else {
size_t stack_offset = calling_convention.GetStackOffsetOf(stack_index_);
- next_location = Is64BitType(type) ? Location::DoubleStackSlot(stack_offset)
- : Location::StackSlot(stack_offset);
+ next_location = Primitive::Is64BitType(type) ? Location::DoubleStackSlot(stack_offset)
+ : Location::StackSlot(stack_offset);
}
// Space on the stack is reserved for all arguments.
- stack_index_ += Is64BitType(type) ? 2 : 1;
+ stack_index_ += Primitive::Is64BitType(type) ? 2 : 1;
return next_location;
}
-CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph)
+CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, const CompilerOptions& compiler_options)
: CodeGenerator(graph,
kNumberOfAllocatableRegisters,
kNumberOfAllocatableFPRegisters,
- kNumberOfAllocatableRegisterPairs),
+ kNumberOfAllocatableRegisterPairs,
+ callee_saved_core_registers.list(),
+ callee_saved_fp_registers.list(),
+ compiler_options),
block_labels_(nullptr),
location_builder_(graph, this),
- instruction_visitor_(graph, this) {}
+ instruction_visitor_(graph, this),
+ move_resolver_(graph->GetArena(), this) {
+ // Save the link register (containing the return address) to mimic Quick.
+ AddAllocatedRegister(LocationFrom(lr));
+}
#undef __
#define __ GetVIXLAssembler()->
@@ -498,45 +422,58 @@
CodeGenerator::Finalize(allocator);
}
+void ParallelMoveResolverARM64::EmitMove(size_t index) {
+ MoveOperands* move = moves_.Get(index);
+ codegen_->MoveLocation(move->GetDestination(), move->GetSource());
+}
+
+void ParallelMoveResolverARM64::EmitSwap(size_t index) {
+ MoveOperands* move = moves_.Get(index);
+ codegen_->SwapLocations(move->GetDestination(), move->GetSource());
+}
+
+void ParallelMoveResolverARM64::RestoreScratch(int reg) {
+ __ Pop(Register(VIXLRegCodeFromART(reg), kXRegSize));
+}
+
+void ParallelMoveResolverARM64::SpillScratch(int reg) {
+ __ Push(Register(VIXLRegCodeFromART(reg), kXRegSize));
+}
+
void CodeGeneratorARM64::GenerateFrameEntry() {
+ __ Bind(&frame_entry_label_);
+
bool do_overflow_check = FrameNeedsStackCheck(GetFrameSize(), kArm64) || !IsLeafMethod();
if (do_overflow_check) {
UseScratchRegisterScope temps(GetVIXLAssembler());
Register temp = temps.AcquireX();
- if (kExplicitStackOverflowCheck) {
- SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM64();
- AddSlowPath(slow_path);
-
- __ Ldr(temp, MemOperand(tr, Thread::StackEndOffset<kArm64WordSize>().Int32Value()));
- __ Cmp(sp, temp);
- __ B(lo, slow_path->GetEntryLabel());
- } else {
- __ Add(temp, sp, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm64)));
- __ Ldr(wzr, MemOperand(temp, 0));
- RecordPcInfo(nullptr, 0);
- }
+ DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
+ __ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(kArm64)));
+ __ Ldr(wzr, MemOperand(temp, 0));
+ RecordPcInfo(nullptr, 0);
}
- CPURegList preserved_regs = GetFramePreservedRegisters();
- int frame_size = GetFrameSize();
- core_spill_mask_ |= preserved_regs.list();
-
- __ Str(w0, MemOperand(sp, -frame_size, PreIndex));
- __ PokeCPURegList(preserved_regs, frame_size - preserved_regs.TotalSizeInBytes());
-
- // Stack layout:
- // sp[frame_size - 8] : lr.
- // ... : other preserved registers.
- // sp[frame_size - regs_size]: first preserved register.
- // ... : reserved frame space.
- // sp[0] : current method.
+ if (!HasEmptyFrame()) {
+ int frame_size = GetFrameSize();
+ // Stack layout:
+ // sp[frame_size - 8] : lr.
+ // ... : other preserved core registers.
+ // ... : other preserved fp registers.
+ // ... : reserved frame space.
+ // sp[0] : current method.
+ __ Str(kArtMethodRegister, MemOperand(sp, -frame_size, PreIndex));
+ __ PokeCPURegList(GetFramePreservedCoreRegisters(), frame_size - GetCoreSpillSize());
+ __ PokeCPURegList(GetFramePreservedFPRegisters(), frame_size - FrameEntrySpillSize());
+ }
}
void CodeGeneratorARM64::GenerateFrameExit() {
- int frame_size = GetFrameSize();
- CPURegList preserved_regs = GetFramePreservedRegisters();
- __ PeekCPURegList(preserved_regs, frame_size - preserved_regs.TotalSizeInBytes());
- __ Drop(frame_size);
+ if (!HasEmptyFrame()) {
+ int frame_size = GetFrameSize();
+ __ PeekCPURegList(GetFramePreservedFPRegisters(), frame_size - FrameEntrySpillSize());
+ __ PeekCPURegList(GetFramePreservedCoreRegisters(), frame_size - GetCoreSpillSize());
+ __ Drop(frame_size);
+ }
}
void CodeGeneratorARM64::Bind(HBasicBlock* block) {
@@ -571,25 +508,21 @@
}
} else if (instruction->IsTemporary()) {
Location temp_location = GetTemporaryLocation(instruction->AsTemporary());
- MoveHelper(location, temp_location, type);
+ MoveLocation(location, temp_location, type);
} else if (instruction->IsLoadLocal()) {
uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal());
- if (Is64BitType(type)) {
- MoveHelper(location, Location::DoubleStackSlot(stack_slot), type);
+ if (Primitive::Is64BitType(type)) {
+ MoveLocation(location, Location::DoubleStackSlot(stack_slot), type);
} else {
- MoveHelper(location, Location::StackSlot(stack_slot), type);
+ MoveLocation(location, Location::StackSlot(stack_slot), type);
}
} else {
DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary());
- MoveHelper(location, locations->Out(), type);
+ MoveLocation(location, locations->Out(), type);
}
}
-size_t CodeGeneratorARM64::FrameEntrySpillSize() const {
- return GetFramePreservedRegistersSize();
-}
-
Location CodeGeneratorARM64::GetStackLocation(HLoadLocal* load) const {
Primitive::Type type = load->GetType();
@@ -627,26 +560,38 @@
__ Bind(&done);
}
-void CodeGeneratorARM64::SetupBlockedRegisters() const {
- // Block reserved registers:
- // ip0 (VIXL temporary)
- // ip1 (VIXL temporary)
- // tr
- // lr
- // sp is not part of the allocatable registers, so we don't need to block it.
- // TODO: Avoid blocking callee-saved registers, and instead preserve them
- // where necessary.
+void CodeGeneratorARM64::SetupBlockedRegisters(bool is_baseline) const {
+ // Blocked core registers:
+ // lr : Runtime reserved.
+ // tr : Runtime reserved.
+ // xSuspend : Runtime reserved. TODO: Unblock this when the runtime stops using it.
+ // ip1 : VIXL core temp.
+ // ip0 : VIXL core temp.
+ //
+ // Blocked fp registers:
+ // d31 : VIXL fp temp.
CPURegList reserved_core_registers = vixl_reserved_core_registers;
reserved_core_registers.Combine(runtime_reserved_core_registers);
- reserved_core_registers.Combine(quick_callee_saved_registers);
while (!reserved_core_registers.IsEmpty()) {
blocked_core_registers_[reserved_core_registers.PopLowestIndex().code()] = true;
}
+
CPURegList reserved_fp_registers = vixl_reserved_fp_registers;
- reserved_fp_registers.Combine(CPURegList::GetCalleeSavedFP());
while (!reserved_core_registers.IsEmpty()) {
blocked_fpu_registers_[reserved_fp_registers.PopLowestIndex().code()] = true;
}
+
+ if (is_baseline) {
+ CPURegList reserved_core_baseline_registers = callee_saved_core_registers;
+ while (!reserved_core_baseline_registers.IsEmpty()) {
+ blocked_core_registers_[reserved_core_baseline_registers.PopLowestIndex().code()] = true;
+ }
+
+ CPURegList reserved_fp_baseline_registers = callee_saved_fp_registers;
+ while (!reserved_fp_baseline_registers.IsEmpty()) {
+ blocked_fpu_registers_[reserved_fp_baseline_registers.PopLowestIndex().code()] = true;
+ }
+ }
}
Location CodeGeneratorARM64::AllocateFreeRegister(Primitive::Type type) const {
@@ -654,7 +599,7 @@
LOG(FATAL) << "Unreachable type " << type;
}
- if (IsFPType(type)) {
+ if (Primitive::IsFloatingPointType(type)) {
ssize_t reg = FindFreeEntry(blocked_fpu_registers_, kNumberOfAllocatableFPRegisters);
DCHECK_NE(reg, -1);
return Location::FpuRegisterLocation(reg);
@@ -665,6 +610,30 @@
}
}
+size_t CodeGeneratorARM64::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
+ Register reg = Register(VIXLRegCodeFromART(reg_id), kXRegSize);
+ __ Str(reg, MemOperand(sp, stack_index));
+ return kArm64WordSize;
+}
+
+size_t CodeGeneratorARM64::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
+ Register reg = Register(VIXLRegCodeFromART(reg_id), kXRegSize);
+ __ Ldr(reg, MemOperand(sp, stack_index));
+ return kArm64WordSize;
+}
+
+size_t CodeGeneratorARM64::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
+ FPRegister reg = FPRegister(reg_id, kDRegSize);
+ __ Str(reg, MemOperand(sp, stack_index));
+ return kArm64WordSize;
+}
+
+size_t CodeGeneratorARM64::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
+ FPRegister reg = FPRegister(reg_id, kDRegSize);
+ __ Ldr(reg, MemOperand(sp, stack_index));
+ return kArm64WordSize;
+}
+
void CodeGeneratorARM64::DumpCoreRegister(std::ostream& stream, int reg) const {
stream << Arm64ManagedRegister::FromXRegister(XRegister(reg));
}
@@ -686,61 +655,165 @@
}
}
-void CodeGeneratorARM64::MoveHelper(Location destination,
- Location source,
- Primitive::Type type) {
+
+static bool CoherentConstantAndType(Location constant, Primitive::Type type) {
+ DCHECK(constant.IsConstant());
+ HConstant* cst = constant.GetConstant();
+ return (cst->IsIntConstant() && type == Primitive::kPrimInt) ||
+ (cst->IsLongConstant() && type == Primitive::kPrimLong) ||
+ (cst->IsFloatConstant() && type == Primitive::kPrimFloat) ||
+ (cst->IsDoubleConstant() && type == Primitive::kPrimDouble);
+}
+
+void CodeGeneratorARM64::MoveLocation(Location destination, Location source, Primitive::Type type) {
if (source.Equals(destination)) {
return;
}
- if (destination.IsRegister()) {
- Register dst = RegisterFrom(destination, type);
- if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
- DCHECK(dst.Is64Bits() == source.IsDoubleStackSlot());
- __ Ldr(dst, StackOperandFrom(source));
- } else {
- __ Mov(dst, OperandFrom(source, type));
- }
- } else if (destination.IsFpuRegister()) {
- FPRegister dst = FPRegisterFrom(destination, type);
- if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
- DCHECK(dst.Is64Bits() == source.IsDoubleStackSlot());
- __ Ldr(dst, StackOperandFrom(source));
- } else if (source.IsFpuRegister()) {
- __ Fmov(dst, FPRegisterFrom(source, type));
- } else {
- MoveConstant(dst, source.GetConstant());
- }
- } else {
- DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot());
- if (source.IsRegister()) {
- __ Str(RegisterFrom(source, type), StackOperandFrom(destination));
- } else if (source.IsFpuRegister()) {
- __ Str(FPRegisterFrom(source, type), StackOperandFrom(destination));
- } else if (source.IsConstant()) {
- UseScratchRegisterScope temps(GetVIXLAssembler());
- HConstant* cst = source.GetConstant();
- CPURegister temp;
- if (cst->IsIntConstant() || cst->IsLongConstant()) {
- temp = cst->IsIntConstant() ? temps.AcquireW() : temps.AcquireX();
+
+ // A valid move can always be inferred from the destination and source
+ // locations. When moving from and to a register, the argument type can be
+ // used to generate 32bit instead of 64bit moves. In debug mode we also
+ // checks the coherency of the locations and the type.
+ bool unspecified_type = (type == Primitive::kPrimVoid);
+
+ if (destination.IsRegister() || destination.IsFpuRegister()) {
+ if (unspecified_type) {
+ HConstant* src_cst = source.IsConstant() ? source.GetConstant() : nullptr;
+ if (source.IsStackSlot() ||
+ (src_cst != nullptr && (src_cst->IsIntConstant() || src_cst->IsFloatConstant()))) {
+ // For stack slots and 32bit constants, a 64bit type is appropriate.
+ type = destination.IsRegister() ? Primitive::kPrimInt : Primitive::kPrimFloat;
} else {
- DCHECK(cst->IsFloatConstant() || cst->IsDoubleConstant());
- temp = cst->IsFloatConstant() ? temps.AcquireS() : temps.AcquireD();
+ // If the source is a double stack slot or a 64bit constant, a 64bit
+ // type is appropriate. Else the source is a register, and since the
+ // type has not been specified, we chose a 64bit type to force a 64bit
+ // move.
+ type = destination.IsRegister() ? Primitive::kPrimLong : Primitive::kPrimDouble;
}
- MoveConstant(temp, cst);
+ }
+ DCHECK((destination.IsFpuRegister() && Primitive::IsFloatingPointType(type)) ||
+ (destination.IsRegister() && !Primitive::IsFloatingPointType(type)));
+ CPURegister dst = CPURegisterFrom(destination, type);
+ if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
+ DCHECK(dst.Is64Bits() == source.IsDoubleStackSlot());
+ __ Ldr(dst, StackOperandFrom(source));
+ } else if (source.IsConstant()) {
+ DCHECK(CoherentConstantAndType(source, type));
+ MoveConstant(dst, source.GetConstant());
+ } else {
+ if (destination.IsRegister()) {
+ __ Mov(Register(dst), RegisterFrom(source, type));
+ } else {
+ __ Fmov(FPRegister(dst), FPRegisterFrom(source, type));
+ }
+ }
+
+ } else { // The destination is not a register. It must be a stack slot.
+ DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot());
+ if (source.IsRegister() || source.IsFpuRegister()) {
+ if (unspecified_type) {
+ if (source.IsRegister()) {
+ type = destination.IsStackSlot() ? Primitive::kPrimInt : Primitive::kPrimLong;
+ } else {
+ type = destination.IsStackSlot() ? Primitive::kPrimFloat : Primitive::kPrimDouble;
+ }
+ }
+ DCHECK((destination.IsDoubleStackSlot() == Primitive::Is64BitType(type)) &&
+ (source.IsFpuRegister() == Primitive::IsFloatingPointType(type)));
+ __ Str(CPURegisterFrom(source, type), StackOperandFrom(destination));
+ } else if (source.IsConstant()) {
+ DCHECK(unspecified_type || CoherentConstantAndType(source, type));
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ HConstant* src_cst = source.GetConstant();
+ CPURegister temp;
+ if (src_cst->IsIntConstant()) {
+ temp = temps.AcquireW();
+ } else if (src_cst->IsLongConstant()) {
+ temp = temps.AcquireX();
+ } else if (src_cst->IsFloatConstant()) {
+ temp = temps.AcquireS();
+ } else {
+ DCHECK(src_cst->IsDoubleConstant());
+ temp = temps.AcquireD();
+ }
+ MoveConstant(temp, src_cst);
__ Str(temp, StackOperandFrom(destination));
} else {
DCHECK(source.IsStackSlot() || source.IsDoubleStackSlot());
+ DCHECK(source.IsDoubleStackSlot() == destination.IsDoubleStackSlot());
UseScratchRegisterScope temps(GetVIXLAssembler());
- Register temp = destination.IsDoubleStackSlot() ? temps.AcquireX() : temps.AcquireW();
+ // There is generally less pressure on FP registers.
+ FPRegister temp = destination.IsDoubleStackSlot() ? temps.AcquireD() : temps.AcquireS();
__ Ldr(temp, StackOperandFrom(source));
__ Str(temp, StackOperandFrom(destination));
}
}
}
+void CodeGeneratorARM64::SwapLocations(Location loc1, Location loc2) {
+ DCHECK(!loc1.IsConstant());
+ DCHECK(!loc2.IsConstant());
+
+ if (loc1.Equals(loc2)) {
+ return;
+ }
+
+ UseScratchRegisterScope temps(GetAssembler()->vixl_masm_);
+
+ bool is_slot1 = loc1.IsStackSlot() || loc1.IsDoubleStackSlot();
+ bool is_slot2 = loc2.IsStackSlot() || loc2.IsDoubleStackSlot();
+ bool is_fp_reg1 = loc1.IsFpuRegister();
+ bool is_fp_reg2 = loc2.IsFpuRegister();
+
+ if (loc2.IsRegister() && loc1.IsRegister()) {
+ Register r1 = XRegisterFrom(loc1);
+ Register r2 = XRegisterFrom(loc2);
+ Register tmp = temps.AcquireSameSizeAs(r1);
+ __ Mov(tmp, r2);
+ __ Mov(r2, r1);
+ __ Mov(r1, tmp);
+ } else if (is_fp_reg2 && is_fp_reg1) {
+ FPRegister r1 = DRegisterFrom(loc1);
+ FPRegister r2 = DRegisterFrom(loc2);
+ FPRegister tmp = temps.AcquireSameSizeAs(r1);
+ __ Fmov(tmp, r2);
+ __ Fmov(r2, r1);
+ __ Fmov(r1, tmp);
+ } else if (is_slot1 != is_slot2) {
+ MemOperand mem = StackOperandFrom(is_slot1 ? loc1 : loc2);
+ Location reg_loc = is_slot1 ? loc2 : loc1;
+ CPURegister reg, tmp;
+ if (reg_loc.IsFpuRegister()) {
+ reg = DRegisterFrom(reg_loc);
+ tmp = temps.AcquireD();
+ } else {
+ reg = XRegisterFrom(reg_loc);
+ tmp = temps.AcquireX();
+ }
+ __ Ldr(tmp, mem);
+ __ Str(reg, mem);
+ if (reg_loc.IsFpuRegister()) {
+ __ Fmov(FPRegister(reg), FPRegister(tmp));
+ } else {
+ __ Mov(Register(reg), Register(tmp));
+ }
+ } else if (is_slot1 && is_slot2) {
+ MemOperand mem1 = StackOperandFrom(loc1);
+ MemOperand mem2 = StackOperandFrom(loc2);
+ Register tmp1 = loc1.IsStackSlot() ? temps.AcquireW() : temps.AcquireX();
+ Register tmp2 = temps.AcquireSameSizeAs(tmp1);
+ __ Ldr(tmp1, mem1);
+ __ Ldr(tmp2, mem2);
+ __ Str(tmp1, mem2);
+ __ Str(tmp2, mem1);
+ } else {
+ LOG(FATAL) << "Unimplemented";
+ }
+}
+
void CodeGeneratorARM64::Load(Primitive::Type type,
- vixl::CPURegister dst,
- const vixl::MemOperand& src) {
+ CPURegister dst,
+ const MemOperand& src) {
switch (type) {
case Primitive::kPrimBoolean:
__ Ldrb(Register(dst), src);
@@ -759,7 +832,7 @@
case Primitive::kPrimLong:
case Primitive::kPrimFloat:
case Primitive::kPrimDouble:
- DCHECK(dst.Is64Bits() == Is64BitType(type));
+ DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type));
__ Ldr(dst, src);
break;
case Primitive::kPrimVoid:
@@ -767,32 +840,131 @@
}
}
+void CodeGeneratorARM64::LoadAcquire(HInstruction* instruction,
+ CPURegister dst,
+ const MemOperand& src) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ Register temp_base = temps.AcquireX();
+ Primitive::Type type = instruction->GetType();
+
+ DCHECK(!src.IsPreIndex());
+ DCHECK(!src.IsPostIndex());
+
+ // TODO(vixl): Let the MacroAssembler handle MemOperand.
+ __ Add(temp_base, src.base(), OperandFromMemOperand(src));
+ MemOperand base = MemOperand(temp_base);
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ __ Ldarb(Register(dst), base);
+ MaybeRecordImplicitNullCheck(instruction);
+ break;
+ case Primitive::kPrimByte:
+ __ Ldarb(Register(dst), base);
+ MaybeRecordImplicitNullCheck(instruction);
+ __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte);
+ break;
+ case Primitive::kPrimChar:
+ __ Ldarh(Register(dst), base);
+ MaybeRecordImplicitNullCheck(instruction);
+ break;
+ case Primitive::kPrimShort:
+ __ Ldarh(Register(dst), base);
+ MaybeRecordImplicitNullCheck(instruction);
+ __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte);
+ break;
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot:
+ case Primitive::kPrimLong:
+ DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type));
+ __ Ldar(Register(dst), base);
+ MaybeRecordImplicitNullCheck(instruction);
+ break;
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble: {
+ DCHECK(dst.IsFPRegister());
+ DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type));
+
+ Register temp = dst.Is64Bits() ? temps.AcquireX() : temps.AcquireW();
+ __ Ldar(temp, base);
+ MaybeRecordImplicitNullCheck(instruction);
+ __ Fmov(FPRegister(dst), temp);
+ break;
+ }
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << type;
+ }
+}
+
void CodeGeneratorARM64::Store(Primitive::Type type,
- vixl::CPURegister rt,
- const vixl::MemOperand& dst) {
+ CPURegister src,
+ const MemOperand& dst) {
switch (type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
- __ Strb(Register(rt), dst);
+ __ Strb(Register(src), dst);
break;
case Primitive::kPrimChar:
case Primitive::kPrimShort:
- __ Strh(Register(rt), dst);
+ __ Strh(Register(src), dst);
break;
case Primitive::kPrimInt:
case Primitive::kPrimNot:
case Primitive::kPrimLong:
case Primitive::kPrimFloat:
case Primitive::kPrimDouble:
- DCHECK(rt.Is64Bits() == Is64BitType(type));
- __ Str(rt, dst);
+ DCHECK_EQ(src.Is64Bits(), Primitive::Is64BitType(type));
+ __ Str(src, dst);
break;
case Primitive::kPrimVoid:
LOG(FATAL) << "Unreachable type " << type;
}
}
+void CodeGeneratorARM64::StoreRelease(Primitive::Type type,
+ CPURegister src,
+ const MemOperand& dst) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ Register temp_base = temps.AcquireX();
+
+ DCHECK(!dst.IsPreIndex());
+ DCHECK(!dst.IsPostIndex());
+
+ // TODO(vixl): Let the MacroAssembler handle this.
+ Operand op = OperandFromMemOperand(dst);
+ __ Add(temp_base, dst.base(), op);
+ MemOperand base = MemOperand(temp_base);
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ __ Stlrb(Register(src), base);
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ __ Stlrh(Register(src), base);
+ break;
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot:
+ case Primitive::kPrimLong:
+ DCHECK_EQ(src.Is64Bits(), Primitive::Is64BitType(type));
+ __ Stlr(Register(src), base);
+ break;
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble: {
+ DCHECK(src.IsFPRegister());
+ DCHECK_EQ(src.Is64Bits(), Primitive::Is64BitType(type));
+
+ Register temp = src.Is64Bits() ? temps.AcquireX() : temps.AcquireW();
+ __ Fmov(temp, FPRegister(src));
+ __ Stlr(temp, base);
+ break;
+ }
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << type;
+ }
+}
+
void CodeGeneratorARM64::LoadCurrentMethod(vixl::Register current_method) {
+ DCHECK(RequiresCurrentMethod());
DCHECK(current_method.IsW());
__ Ldr(current_method, MemOperand(sp, kCurrentMethodStackOffset));
}
@@ -816,14 +988,47 @@
vixl::Register class_reg) {
UseScratchRegisterScope temps(GetVIXLAssembler());
Register temp = temps.AcquireW();
- __ Ldr(temp, HeapOperand(class_reg, mirror::Class::StatusOffset()));
- __ Cmp(temp, mirror::Class::kStatusInitialized);
- __ B(lt, slow_path->GetEntryLabel());
+ size_t status_offset = mirror::Class::StatusOffset().SizeValue();
+
// Even if the initialized flag is set, we need to ensure consistent memory ordering.
- __ Dmb(InnerShareable, BarrierReads);
+ if (kUseAcquireRelease) {
+ // TODO(vixl): Let the MacroAssembler handle MemOperand.
+ __ Add(temp, class_reg, status_offset);
+ __ Ldar(temp, HeapOperand(temp));
+ __ Cmp(temp, mirror::Class::kStatusInitialized);
+ __ B(lt, slow_path->GetEntryLabel());
+ } else {
+ __ Ldr(temp, HeapOperand(class_reg, status_offset));
+ __ Cmp(temp, mirror::Class::kStatusInitialized);
+ __ B(lt, slow_path->GetEntryLabel());
+ __ Dmb(InnerShareable, BarrierReads);
+ }
__ Bind(slow_path->GetExitLabel());
}
+void InstructionCodeGeneratorARM64::GenerateMemoryBarrier(MemBarrierKind kind) {
+ BarrierType type = BarrierAll;
+
+ switch (kind) {
+ case MemBarrierKind::kAnyAny:
+ case MemBarrierKind::kAnyStore: {
+ type = BarrierAll;
+ break;
+ }
+ case MemBarrierKind::kLoadAny: {
+ type = BarrierReads;
+ break;
+ }
+ case MemBarrierKind::kStoreStore: {
+ type = BarrierWrites;
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected memory barrier " << kind;
+ }
+ __ Dmb(InnerShareable, type);
+}
+
void InstructionCodeGeneratorARM64::GenerateSuspendCheck(HSuspendCheck* instruction,
HBasicBlock* successor) {
SuspendCheckSlowPathARM64* slow_path =
@@ -850,7 +1055,7 @@
codegen_(codegen) {}
#define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M) \
- M(ParallelMove) \
+ /* No unimplemented IR. */
#define UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name) name##UnimplementedInstructionBreakCode
@@ -1044,6 +1249,7 @@
}
codegen_->Load(type, OutputCPURegister(instruction), source);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
}
void LocationsBuilderARM64::VisitArrayLength(HArrayLength* instruction) {
@@ -1055,6 +1261,7 @@
void InstructionCodeGeneratorARM64::VisitArrayLength(HArrayLength* instruction) {
__ Ldr(OutputRegister(instruction),
HeapOperand(InputRegisterAt(instruction, 0), mirror::Array::LengthOffset()));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
}
void LocationsBuilderARM64::VisitArraySet(HArraySet* instruction) {
@@ -1078,7 +1285,7 @@
Primitive::Type value_type = instruction->GetComponentType();
if (value_type == Primitive::kPrimNot) {
codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), instruction, instruction->GetDexPc());
-
+ CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
} else {
LocationSummary* locations = instruction->GetLocations();
Register obj = InputRegisterAt(instruction, 0);
@@ -1099,6 +1306,7 @@
}
codegen_->Store(value_type, value, destination);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
}
}
@@ -1113,7 +1321,9 @@
}
void InstructionCodeGeneratorARM64::VisitBoundsCheck(HBoundsCheck* instruction) {
- BoundsCheckSlowPathARM64* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM64();
+ LocationSummary* locations = instruction->GetLocations();
+ BoundsCheckSlowPathARM64* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM64(
+ instruction, locations->InAt(0), locations->InAt(1));
codegen_->AddSlowPath(slow_path);
__ Cmp(InputRegisterAt(instruction, 0), InputOperandAt(instruction, 1));
@@ -1125,22 +1335,24 @@
instruction, LocationSummary::kCallOnSlowPath);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
}
void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) {
- UseScratchRegisterScope temps(GetVIXLAssembler());
+ LocationSummary* locations = instruction->GetLocations();
Register obj = InputRegisterAt(instruction, 0);;
Register cls = InputRegisterAt(instruction, 1);;
- Register temp = temps.AcquireW();
+ Register obj_cls = WRegisterFrom(instruction->GetLocations()->GetTemp(0));
- SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM64();
+ SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM64(
+ instruction, locations->InAt(1), LocationFrom(obj_cls), instruction->GetDexPc());
codegen_->AddSlowPath(slow_path);
// TODO: avoid this check if we know obj is not null.
__ Cbz(obj, slow_path->GetExitLabel());
// Compare the class of `obj` with `cls`.
- __ Ldr(temp, HeapOperand(obj, mirror::Object::ClassOffset()));
- __ Cmp(temp, cls);
+ __ Ldr(obj_cls, HeapOperand(obj, mirror::Object::ClassOffset()));
+ __ Cmp(obj_cls, cls);
__ B(ne, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
}
@@ -1316,12 +1528,20 @@
codegen_->AddSlowPath(slow_path);
Location value = instruction->GetLocations()->InAt(0);
+ Primitive::Type type = instruction->GetType();
+
+ if ((type != Primitive::kPrimInt) && (type != Primitive::kPrimLong)) {
+ LOG(FATAL) << "Unexpected type " << type << "for DivZeroCheck.";
+ return;
+ }
+
if (value.IsConstant()) {
int64_t divisor = Int64ConstantFrom(value);
if (divisor == 0) {
__ B(slow_path->GetEntryLabel());
} else {
- LOG(FATAL) << "Divisions by non-null constants should have been optimized away.";
+ // A division by a non-null constant is valid. We don't need to perform
+ // any check, so simply fall through.
}
} else {
__ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
@@ -1438,28 +1658,60 @@
}
void LocationsBuilderARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
void InstructionCodeGeneratorARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
MemOperand field = HeapOperand(InputRegisterAt(instruction, 0), instruction->GetFieldOffset());
- codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), field);
+
+ if (instruction->IsVolatile()) {
+ if (kUseAcquireRelease) {
+ // NB: LoadAcquire will record the pc info if needed.
+ codegen_->LoadAcquire(instruction, OutputCPURegister(instruction), field);
+ } else {
+ codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), field);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ // For IRIW sequential consistency kLoadAny is not sufficient.
+ GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+ }
+ } else {
+ codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), field);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
}
void LocationsBuilderARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
}
void InstructionCodeGeneratorARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- Primitive::Type field_type = instruction->GetFieldType();
- CPURegister value = InputCPURegisterAt(instruction, 1);
Register obj = InputRegisterAt(instruction, 0);
- codegen_->Store(field_type, value, HeapOperand(obj, instruction->GetFieldOffset()));
- if (field_type == Primitive::kPrimNot) {
+ CPURegister value = InputCPURegisterAt(instruction, 1);
+ Offset offset = instruction->GetFieldOffset();
+ Primitive::Type field_type = instruction->GetFieldType();
+
+ if (instruction->IsVolatile()) {
+ if (kUseAcquireRelease) {
+ codegen_->StoreRelease(field_type, value, HeapOperand(obj, offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ } else {
+ GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
+ codegen_->Store(field_type, value, HeapOperand(obj, offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+ }
+ } else {
+ codegen_->Store(field_type, value, HeapOperand(obj, offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
+
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
codegen_->MarkGCCard(obj, Register(value));
}
}
@@ -1470,7 +1722,8 @@
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), true); // The output does overlap inputs.
+ // The output does overlap inputs.
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
}
void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) {
@@ -1496,7 +1749,8 @@
// If the classes are not equal, we go into a slow path.
DCHECK(locations->OnlyCallsOnSlowPath());
SlowPathCodeARM64* slow_path =
- new (GetGraph()->GetArena()) TypeCheckSlowPathARM64();
+ new (GetGraph()->GetArena()) TypeCheckSlowPathARM64(
+ instruction, locations->InAt(1), locations->Out(), instruction->GetDexPc());
codegen_->AddSlowPath(slow_path);
__ B(ne, slow_path->GetEntryLabel());
__ Mov(out, 1);
@@ -1559,6 +1813,7 @@
} else {
__ Ldr(temp, HeapOperandFrom(receiver, class_offset));
}
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
// temp = temp->GetImtEntryAt(method_offset);
__ Ldr(temp, HeapOperand(temp, method_offset));
// lr = temp->GetEntryPoint();
@@ -1570,19 +1825,37 @@
}
void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+ IntrinsicLocationsBuilderARM64 intrinsic(GetGraph()->GetArena());
+ if (intrinsic.TryDispatch(invoke)) {
+ return;
+ }
+
HandleInvoke(invoke);
}
-void LocationsBuilderARM64::VisitInvokeStatic(HInvokeStatic* invoke) {
+void LocationsBuilderARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+ IntrinsicLocationsBuilderARM64 intrinsic(GetGraph()->GetArena());
+ if (intrinsic.TryDispatch(invoke)) {
+ return;
+ }
+
HandleInvoke(invoke);
}
-void InstructionCodeGeneratorARM64::VisitInvokeStatic(HInvokeStatic* invoke) {
- Register temp = WRegisterFrom(invoke->GetLocations()->GetTemp(0));
- // Make sure that ArtMethod* is passed in W0 as per the calling convention
- DCHECK(temp.Is(w0));
+static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM64* codegen) {
+ if (invoke->GetLocations()->Intrinsified()) {
+ IntrinsicCodeGeneratorARM64 intrinsic(codegen);
+ intrinsic.Dispatch(invoke);
+ return true;
+ }
+ return false;
+}
+
+void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Register temp) {
+ // Make sure that ArtMethod* is passed in kArtMethodRegister as per the calling convention.
+ DCHECK(temp.Is(kArtMethodRegister));
size_t index_in_cache = mirror::Array::DataOffset(kHeapRefSize).SizeValue() +
- invoke->GetIndexInDexCache() * kHeapRefSize;
+ invoke->GetDexMethodIndex() * kHeapRefSize;
// TODO: Implement all kinds of calls:
// 1) boot -> boot
@@ -1592,22 +1865,39 @@
// Currently we implement the app -> app logic, which looks up in the resolve cache.
// temp = method;
- codegen_->LoadCurrentMethod(temp);
- // temp = temp->dex_cache_resolved_methods_;
- __ Ldr(temp, HeapOperand(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset()));
- // temp = temp[index_in_cache];
- __ Ldr(temp, HeapOperand(temp, index_in_cache));
- // lr = temp->entry_point_from_quick_compiled_code_;
- __ Ldr(lr, HeapOperand(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
- kArm64WordSize)));
- // lr();
- __ Blr(lr);
+ LoadCurrentMethod(temp);
+ if (!invoke->IsRecursive()) {
+ // temp = temp->dex_cache_resolved_methods_;
+ __ Ldr(temp, HeapOperand(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset()));
+ // temp = temp[index_in_cache];
+ __ Ldr(temp, HeapOperand(temp, index_in_cache));
+ // lr = temp->entry_point_from_quick_compiled_code_;
+ __ Ldr(lr, HeapOperand(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kArm64WordSize)));
+ // lr();
+ __ Blr(lr);
+ } else {
+ __ Bl(&frame_entry_label_);
+ }
- codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
- DCHECK(!codegen_->IsLeafMethod());
+ RecordPcInfo(invoke, invoke->GetDexPc());
+ DCHECK(!IsLeafMethod());
+}
+
+void InstructionCodeGeneratorARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+ if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+ return;
+ }
+
+ Register temp = WRegisterFrom(invoke->GetLocations()->GetTemp(0));
+ codegen_->GenerateStaticOrDirectCall(invoke, temp);
}
void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+ if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+ return;
+ }
+
LocationSummary* locations = invoke->GetLocations();
Location receiver = locations->InAt(0);
Register temp = WRegisterFrom(invoke->GetLocations()->GetTemp(0));
@@ -1624,6 +1914,7 @@
DCHECK(receiver.IsRegister());
__ Ldr(temp, HeapOperandFrom(receiver, class_offset));
}
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
// temp = temp->GetMethodAt(method_offset);
__ Ldr(temp, HeapOperand(temp, method_offset));
// lr = temp->GetEntryPoint();
@@ -1736,6 +2027,7 @@
? QUICK_ENTRY_POINT(pLockObject) : QUICK_ENTRY_POINT(pUnlockObject),
instruction,
instruction->GetDexPc());
+ CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
}
void LocationsBuilderARM64::VisitMul(HMul* mul) {
@@ -1821,9 +2113,11 @@
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
- locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1)));
+ locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
locations->SetOut(LocationFrom(x0));
- locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(2)));
+ locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1)));
+ CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck,
+ void*, uint32_t, int32_t, mirror::ArtMethod*>();
}
void InstructionCodeGeneratorARM64::VisitNewArray(HNewArray* instruction) {
@@ -1832,11 +2126,15 @@
Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt);
DCHECK(type_index.Is(w0));
Register current_method = RegisterFrom(locations->GetTemp(1), Primitive::kPrimNot);
- DCHECK(current_method.Is(w1));
+ DCHECK(current_method.Is(w2));
codegen_->LoadCurrentMethod(current_method);
__ Mov(type_index, instruction->GetTypeIndex());
codegen_->InvokeRuntime(
- QUICK_ENTRY_POINT(pAllocArrayWithAccessCheck), instruction, instruction->GetDexPc());
+ GetThreadOffset<kArm64WordSize>(instruction->GetEntrypoint()).Int32Value(),
+ instruction,
+ instruction->GetDexPc());
+ CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck,
+ void*, uint32_t, int32_t, mirror::ArtMethod*>();
}
void LocationsBuilderARM64::VisitNewInstance(HNewInstance* instruction) {
@@ -1846,6 +2144,7 @@
locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1)));
locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
+ CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, mirror::ArtMethod*>();
}
void InstructionCodeGeneratorARM64::VisitNewInstance(HNewInstance* instruction) {
@@ -1857,7 +2156,10 @@
codegen_->LoadCurrentMethod(current_method);
__ Mov(type_index, instruction->GetTypeIndex());
codegen_->InvokeRuntime(
- QUICK_ENTRY_POINT(pAllocObjectWithAccessCheck), instruction, instruction->GetDexPc());
+ GetThreadOffset<kArm64WordSize>(instruction->GetEntrypoint()).Int32Value(),
+ instruction,
+ instruction->GetDexPc());
+ CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, mirror::ArtMethod*>();
}
void LocationsBuilderARM64::VisitNot(HNot* instruction) {
@@ -1868,10 +2170,6 @@
void InstructionCodeGeneratorARM64::VisitNot(HNot* instruction) {
switch (instruction->InputAt(0)->GetType()) {
- case Primitive::kPrimBoolean:
- __ Eor(OutputRegister(instruction), InputRegisterAt(instruction, 0), Operand(1));
- break;
-
case Primitive::kPrimInt:
case Primitive::kPrimLong:
__ Mvn(OutputRegister(instruction), InputOperandAt(instruction, 0));
@@ -1891,18 +2189,31 @@
}
}
-void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) {
+void InstructionCodeGeneratorARM64::GenerateImplicitNullCheck(HNullCheck* instruction) {
+ if (codegen_->CanMoveNullCheckToUser(instruction)) {
+ return;
+ }
+ Location obj = instruction->GetLocations()->InAt(0);
+
+ __ Ldr(wzr, HeapOperandFrom(obj, Offset(0)));
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+}
+
+void InstructionCodeGeneratorARM64::GenerateExplicitNullCheck(HNullCheck* instruction) {
SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM64(instruction);
codegen_->AddSlowPath(slow_path);
LocationSummary* locations = instruction->GetLocations();
Location obj = locations->InAt(0);
- if (obj.IsRegister()) {
- __ Cbz(RegisterFrom(obj, instruction->InputAt(0)->GetType()), slow_path->GetEntryLabel());
+
+ __ Cbz(RegisterFrom(obj, instruction->InputAt(0)->GetType()), slow_path->GetEntryLabel());
+}
+
+void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) {
+ if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
+ GenerateImplicitNullCheck(instruction);
} else {
- DCHECK(obj.IsConstant()) << obj;
- DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0);
- __ B(slow_path->GetEntryLabel());
+ GenerateExplicitNullCheck(instruction);
}
}
@@ -1914,6 +2225,14 @@
HandleBinaryOp(instruction);
}
+void LocationsBuilderARM64::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
+ LOG(FATAL) << "Unreachable";
+}
+
+void InstructionCodeGeneratorARM64::VisitParallelMove(HParallelMove* instruction) {
+ codegen_->GetMoveResolver()->EmitNativeCode(instruction);
+}
+
void LocationsBuilderARM64::VisitParameterValue(HParameterValue* instruction) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
@@ -1944,9 +2263,12 @@
}
void LocationsBuilderARM64::VisitRem(HRem* rem) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(rem, LocationSummary::kNoCall);
- switch (rem->GetResultType()) {
+ Primitive::Type type = rem->GetResultType();
+ LocationSummary::CallKind call_kind =
+ Primitive::IsFloatingPointType(type) ? LocationSummary::kCall : LocationSummary::kNoCall;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind);
+
+ switch (type) {
case Primitive::kPrimInt:
case Primitive::kPrimLong:
locations->SetInAt(0, Location::RequiresRegister());
@@ -1954,13 +2276,24 @@
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
break;
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble: {
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
+ locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
+ locations->SetOut(calling_convention.GetReturnLocation(type));
+
+ break;
+ }
+
default:
- LOG(FATAL) << "Unexpected rem type " << rem->GetResultType();
+ LOG(FATAL) << "Unexpected rem type " << type;
}
}
void InstructionCodeGeneratorARM64::VisitRem(HRem* rem) {
Primitive::Type type = rem->GetResultType();
+
switch (type) {
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
@@ -1975,6 +2308,14 @@
break;
}
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble: {
+ int32_t entry_offset = (type == Primitive::kPrimFloat) ? QUICK_ENTRY_POINT(pFmodf)
+ : QUICK_ENTRY_POINT(pFmod);
+ codegen_->InvokeRuntime(entry_offset, rem, rem->GetDexPc());
+ break;
+ }
+
default:
LOG(FATAL) << "Unexpected rem type " << type;
}
@@ -1989,7 +2330,7 @@
void InstructionCodeGeneratorARM64::VisitReturn(HReturn* instruction) {
UNUSED(instruction);
codegen_->GenerateFrameExit();
- __ Br(lr);
+ __ Ret();
}
void LocationsBuilderARM64::VisitReturnVoid(HReturnVoid* instruction) {
@@ -1999,7 +2340,7 @@
void InstructionCodeGeneratorARM64::VisitReturnVoid(HReturnVoid* instruction) {
UNUSED(instruction);
codegen_->GenerateFrameExit();
- __ Br(lr);
+ __ Ret();
}
void LocationsBuilderARM64::VisitShl(HShl* shl) {
@@ -2063,7 +2404,19 @@
void InstructionCodeGeneratorARM64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
MemOperand field = HeapOperand(InputRegisterAt(instruction, 0), instruction->GetFieldOffset());
- codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), field);
+
+ if (instruction->IsVolatile()) {
+ if (kUseAcquireRelease) {
+ // NB: LoadAcquire will record the pc info if needed.
+ codegen_->LoadAcquire(instruction, OutputCPURegister(instruction), field);
+ } else {
+ codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), field);
+ // For IRIW sequential consistency kLoadAny is not sufficient.
+ GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+ }
+ } else {
+ codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), field);
+ }
}
void LocationsBuilderARM64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
@@ -2074,13 +2427,24 @@
}
void InstructionCodeGeneratorARM64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- CPURegister value = InputCPURegisterAt(instruction, 1);
Register cls = InputRegisterAt(instruction, 0);
+ CPURegister value = InputCPURegisterAt(instruction, 1);
Offset offset = instruction->GetFieldOffset();
Primitive::Type field_type = instruction->GetFieldType();
- codegen_->Store(field_type, value, HeapOperand(cls, offset));
- if (field_type == Primitive::kPrimNot) {
+ if (instruction->IsVolatile()) {
+ if (kUseAcquireRelease) {
+ codegen_->StoreRelease(field_type, value, HeapOperand(cls, offset));
+ } else {
+ GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
+ codegen_->Store(field_type, value, HeapOperand(cls, offset));
+ GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+ }
+ } else {
+ codegen_->Store(field_type, value, HeapOperand(cls, offset));
+ }
+
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
codegen_->MarkGCCard(cls, Register(value));
}
}
@@ -2122,6 +2486,7 @@
void InstructionCodeGeneratorARM64::VisitThrow(HThrow* instruction) {
codegen_->InvokeRuntime(
QUICK_ENTRY_POINT(pDeliverException), instruction, instruction->GetDexPc());
+ CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
}
void LocationsBuilderARM64::VisitTypeConversion(HTypeConversion* conversion) {
@@ -2135,13 +2500,13 @@
LOG(FATAL) << "Unexpected type conversion from " << input_type << " to " << result_type;
}
- if (IsFPType(input_type)) {
+ if (Primitive::IsFloatingPointType(input_type)) {
locations->SetInAt(0, Location::RequiresFpuRegister());
} else {
locations->SetInAt(0, Location::RequiresRegister());
}
- if (IsFPType(result_type)) {
+ if (Primitive::IsFloatingPointType(result_type)) {
locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
} else {
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
@@ -2154,25 +2519,27 @@
DCHECK_NE(input_type, result_type);
- if (IsIntegralType(result_type) && IsIntegralType(input_type)) {
+ if (Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type)) {
int result_size = Primitive::ComponentSize(result_type);
int input_size = Primitive::ComponentSize(input_type);
- int min_size = kBitsPerByte * std::min(result_size, input_size);
+ int min_size = std::min(result_size, input_size);
Register output = OutputRegister(conversion);
Register source = InputRegisterAt(conversion, 0);
- if ((result_type == Primitive::kPrimChar) ||
- ((input_type == Primitive::kPrimChar) && (result_size > input_size))) {
- __ Ubfx(output, output.IsX() ? source.X() : source.W(), 0, min_size);
+ if ((result_type == Primitive::kPrimChar) && (input_size < result_size)) {
+ __ Ubfx(output, source, 0, result_size * kBitsPerByte);
+ } else if ((result_type == Primitive::kPrimChar) ||
+ ((input_type == Primitive::kPrimChar) && (result_size > input_size))) {
+ __ Ubfx(output, output.IsX() ? source.X() : source.W(), 0, min_size * kBitsPerByte);
} else {
- __ Sbfx(output, output.IsX() ? source.X() : source.W(), 0, min_size);
+ __ Sbfx(output, output.IsX() ? source.X() : source.W(), 0, min_size * kBitsPerByte);
}
- } else if (IsFPType(result_type) && IsIntegralType(input_type)) {
- CHECK(input_type == Primitive::kPrimInt || input_type == Primitive::kPrimLong);
+ } else if (Primitive::IsFloatingPointType(result_type) && Primitive::IsIntegralType(input_type)) {
__ Scvtf(OutputFPRegister(conversion), InputRegisterAt(conversion, 0));
- } else if (IsIntegralType(result_type) && IsFPType(input_type)) {
+ } else if (Primitive::IsIntegralType(result_type) && Primitive::IsFloatingPointType(input_type)) {
CHECK(result_type == Primitive::kPrimInt || result_type == Primitive::kPrimLong);
__ Fcvtzs(OutputRegister(conversion), InputFPRegisterAt(conversion, 0));
- } else if (IsFPType(result_type) && IsFPType(input_type)) {
+ } else if (Primitive::IsFloatingPointType(result_type) &&
+ Primitive::IsFloatingPointType(input_type)) {
__ Fcvt(OutputFPRegister(conversion), InputFPRegisterAt(conversion, 0));
} else {
LOG(FATAL) << "Unexpected or unimplemented type conversion from " << input_type
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 0e3d25f..2e937e2 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -18,6 +18,8 @@
#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_
#include "code_generator.h"
+#include "dex/compiler_enums.h"
+#include "driver/compiler_options.h"
#include "nodes.h"
#include "parallel_move_resolver.h"
#include "utils/arm64/assembler_arm64.h"
@@ -29,7 +31,10 @@
namespace arm64 {
class CodeGeneratorARM64;
-class SlowPathCodeARM64;
+
+// TODO: Tune the use of Load-Acquire, Store-Release vs Data Memory Barriers.
+// For now we prefer the use of load-acquire, store-release over explicit memory barriers.
+static constexpr bool kUseAcquireRelease = true;
// Use a local definition to prevent copying mistakes.
static constexpr size_t kArm64WordSize = kArm64PointerSize;
@@ -43,17 +48,42 @@
};
static constexpr size_t kParameterFPRegistersLength = arraysize(kParameterFPRegisters);
-const vixl::Register tr = vixl::x18; // Thread Register
+const vixl::Register tr = vixl::x18; // Thread Register
+static const vixl::Register kArtMethodRegister = vixl::w0; // Method register on invoke.
+const vixl::Register kQuickSuspendRegister = vixl::x19;
const vixl::CPURegList vixl_reserved_core_registers(vixl::ip0, vixl::ip1);
const vixl::CPURegList vixl_reserved_fp_registers(vixl::d31);
-const vixl::CPURegList runtime_reserved_core_registers(tr, vixl::lr);
-const vixl::CPURegList quick_callee_saved_registers(vixl::CPURegister::kRegister,
- vixl::kXRegSize,
- kArm64CalleeSaveRefSpills);
+// TODO: When the runtime does not use kQuickSuspendRegister as a suspend
+// counter remove it from the reserved registers list.
+const vixl::CPURegList runtime_reserved_core_registers(tr, kQuickSuspendRegister, vixl::lr);
+
+// Callee-saved registers defined by AAPCS64.
+const vixl::CPURegList callee_saved_core_registers(vixl::CPURegister::kRegister,
+ vixl::kXRegSize,
+ vixl::x19.code(),
+ vixl::x30.code());
+const vixl::CPURegList callee_saved_fp_registers(vixl::CPURegister::kFPRegister,
+ vixl::kDRegSize,
+ vixl::d8.code(),
+ vixl::d15.code());
Location ARM64ReturnLocation(Primitive::Type return_type);
+class SlowPathCodeARM64 : public SlowPathCode {
+ public:
+ SlowPathCodeARM64() : entry_label_(), exit_label_() {}
+
+ vixl::Label* GetEntryLabel() { return &entry_label_; }
+ vixl::Label* GetExitLabel() { return &exit_label_; }
+
+ private:
+ vixl::Label entry_label_;
+ vixl::Label exit_label_;
+
+ DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM64);
+};
+
class InvokeDexCallingConvention : public CallingConvention<vixl::Register, vixl::FPRegister> {
public:
InvokeDexCallingConvention()
@@ -108,9 +138,12 @@
private:
void GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path, vixl::Register class_reg);
+ void GenerateMemoryBarrier(MemBarrierKind kind);
void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
void HandleBinaryOp(HBinaryOperation* instr);
void HandleShift(HBinaryOperation* instr);
+ void GenerateImplicitNullCheck(HNullCheck* instruction);
+ void GenerateExplicitNullCheck(HNullCheck* instruction);
Arm64Assembler* const assembler_;
CodeGeneratorARM64* const codegen_;
@@ -139,21 +172,43 @@
DISALLOW_COPY_AND_ASSIGN(LocationsBuilderARM64);
};
+class ParallelMoveResolverARM64 : public ParallelMoveResolver {
+ public:
+ ParallelMoveResolverARM64(ArenaAllocator* allocator, CodeGeneratorARM64* codegen)
+ : ParallelMoveResolver(allocator), codegen_(codegen) {}
+
+ void EmitMove(size_t index) OVERRIDE;
+ void EmitSwap(size_t index) OVERRIDE;
+ void RestoreScratch(int reg) OVERRIDE;
+ void SpillScratch(int reg) OVERRIDE;
+
+ private:
+ Arm64Assembler* GetAssembler() const;
+ vixl::MacroAssembler* GetVIXLAssembler() const {
+ return GetAssembler()->vixl_masm_;
+ }
+
+ CodeGeneratorARM64* const codegen_;
+
+ DISALLOW_COPY_AND_ASSIGN(ParallelMoveResolverARM64);
+};
+
class CodeGeneratorARM64 : public CodeGenerator {
public:
- explicit CodeGeneratorARM64(HGraph* graph);
+ CodeGeneratorARM64(HGraph* graph, const CompilerOptions& compiler_options);
virtual ~CodeGeneratorARM64() {}
void GenerateFrameEntry() OVERRIDE;
void GenerateFrameExit() OVERRIDE;
- static const vixl::CPURegList& GetFramePreservedRegisters() {
- static const vixl::CPURegList frame_preserved_regs =
- vixl::CPURegList(vixl::CPURegister::kRegister, vixl::kXRegSize, vixl::lr.Bit());
- return frame_preserved_regs;
+ vixl::CPURegList GetFramePreservedCoreRegisters() const {
+ return vixl::CPURegList(vixl::CPURegister::kRegister, vixl::kXRegSize,
+ core_spill_mask_);
}
- static int GetFramePreservedRegistersSize() {
- return GetFramePreservedRegisters().TotalSizeInBytes();
+
+ vixl::CPURegList GetFramePreservedFPRegisters() const {
+ return vixl::CPURegList(vixl::CPURegister::kFPRegister, vixl::kDRegSize,
+ fpu_spill_mask_);
}
void Bind(HBasicBlock* block) OVERRIDE;
@@ -168,14 +223,17 @@
return kArm64WordSize;
}
+ size_t GetFloatingPointSpillSlotSize() const OVERRIDE {
+ // Allocated in D registers, which are word sized.
+ return kArm64WordSize;
+ }
+
uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE {
vixl::Label* block_entry_label = GetLabelOf(block);
DCHECK(block_entry_label->IsBound());
return block_entry_label->location();
}
- size_t FrameEntrySpillSize() const OVERRIDE;
-
HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; }
HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; }
Arm64Assembler* GetAssembler() OVERRIDE { return &assembler_; }
@@ -186,26 +244,17 @@
// Register allocation.
- void SetupBlockedRegisters() const OVERRIDE;
+ void SetupBlockedRegisters(bool is_baseline) const OVERRIDE;
// AllocateFreeRegister() is only used when allocating registers locally
// during CompileBaseline().
Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE;
Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
- size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
- UNUSED(stack_index);
- UNUSED(reg_id);
- LOG(INFO) << "CodeGeneratorARM64::SaveCoreRegister()";
- return kArm64WordSize;
- }
-
- size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
- UNUSED(stack_index);
- UNUSED(reg_id);
- LOG(INFO) << "CodeGeneratorARM64::RestoreCoreRegister()";
- return kArm64WordSize;
- }
+ size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id);
+ size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id);
+ size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id);
+ size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id);
// The number of registers that can be allocated. The register allocator may
// decide to reserve and not use a few of them.
@@ -237,30 +286,45 @@
// Code generation helpers.
void MoveConstant(vixl::CPURegister destination, HConstant* constant);
- void MoveHelper(Location destination, Location source, Primitive::Type type);
+ // The type is optional. When specified it must be coherent with the
+ // locations, and is used for optimisation and debugging.
+ void MoveLocation(Location destination, Location source,
+ Primitive::Type type = Primitive::kPrimVoid);
+ void SwapLocations(Location loc_1, Location loc_2);
void Load(Primitive::Type type, vixl::CPURegister dst, const vixl::MemOperand& src);
void Store(Primitive::Type type, vixl::CPURegister rt, const vixl::MemOperand& dst);
void LoadCurrentMethod(vixl::Register current_method);
+ void LoadAcquire(HInstruction* instruction, vixl::CPURegister dst, const vixl::MemOperand& src);
+ void StoreRelease(Primitive::Type type, vixl::CPURegister rt, const vixl::MemOperand& dst);
// Generate code to invoke a runtime entry point.
void InvokeRuntime(int32_t offset, HInstruction* instruction, uint32_t dex_pc);
- ParallelMoveResolver* GetMoveResolver() OVERRIDE {
- UNIMPLEMENTED(INFO) << "TODO: MoveResolver";
- return nullptr;
+ ParallelMoveResolverARM64* GetMoveResolver() { return &move_resolver_; }
+
+ bool NeedsTwoRegisters(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+ return false;
}
+ void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, vixl::Register temp);
+
private:
// Labels for each block that will be compiled.
vixl::Label* block_labels_;
+ vixl::Label frame_entry_label_;
LocationsBuilderARM64 location_builder_;
InstructionCodeGeneratorARM64 instruction_visitor_;
+ ParallelMoveResolverARM64 move_resolver_;
Arm64Assembler assembler_;
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM64);
};
+inline Arm64Assembler* ParallelMoveResolverARM64::GetAssembler() const {
+ return codegen_->GetAssembler();
+}
+
} // namespace arm64
} // namespace art
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 6f83d9f..1a95f41 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -17,6 +17,7 @@
#include "code_generator_x86.h"
#include "entrypoints/quick/quick_entrypoints.h"
+#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "gc/accounting/card_table.h"
#include "mirror/array-inl.h"
#include "mirror/art_method.h"
@@ -31,19 +32,20 @@
namespace x86 {
-static constexpr bool kExplicitStackOverflowCheck = false;
-
-static constexpr int kNumberOfPushedRegistersAtEntry = 1;
static constexpr int kCurrentMethodStackOffset = 0;
static constexpr Register kRuntimeParameterCoreRegisters[] = { EAX, ECX, EDX, EBX };
static constexpr size_t kRuntimeParameterCoreRegistersLength =
arraysize(kRuntimeParameterCoreRegisters);
-static constexpr XmmRegister kRuntimeParameterFpuRegisters[] = { };
-static constexpr size_t kRuntimeParameterFpuRegistersLength = 0;
+static constexpr XmmRegister kRuntimeParameterFpuRegisters[] = { XMM0, XMM1, XMM2, XMM3 };
+static constexpr size_t kRuntimeParameterFpuRegistersLength =
+ arraysize(kRuntimeParameterFpuRegisters);
+
+static constexpr int kC2ConditionMask = 0x400;
// Marker for places that can be updated once we don't follow the quick ABI.
static constexpr bool kFollowsQuickABI = true;
+static constexpr int kFakeReturnRegister = Register(8);
class InvokeRuntimeCallingConvention : public CallingConvention<Register, XmmRegister> {
public:
@@ -123,21 +125,6 @@
DISALLOW_COPY_AND_ASSIGN(DivRemMinusOneSlowPathX86);
};
-class StackOverflowCheckSlowPathX86 : public SlowPathCodeX86 {
- public:
- StackOverflowCheckSlowPathX86() {}
-
- virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
- __ Bind(GetEntryLabel());
- __ addl(ESP,
- Immediate(codegen->GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize));
- __ fs()->jmp(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowStackOverflow)));
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86);
-};
-
class BoundsCheckSlowPathX86 : public SlowPathCodeX86 {
public:
BoundsCheckSlowPathX86(HBoundsCheck* instruction,
@@ -215,8 +202,8 @@
codegen->SaveLiveRegisters(locations);
InvokeRuntimeCallingConvention calling_convention;
- x86_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(0));
- __ movl(calling_convention.GetRegisterAt(1), Immediate(instruction_->GetStringIndex()));
+ x86_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+ __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction_->GetStringIndex()));
__ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pResolveString)));
codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
x86_codegen->Move32(locations->Out(), Location::RegisterLocation(EAX));
@@ -373,15 +360,25 @@
return kX86WordSize;
}
-CodeGeneratorX86::CodeGeneratorX86(HGraph* graph)
- : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfXmmRegisters, kNumberOfRegisterPairs),
+size_t CodeGeneratorX86::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
+ __ movsd(Address(ESP, stack_index), XmmRegister(reg_id));
+ return GetFloatingPointSpillSlotSize();
+}
+
+size_t CodeGeneratorX86::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
+ __ movsd(XmmRegister(reg_id), Address(ESP, stack_index));
+ return GetFloatingPointSpillSlotSize();
+}
+
+CodeGeneratorX86::CodeGeneratorX86(HGraph* graph, const CompilerOptions& compiler_options)
+ : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfXmmRegisters,
+ kNumberOfRegisterPairs, (1 << kFakeReturnRegister), 0, compiler_options),
block_labels_(graph->GetArena(), 0),
location_builder_(graph, this),
instruction_visitor_(graph, this),
- move_resolver_(graph->GetArena(), this) {}
-
-size_t CodeGeneratorX86::FrameEntrySpillSize() const {
- return kNumberOfPushedRegistersAtEntry * kX86WordSize;
+ move_resolver_(graph->GetArena(), this) {
+ // Use a fake return address register to mimic Quick.
+ AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister));
}
Location CodeGeneratorX86::AllocateFreeRegister(Primitive::Type type) const {
@@ -430,7 +427,7 @@
return Location();
}
-void CodeGeneratorX86::SetupBlockedRegisters() const {
+void CodeGeneratorX86::SetupBlockedRegisters(bool is_baseline ATTRIBUTE_UNUSED) const {
// Don't allocate the dalvik style register pair passing.
blocked_register_pairs_[ECX_EDX] = true;
@@ -463,33 +460,26 @@
codegen_(codegen) {}
void CodeGeneratorX86::GenerateFrameEntry() {
- // Create a fake register to mimic Quick.
- static const int kFakeReturnRegister = 8;
- core_spill_mask_ |= (1 << kFakeReturnRegister);
-
+ __ Bind(&frame_entry_label_);
bool skip_overflow_check =
IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86);
- if (!skip_overflow_check && !kExplicitStackOverflowCheck) {
+ DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
+
+ if (!skip_overflow_check) {
__ testl(EAX, Address(ESP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86))));
RecordPcInfo(nullptr, 0);
}
- // The return PC has already been pushed on the stack.
- __ subl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize));
-
- if (!skip_overflow_check && kExplicitStackOverflowCheck) {
- SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86();
- AddSlowPath(slow_path);
-
- __ fs()->cmpl(ESP, Address::Absolute(Thread::StackEndOffset<kX86WordSize>()));
- __ j(kLess, slow_path->GetEntryLabel());
+ if (!HasEmptyFrame()) {
+ __ subl(ESP, Immediate(GetFrameSize() - FrameEntrySpillSize()));
+ __ movl(Address(ESP, kCurrentMethodStackOffset), EAX);
}
-
- __ movl(Address(ESP, kCurrentMethodStackOffset), EAX);
}
void CodeGeneratorX86::GenerateFrameExit() {
- __ addl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize));
+ if (!HasEmptyFrame()) {
+ __ addl(ESP, Immediate(GetFrameSize() - FrameEntrySpillSize()));
+ }
}
void CodeGeneratorX86::Bind(HBasicBlock* block) {
@@ -497,6 +487,7 @@
}
void CodeGeneratorX86::LoadCurrentMethod(Register reg) {
+ DCHECK(RequiresCurrentMethod());
__ movl(reg, Address(ESP, kCurrentMethodStackOffset));
}
@@ -531,30 +522,46 @@
case Primitive::kPrimChar:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
- case Primitive::kPrimFloat:
case Primitive::kPrimNot: {
uint32_t index = gp_index_++;
+ stack_index_++;
if (index < calling_convention.GetNumberOfRegisters()) {
return Location::RegisterLocation(calling_convention.GetRegisterAt(index));
} else {
- return Location::StackSlot(calling_convention.GetStackOffsetOf(index));
+ return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index_ - 1));
}
}
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble: {
+ case Primitive::kPrimLong: {
uint32_t index = gp_index_;
gp_index_ += 2;
+ stack_index_ += 2;
if (index + 1 < calling_convention.GetNumberOfRegisters()) {
X86ManagedRegister pair = X86ManagedRegister::FromRegisterPair(
calling_convention.GetRegisterPairAt(index));
return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh());
- } else if (index + 1 == calling_convention.GetNumberOfRegisters()) {
- // On X86, the register index and stack index of a quick parameter is the same, since
- // we are passing floating pointer values in core registers.
- return Location::QuickParameter(index, index);
} else {
- return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(index));
+ return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index_ - 2));
+ }
+ }
+
+ case Primitive::kPrimFloat: {
+ uint32_t index = fp_index_++;
+ stack_index_++;
+ if (index < calling_convention.GetNumberOfFpuRegisters()) {
+ return Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(index));
+ } else {
+ return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index_ - 1));
+ }
+ }
+
+ case Primitive::kPrimDouble: {
+ uint32_t index = fp_index_++;
+ stack_index_ += 2;
+ if (index < calling_convention.GetNumberOfFpuRegisters()) {
+ return Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(index));
+ } else {
+ return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index_ - 2));
}
}
@@ -593,6 +600,16 @@
__ movl(Address(ESP, destination.GetStackIndex()), source.AsRegister<Register>());
} else if (source.IsFpuRegister()) {
__ movss(Address(ESP, destination.GetStackIndex()), source.AsFpuRegister<XmmRegister>());
+ } else if (source.IsConstant()) {
+ HConstant* constant = source.GetConstant();
+ int32_t value;
+ if (constant->IsIntConstant()) {
+ value = constant->AsIntConstant()->GetValue();
+ } else {
+ DCHECK(constant->IsFloatConstant());
+ value = bit_cast<float, int32_t>(constant->AsFloatConstant()->GetValue());
+ }
+ __ movl(Address(ESP, destination.GetStackIndex()), Immediate(value));
} else {
DCHECK(source.IsStackSlot());
__ pushl(Address(ESP, source.GetStackIndex()));
@@ -614,16 +631,6 @@
Location::RegisterLocation(destination.AsRegisterPairLow<Register>()));
} else if (source.IsFpuRegister()) {
LOG(FATAL) << "Unimplemented";
- } else if (source.IsQuickParameter()) {
- uint16_t register_index = source.GetQuickParameterRegisterIndex();
- uint16_t stack_index = source.GetQuickParameterStackIndex();
- InvokeDexCallingConvention calling_convention;
- EmitParallelMoves(
- Location::RegisterLocation(calling_convention.GetRegisterAt(register_index)),
- Location::RegisterLocation(destination.AsRegisterPairLow<Register>()),
- Location::StackSlot(
- calling_convention.GetStackOffsetOf(stack_index + 1) + GetFrameSize()),
- Location::RegisterLocation(destination.AsRegisterPairHigh<Register>()));
} else {
// No conflict possible, so just do the moves.
DCHECK(source.IsDoubleStackSlot());
@@ -631,24 +638,10 @@
__ movl(destination.AsRegisterPairHigh<Register>(),
Address(ESP, source.GetHighStackIndex(kX86WordSize)));
}
- } else if (destination.IsQuickParameter()) {
- InvokeDexCallingConvention calling_convention;
- uint16_t register_index = destination.GetQuickParameterRegisterIndex();
- uint16_t stack_index = destination.GetQuickParameterStackIndex();
- if (source.IsRegisterPair()) {
- LOG(FATAL) << "Unimplemented";
- } else if (source.IsFpuRegister()) {
- LOG(FATAL) << "Unimplemented";
- } else {
- DCHECK(source.IsDoubleStackSlot());
- EmitParallelMoves(
- Location::StackSlot(source.GetStackIndex()),
- Location::RegisterLocation(calling_convention.GetRegisterAt(register_index)),
- Location::StackSlot(source.GetHighStackIndex(kX86WordSize)),
- Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index + 1)));
- }
} else if (destination.IsFpuRegister()) {
- if (source.IsDoubleStackSlot()) {
+ if (source.IsFpuRegister()) {
+ __ movaps(destination.AsFpuRegister<XmmRegister>(), source.AsFpuRegister<XmmRegister>());
+ } else if (source.IsDoubleStackSlot()) {
__ movsd(destination.AsFpuRegister<XmmRegister>(), Address(ESP, source.GetStackIndex()));
} else {
LOG(FATAL) << "Unimplemented";
@@ -660,18 +653,6 @@
__ movl(Address(ESP, destination.GetStackIndex()), source.AsRegisterPairLow<Register>());
__ movl(Address(ESP, destination.GetHighStackIndex(kX86WordSize)),
source.AsRegisterPairHigh<Register>());
- } else if (source.IsQuickParameter()) {
- // No conflict possible, so just do the move.
- InvokeDexCallingConvention calling_convention;
- uint16_t register_index = source.GetQuickParameterRegisterIndex();
- uint16_t stack_index = source.GetQuickParameterStackIndex();
- // Just move the low part. The only time a source is a quick parameter is
- // when moving the parameter to its stack locations. And the (Java) caller
- // of this method has already done that.
- __ movl(Address(ESP, destination.GetStackIndex()),
- calling_convention.GetRegisterAt(register_index));
- DCHECK_EQ(calling_convention.GetStackOffsetOf(stack_index + 1) + GetFrameSize(),
- static_cast<size_t>(destination.GetHighStackIndex(kX86WordSize)));
} else if (source.IsFpuRegister()) {
__ movsd(Address(ESP, destination.GetStackIndex()), source.AsFpuRegister<XmmRegister>());
} else {
@@ -1115,11 +1096,11 @@
__ ret();
}
-void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) {
+void LocationsBuilderX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
HandleInvoke(invoke);
}
-void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) {
+void InstructionCodeGeneratorX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
// TODO: Implement all kinds of calls:
@@ -1131,13 +1112,17 @@
// temp = method;
codegen_->LoadCurrentMethod(temp);
- // temp = temp->dex_cache_resolved_methods_;
- __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
- // temp = temp[index_in_cache]
- __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache())));
- // (temp + offset_of_quick_compiled_code)()
- __ call(Address(
- temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
+ if (!invoke->IsRecursive()) {
+ // temp = temp->dex_cache_resolved_methods_;
+ __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
+ // temp = temp[index_in_cache]
+ __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex())));
+ // (temp + offset_of_quick_compiled_code)()
+ __ call(Address(
+ temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
+ } else {
+ __ call(codegen_->GetFrameEntryLabel());
+ }
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
@@ -1198,6 +1183,7 @@
} else {
__ movl(temp, Address(receiver.AsRegister<Register>(), class_offset));
}
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
// temp = temp->GetMethodAt(method_offset);
__ movl(temp, Address(temp, method_offset));
// call temp->GetEntryPoint();
@@ -1211,7 +1197,7 @@
void LocationsBuilderX86::VisitInvokeInterface(HInvokeInterface* invoke) {
HandleInvoke(invoke);
// Add the hidden argument.
- invoke->GetLocations()->AddTemp(Location::FpuRegisterLocation(XMM0));
+ invoke->GetLocations()->AddTemp(Location::FpuRegisterLocation(XMM7));
}
void InstructionCodeGeneratorX86::VisitInvokeInterface(HInvokeInterface* invoke) {
@@ -1234,6 +1220,7 @@
} else {
__ movl(temp, Address(receiver.AsRegister<Register>(), class_offset));
}
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
// temp = temp->GetImtEntryAt(method_offset);
__ movl(temp, Address(temp, method_offset));
// call temp->GetEntryPoint();
@@ -1326,11 +1313,20 @@
}
void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(conversion, LocationSummary::kNoCall);
Primitive::Type result_type = conversion->GetResultType();
Primitive::Type input_type = conversion->GetInputType();
DCHECK_NE(result_type, input_type);
+
+ // The float-to-long and double-to-long type conversions rely on a
+ // call to the runtime.
+ LocationSummary::CallKind call_kind =
+ ((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble)
+ && result_type == Primitive::kPrimLong)
+ ? LocationSummary::kCall
+ : LocationSummary::kNoCall;
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
+
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
@@ -1380,8 +1376,10 @@
break;
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ // Processing a Dex `double-to-int' instruction.
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresFpuRegister());
break;
default:
@@ -1402,10 +1400,16 @@
break;
case Primitive::kPrimFloat:
- case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type << " to "
- << result_type << " not yet implemented";
- break;
+ case Primitive::kPrimDouble: {
+ // Processing a Dex `float-to-long' or 'double-to-long' instruction.
+ InvokeRuntimeCallingConvention calling_convention;
+ XmmRegister parameter = calling_convention.GetFpuRegisterAt(0);
+ locations->SetInAt(0, Location::FpuRegisterLocation(parameter));
+
+ // The runtime helper puts the result in EAX, EDX.
+ locations->SetOut(Location::RegisterPairLocation(EAX, EDX));
+ }
+ break;
default:
LOG(FATAL) << "Unexpected type conversion from " << input_type
@@ -1449,8 +1453,9 @@
break;
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ // Processing a Dex `double-to-float' instruction.
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
break;
default:
@@ -1479,8 +1484,9 @@
break;
case Primitive::kPrimFloat:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ // Processing a Dex `float-to-double' instruction.
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
break;
default:
@@ -1590,10 +1596,30 @@
break;
}
- case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ case Primitive::kPrimDouble: {
+ // Processing a Dex `double-to-int' instruction.
+ XmmRegister input = in.AsFpuRegister<XmmRegister>();
+ Register output = out.AsRegister<Register>();
+ XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ Label done, nan;
+
+ __ movl(output, Immediate(kPrimIntMax));
+ // temp = int-to-double(output)
+ __ cvtsi2sd(temp, output);
+ // if input >= temp goto done
+ __ comisd(input, temp);
+ __ j(kAboveEqual, &done);
+ // if input == NaN goto nan
+ __ j(kUnordered, &nan);
+ // output = double-to-int-truncate(input)
+ __ cvttsd2si(output, input);
+ __ jmp(&done);
+ __ Bind(&nan);
+ // output = 0
+ __ xorl(output, output);
+ __ Bind(&done);
break;
+ }
default:
LOG(FATAL) << "Unexpected type conversion from " << input_type
@@ -1615,9 +1641,15 @@
break;
case Primitive::kPrimFloat:
+ // Processing a Dex `float-to-long' instruction.
+ __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pF2l)));
+ codegen_->RecordPcInfo(conversion, conversion->GetDexPc());
+ break;
+
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type << " to "
- << result_type << " not yet implemented";
+ // Processing a Dex `double-to-long' instruction.
+ __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pD2l)));
+ codegen_->RecordPcInfo(conversion, conversion->GetDexPc());
break;
default:
@@ -1694,8 +1726,8 @@
}
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ // Processing a Dex `double-to-float' instruction.
+ __ cvtsd2ss(out.AsFpuRegister<XmmRegister>(), in.AsFpuRegister<XmmRegister>());
break;
default:
@@ -1741,8 +1773,8 @@
}
case Primitive::kPrimFloat:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ // Processing a Dex `float-to-double' instruction.
+ __ cvtss2sd(out.AsFpuRegister<XmmRegister>(), in.AsFpuRegister<XmmRegister>());
break;
default:
@@ -2015,6 +2047,81 @@
}
}
+void InstructionCodeGeneratorX86::PushOntoFPStack(Location source, uint32_t temp_offset,
+ uint32_t stack_adjustment, bool is_float) {
+ if (source.IsStackSlot()) {
+ DCHECK(is_float);
+ __ flds(Address(ESP, source.GetStackIndex() + stack_adjustment));
+ } else if (source.IsDoubleStackSlot()) {
+ DCHECK(!is_float);
+ __ fldl(Address(ESP, source.GetStackIndex() + stack_adjustment));
+ } else {
+ // Write the value to the temporary location on the stack and load to FP stack.
+ if (is_float) {
+ Location stack_temp = Location::StackSlot(temp_offset);
+ codegen_->Move32(stack_temp, source);
+ __ flds(Address(ESP, temp_offset));
+ } else {
+ Location stack_temp = Location::DoubleStackSlot(temp_offset);
+ codegen_->Move64(stack_temp, source);
+ __ fldl(Address(ESP, temp_offset));
+ }
+ }
+}
+
+void InstructionCodeGeneratorX86::GenerateRemFP(HRem *rem) {
+ Primitive::Type type = rem->GetResultType();
+ bool is_float = type == Primitive::kPrimFloat;
+ size_t elem_size = Primitive::ComponentSize(type);
+ LocationSummary* locations = rem->GetLocations();
+ Location first = locations->InAt(0);
+ Location second = locations->InAt(1);
+ Location out = locations->Out();
+
+ // Create stack space for 2 elements.
+ // TODO: enhance register allocator to ask for stack temporaries.
+ __ subl(ESP, Immediate(2 * elem_size));
+
+ // Load the values to the FP stack in reverse order, using temporaries if needed.
+ PushOntoFPStack(second, elem_size, 2 * elem_size, is_float);
+ PushOntoFPStack(first, 0, 2 * elem_size, is_float);
+
+ // Loop doing FPREM until we stabilize.
+ Label retry;
+ __ Bind(&retry);
+ __ fprem();
+
+ // Move FP status to AX.
+ __ fstsw();
+
+ // And see if the argument reduction is complete. This is signaled by the
+ // C2 FPU flag bit set to 0.
+ __ andl(EAX, Immediate(kC2ConditionMask));
+ __ j(kNotEqual, &retry);
+
+ // We have settled on the final value. Retrieve it into an XMM register.
+ // Store FP top of stack to real stack.
+ if (is_float) {
+ __ fsts(Address(ESP, 0));
+ } else {
+ __ fstl(Address(ESP, 0));
+ }
+
+ // Pop the 2 items from the FP stack.
+ __ fucompp();
+
+ // Load the value from the stack into an XMM register.
+ DCHECK(out.IsFpuRegister()) << out;
+ if (is_float) {
+ __ movss(out.AsFpuRegister<XmmRegister>(), Address(ESP, 0));
+ } else {
+ __ movsd(out.AsFpuRegister<XmmRegister>(), Address(ESP, 0));
+ }
+
+ // And remove the temporary stack space we allocated.
+ __ addl(ESP, Immediate(2 * elem_size));
+}
+
void InstructionCodeGeneratorX86::GenerateDivRemIntegral(HBinaryOperation* instruction) {
DCHECK(instruction->IsDiv() || instruction->IsRem());
@@ -2147,12 +2254,11 @@
}
void LocationsBuilderX86::VisitRem(HRem* rem) {
- LocationSummary::CallKind call_kind = rem->GetResultType() == Primitive::kPrimLong
- ? LocationSummary::kCall
- : LocationSummary::kNoCall;
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind);
+ Primitive::Type type = rem->GetResultType();
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(rem, LocationSummary::kNoCall);
- switch (rem->GetResultType()) {
+ switch (type) {
case Primitive::kPrimInt: {
locations->SetInAt(0, Location::RegisterLocation(EAX));
locations->SetInAt(1, Location::RequiresRegister());
@@ -2169,14 +2275,17 @@
locations->SetOut(Location::RegisterPairLocation(EAX, EDX));
break;
}
- case Primitive::kPrimFloat:
- case Primitive::kPrimDouble: {
- LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType();
+ case Primitive::kPrimDouble:
+ case Primitive::kPrimFloat: {
+ locations->SetInAt(0, Location::Any());
+ locations->SetInAt(1, Location::Any());
+ locations->SetOut(Location::RequiresFpuRegister());
+ locations->AddTemp(Location::RegisterLocation(EAX));
break;
}
default:
- LOG(FATAL) << "Unexpected rem type " << rem->GetResultType();
+ LOG(FATAL) << "Unexpected rem type " << type;
}
}
@@ -2190,7 +2299,7 @@
}
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
- LOG(FATAL) << "Unimplemented rem type " << type;
+ GenerateRemFP(rem);
break;
}
default:
@@ -2311,7 +2420,7 @@
__ shrl(first_reg, second_reg);
}
} else {
- Immediate imm(second.GetConstant()->AsIntConstant()->GetValue());
+ Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftValue);
if (op->IsShl()) {
__ shll(first_reg, imm);
} else if (op->IsShr()) {
@@ -2410,8 +2519,7 @@
codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
__ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
- __ fs()->call(
- Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocObjectWithAccessCheck)));
+ __ fs()->call(Address::Absolute(GetThreadOffset<kX86WordSize>(instruction->GetEntrypoint())));
codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
DCHECK(!codegen_->IsLeafMethod());
@@ -2423,17 +2531,16 @@
locations->SetOut(Location::RegisterLocation(EAX));
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
- locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
- locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+ locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+ locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
}
void InstructionCodeGeneratorX86::VisitNewArray(HNewArray* instruction) {
InvokeRuntimeCallingConvention calling_convention;
- codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+ codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(2));
__ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
- __ fs()->call(
- Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocArrayWithAccessCheck)));
+ __ fs()->call(Address::Absolute(GetThreadOffset<kX86WordSize>(instruction->GetEntrypoint())));
codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
DCHECK(!codegen_->IsLeafMethod());
@@ -2468,10 +2575,6 @@
Location out = locations->Out();
DCHECK(in.Equals(out));
switch (not_->InputAt(0)->GetType()) {
- case Primitive::kPrimBoolean:
- __ xorl(out.AsRegister<Register>(), Immediate(1));
- break;
-
case Primitive::kPrimInt:
__ notl(out.AsRegister<Register>());
break;
@@ -2576,90 +2679,28 @@
LOG(FATAL) << "Unreachable";
}
-void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- locations->SetInAt(0, Location::RequiresRegister());
- Primitive::Type field_type = instruction->GetFieldType();
- bool needs_write_barrier =
- CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
-
- bool is_byte_type = (field_type == Primitive::kPrimBoolean)
- || (field_type == Primitive::kPrimByte);
- // The register allocator does not support multiple
- // inputs that die at entry with one in a specific register.
- if (is_byte_type) {
- // Ensure the value is in a byte register.
- locations->SetInAt(1, Location::RegisterLocation(EAX));
- } else {
- locations->SetInAt(1, Location::RequiresRegister());
- }
- // Temporary registers for the write barrier.
- if (needs_write_barrier) {
- locations->AddTemp(Location::RequiresRegister());
- // Ensure the card is in a byte register.
- locations->AddTemp(Location::RegisterLocation(ECX));
+void InstructionCodeGeneratorX86::GenerateMemoryBarrier(MemBarrierKind kind) {
+ /*
+ * According to the JSR-133 Cookbook, for x86 only StoreLoad/AnyAny barriers need memory fence.
+ * All other barriers (LoadAny, AnyStore, StoreStore) are nops due to the x86 memory model.
+ * For those cases, all we need to ensure is that there is a scheduling barrier in place.
+ */
+ switch (kind) {
+ case MemBarrierKind::kAnyAny: {
+ __ mfence();
+ break;
+ }
+ case MemBarrierKind::kAnyStore:
+ case MemBarrierKind::kLoadAny:
+ case MemBarrierKind::kStoreStore: {
+ // nop
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected memory barrier " << kind;
}
}
-void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- LocationSummary* locations = instruction->GetLocations();
- Register obj = locations->InAt(0).AsRegister<Register>();
- uint32_t offset = instruction->GetFieldOffset().Uint32Value();
- Primitive::Type field_type = instruction->GetFieldType();
-
- switch (field_type) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte: {
- ByteRegister value = locations->InAt(1).AsRegister<ByteRegister>();
- __ movb(Address(obj, offset), value);
- break;
- }
-
- case Primitive::kPrimShort:
- case Primitive::kPrimChar: {
- Register value = locations->InAt(1).AsRegister<Register>();
- __ movw(Address(obj, offset), value);
- break;
- }
-
- case Primitive::kPrimInt:
- case Primitive::kPrimNot: {
- Register value = locations->InAt(1).AsRegister<Register>();
- __ movl(Address(obj, offset), value);
-
- if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
- Register temp = locations->GetTemp(0).AsRegister<Register>();
- Register card = locations->GetTemp(1).AsRegister<Register>();
- codegen_->MarkGCCard(temp, card, obj, value);
- }
- break;
- }
-
- case Primitive::kPrimLong: {
- Location value = locations->InAt(1);
- __ movl(Address(obj, offset), value.AsRegisterPairLow<Register>());
- __ movl(Address(obj, kX86WordSize + offset), value.AsRegisterPairHigh<Register>());
- break;
- }
-
- case Primitive::kPrimFloat: {
- XmmRegister value = locations->InAt(1).AsFpuRegister<XmmRegister>();
- __ movss(Address(obj, offset), value);
- break;
- }
-
- case Primitive::kPrimDouble: {
- XmmRegister value = locations->InAt(1).AsFpuRegister<XmmRegister>();
- __ movsd(Address(obj, offset), value);
- break;
- }
-
- case Primitive::kPrimVoid:
- LOG(FATAL) << "Unreachable type " << field_type;
- UNREACHABLE();
- }
-}
void CodeGeneratorX86::MarkGCCard(Register temp, Register card, Register object, Register value) {
Label is_null;
@@ -2673,85 +2714,273 @@
__ Bind(&is_null);
}
-void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
+void LocationsBuilderX86::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) {
+ DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+
+ if (field_info.IsVolatile() && (field_info.GetFieldType() == Primitive::kPrimLong)) {
+ // Long values can be loaded atomically into an XMM using movsd.
+ // So we use an XMM register as a temp to achieve atomicity (first load the temp into the XMM
+ // and then copy the XMM into the output 32bits at a time).
+ locations->AddTemp(Location::RequiresFpuRegister());
+ }
}
-void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
- LocationSummary* locations = instruction->GetLocations();
- Register obj = locations->InAt(0).AsRegister<Register>();
- uint32_t offset = instruction->GetFieldOffset().Uint32Value();
+void InstructionCodeGeneratorX86::HandleFieldGet(HInstruction* instruction,
+ const FieldInfo& field_info) {
+ DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
- switch (instruction->GetType()) {
+ LocationSummary* locations = instruction->GetLocations();
+ Register base = locations->InAt(0).AsRegister<Register>();
+ Location out = locations->Out();
+ bool is_volatile = field_info.IsVolatile();
+ Primitive::Type field_type = field_info.GetFieldType();
+ uint32_t offset = field_info.GetFieldOffset().Uint32Value();
+
+ switch (field_type) {
case Primitive::kPrimBoolean: {
- Register out = locations->Out().AsRegister<Register>();
- __ movzxb(out, Address(obj, offset));
+ __ movzxb(out.AsRegister<Register>(), Address(base, offset));
break;
}
case Primitive::kPrimByte: {
- Register out = locations->Out().AsRegister<Register>();
- __ movsxb(out, Address(obj, offset));
+ __ movsxb(out.AsRegister<Register>(), Address(base, offset));
break;
}
case Primitive::kPrimShort: {
- Register out = locations->Out().AsRegister<Register>();
- __ movsxw(out, Address(obj, offset));
+ __ movsxw(out.AsRegister<Register>(), Address(base, offset));
break;
}
case Primitive::kPrimChar: {
- Register out = locations->Out().AsRegister<Register>();
- __ movzxw(out, Address(obj, offset));
+ __ movzxw(out.AsRegister<Register>(), Address(base, offset));
break;
}
case Primitive::kPrimInt:
case Primitive::kPrimNot: {
- Register out = locations->Out().AsRegister<Register>();
- __ movl(out, Address(obj, offset));
+ __ movl(out.AsRegister<Register>(), Address(base, offset));
break;
}
case Primitive::kPrimLong: {
- // TODO: support volatile.
- __ movl(locations->Out().AsRegisterPairLow<Register>(), Address(obj, offset));
- __ movl(locations->Out().AsRegisterPairHigh<Register>(), Address(obj, kX86WordSize + offset));
+ if (is_volatile) {
+ XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ __ movsd(temp, Address(base, offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ __ movd(out.AsRegisterPairLow<Register>(), temp);
+ __ psrlq(temp, Immediate(32));
+ __ movd(out.AsRegisterPairHigh<Register>(), temp);
+ } else {
+ __ movl(out.AsRegisterPairLow<Register>(), Address(base, offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ __ movl(out.AsRegisterPairHigh<Register>(), Address(base, kX86WordSize + offset));
+ }
break;
}
case Primitive::kPrimFloat: {
- XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
- __ movss(out, Address(obj, offset));
+ __ movss(out.AsFpuRegister<XmmRegister>(), Address(base, offset));
break;
}
case Primitive::kPrimDouble: {
- XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
- __ movsd(out, Address(obj, offset));
+ __ movsd(out.AsFpuRegister<XmmRegister>(), Address(base, offset));
break;
}
case Primitive::kPrimVoid:
- LOG(FATAL) << "Unreachable type " << instruction->GetType();
+ LOG(FATAL) << "Unreachable type " << field_type;
UNREACHABLE();
}
+
+ // Longs are handled in the switch.
+ if (field_type != Primitive::kPrimLong) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
+
+ if (is_volatile) {
+ GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
+ }
+}
+
+void LocationsBuilderX86::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info) {
+ DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
+
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ bool is_volatile = field_info.IsVolatile();
+ Primitive::Type field_type = field_info.GetFieldType();
+ bool is_byte_type = (field_type == Primitive::kPrimBoolean)
+ || (field_type == Primitive::kPrimByte);
+
+ // The register allocator does not support multiple
+ // inputs that die at entry with one in a specific register.
+ if (is_byte_type) {
+ // Ensure the value is in a byte register.
+ locations->SetInAt(1, Location::RegisterLocation(EAX));
+ } else {
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
+ // Temporary registers for the write barrier.
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
+ locations->AddTemp(Location::RequiresRegister());
+ // Ensure the card is in a byte register.
+ locations->AddTemp(Location::RegisterLocation(ECX));
+ } else if (is_volatile && (field_type == Primitive::kPrimLong)) {
+ // 64bits value can be atomically written to an address with movsd and an XMM register.
+ // We need two XMM registers because there's no easier way to (bit) copy a register pair
+ // into a single XMM register (we copy each pair part into the XMMs and then interleave them).
+ // NB: We could make the register allocator understand fp_reg <-> core_reg moves but given the
+ // isolated cases when we need this it isn't worth adding the extra complexity.
+ locations->AddTemp(Location::RequiresFpuRegister());
+ locations->AddTemp(Location::RequiresFpuRegister());
+ }
+}
+
+void InstructionCodeGeneratorX86::HandleFieldSet(HInstruction* instruction,
+ const FieldInfo& field_info) {
+ DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
+
+ LocationSummary* locations = instruction->GetLocations();
+ Register base = locations->InAt(0).AsRegister<Register>();
+ Location value = locations->InAt(1);
+ bool is_volatile = field_info.IsVolatile();
+ Primitive::Type field_type = field_info.GetFieldType();
+ uint32_t offset = field_info.GetFieldOffset().Uint32Value();
+
+ if (is_volatile) {
+ GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
+ }
+
+ switch (field_type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte: {
+ __ movb(Address(base, offset), value.AsRegister<ByteRegister>());
+ break;
+ }
+
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar: {
+ __ movw(Address(base, offset), value.AsRegister<Register>());
+ break;
+ }
+
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ __ movl(Address(base, offset), value.AsRegister<Register>());
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ if (is_volatile) {
+ XmmRegister temp1 = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ XmmRegister temp2 = locations->GetTemp(1).AsFpuRegister<XmmRegister>();
+ __ movd(temp1, value.AsRegisterPairLow<Register>());
+ __ movd(temp2, value.AsRegisterPairHigh<Register>());
+ __ punpckldq(temp1, temp2);
+ __ movsd(Address(base, offset), temp1);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ } else {
+ __ movl(Address(base, offset), value.AsRegisterPairLow<Register>());
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ __ movl(Address(base, kX86WordSize + offset), value.AsRegisterPairHigh<Register>());
+ }
+ break;
+ }
+
+ case Primitive::kPrimFloat: {
+ __ movss(Address(base, offset), value.AsFpuRegister<XmmRegister>());
+ break;
+ }
+
+ case Primitive::kPrimDouble: {
+ __ movsd(Address(base, offset), value.AsFpuRegister<XmmRegister>());
+ break;
+ }
+
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << field_type;
+ UNREACHABLE();
+ }
+
+ // Longs are handled in the switch.
+ if (field_type != Primitive::kPrimLong) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
+
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+ Register card = locations->GetTemp(1).AsRegister<Register>();
+ codegen_->MarkGCCard(temp, card, base, value.AsRegister<Register>());
+ }
+
+ if (is_volatile) {
+ GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+ }
+}
+
+void LocationsBuilderX86::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+ HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorX86::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+ HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
+
+void LocationsBuilderX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+ HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+ HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
+
+void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+ HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+ HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
+
+void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
+ HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
+ HandleFieldGet(instruction, instruction->GetFieldInfo());
}
void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- locations->SetInAt(0, Location::Any());
+ Location loc = codegen_->GetCompilerOptions().GetImplicitNullChecks()
+ ? Location::RequiresRegister()
+ : Location::Any();
+ locations->SetInAt(0, loc);
if (instruction->HasUses()) {
locations->SetOut(Location::SameAsFirstInput());
}
}
-void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) {
+void InstructionCodeGeneratorX86::GenerateImplicitNullCheck(HNullCheck* instruction) {
+ if (codegen_->CanMoveNullCheckToUser(instruction)) {
+ return;
+ }
+ LocationSummary* locations = instruction->GetLocations();
+ Location obj = locations->InAt(0);
+
+ __ testl(EAX, Address(obj.AsRegister<Register>(), 0));
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+}
+
+void InstructionCodeGeneratorX86::GenerateExplicitNullCheck(HNullCheck* instruction) {
SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction);
codegen_->AddSlowPath(slow_path);
@@ -2771,6 +3000,14 @@
__ j(kEqual, slow_path->GetEntryLabel());
}
+void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) {
+ if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
+ GenerateImplicitNullCheck(instruction);
+ } else {
+ GenerateExplicitNullCheck(instruction);
+ }
+}
+
void LocationsBuilderX86::VisitArrayGet(HArrayGet* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
@@ -2784,7 +3021,8 @@
Register obj = locations->InAt(0).AsRegister<Register>();
Location index = locations->InAt(1);
- switch (instruction->GetType()) {
+ Primitive::Type type = instruction->GetType();
+ switch (type) {
case Primitive::kPrimBoolean: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
Register out = locations->Out().AsRegister<Register>();
@@ -2852,24 +3090,50 @@
if (index.IsConstant()) {
size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
__ movl(out.AsRegisterPairLow<Register>(), Address(obj, offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
__ movl(out.AsRegisterPairHigh<Register>(), Address(obj, offset + kX86WordSize));
} else {
__ movl(out.AsRegisterPairLow<Register>(),
Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
__ movl(out.AsRegisterPairHigh<Register>(),
Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset + kX86WordSize));
}
break;
}
- case Primitive::kPrimFloat:
- case Primitive::kPrimDouble:
- LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
- UNREACHABLE();
+ case Primitive::kPrimFloat: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
+ XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
+ if (index.IsConstant()) {
+ __ movss(out, Address(obj,
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset));
+ } else {
+ __ movss(out, Address(obj, index.AsRegister<Register>(), TIMES_4, data_offset));
+ }
+ break;
+ }
+
+ case Primitive::kPrimDouble: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
+ XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
+ if (index.IsConstant()) {
+ __ movsd(out, Address(obj,
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset));
+ } else {
+ __ movsd(out, Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset));
+ }
+ break;
+ }
+
case Primitive::kPrimVoid:
- LOG(FATAL) << "Unreachable type " << instruction->GetType();
+ LOG(FATAL) << "Unreachable type " << type;
UNREACHABLE();
}
+
+ if (type != Primitive::kPrimLong) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
}
void LocationsBuilderX86::VisitArraySet(HArraySet* instruction) {
@@ -2946,6 +3210,7 @@
Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
}
}
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
@@ -2969,6 +3234,7 @@
Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
}
}
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
@@ -2997,6 +3263,7 @@
Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
}
}
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
if (needs_write_barrier) {
Register temp = locations->GetTemp(0).AsRegister<Register>();
@@ -3018,17 +3285,20 @@
size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
if (value.IsRegisterPair()) {
__ movl(Address(obj, offset), value.AsRegisterPairLow<Register>());
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
__ movl(Address(obj, offset + kX86WordSize), value.AsRegisterPairHigh<Register>());
} else {
DCHECK(value.IsConstant());
int64_t val = value.GetConstant()->AsLongConstant()->GetValue();
__ movl(Address(obj, offset), Immediate(Low32Bits(val)));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
__ movl(Address(obj, offset + kX86WordSize), Immediate(High32Bits(val)));
}
} else {
if (value.IsRegisterPair()) {
__ movl(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset),
value.AsRegisterPairLow<Register>());
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
__ movl(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset + kX86WordSize),
value.AsRegisterPairHigh<Register>());
} else {
@@ -3036,6 +3306,7 @@
int64_t val = value.GetConstant()->AsLongConstant()->GetValue();
__ movl(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset),
Immediate(Low32Bits(val)));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
__ movl(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset + kX86WordSize),
Immediate(High32Bits(val)));
}
@@ -3043,10 +3314,32 @@
break;
}
- case Primitive::kPrimFloat:
- case Primitive::kPrimDouble:
- LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
- UNREACHABLE();
+ case Primitive::kPrimFloat: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
+ DCHECK(value.IsFpuRegister());
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ __ movss(Address(obj, offset), value.AsFpuRegister<XmmRegister>());
+ } else {
+ __ movss(Address(obj, index.AsRegister<Register>(), TIMES_4, data_offset),
+ value.AsFpuRegister<XmmRegister>());
+ }
+ break;
+ }
+
+ case Primitive::kPrimDouble: {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
+ DCHECK(value.IsFpuRegister());
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ __ movsd(Address(obj, offset), value.AsFpuRegister<XmmRegister>());
+ } else {
+ __ movsd(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset),
+ value.AsFpuRegister<XmmRegister>());
+ }
+ break;
+ }
+
case Primitive::kPrimVoid:
LOG(FATAL) << "Unreachable type " << instruction->GetType();
UNREACHABLE();
@@ -3066,6 +3359,7 @@
Register obj = locations->InAt(0).AsRegister<Register>();
Register out = locations->Out().AsRegister<Register>();
__ movl(out, Address(obj, offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
}
void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) {
@@ -3147,12 +3441,24 @@
return codegen_->GetAssembler();
}
-void ParallelMoveResolverX86::MoveMemoryToMemory(int dst, int src) {
+void ParallelMoveResolverX86::MoveMemoryToMemory32(int dst, int src) {
ScratchRegisterScope ensure_scratch(
this, kNoRegister, EAX, codegen_->GetNumberOfCoreRegisters());
+ Register temp_reg = static_cast<Register>(ensure_scratch.GetRegister());
int stack_offset = ensure_scratch.IsSpilled() ? kX86WordSize : 0;
- __ movl(static_cast<Register>(ensure_scratch.GetRegister()), Address(ESP, src + stack_offset));
- __ movl(Address(ESP, dst + stack_offset), static_cast<Register>(ensure_scratch.GetRegister()));
+ __ movl(temp_reg, Address(ESP, src + stack_offset));
+ __ movl(Address(ESP, dst + stack_offset), temp_reg);
+}
+
+void ParallelMoveResolverX86::MoveMemoryToMemory64(int dst, int src) {
+ ScratchRegisterScope ensure_scratch(
+ this, kNoRegister, EAX, codegen_->GetNumberOfCoreRegisters());
+ Register temp_reg = static_cast<Register>(ensure_scratch.GetRegister());
+ int stack_offset = ensure_scratch.IsSpilled() ? kX86WordSize : 0;
+ __ movl(temp_reg, Address(ESP, src + stack_offset));
+ __ movl(Address(ESP, dst + stack_offset), temp_reg);
+ __ movl(temp_reg, Address(ESP, src + stack_offset + kX86WordSize));
+ __ movl(Address(ESP, dst + stack_offset + kX86WordSize), temp_reg);
}
void ParallelMoveResolverX86::EmitMove(size_t index) {
@@ -3167,24 +3473,58 @@
DCHECK(destination.IsStackSlot());
__ movl(Address(ESP, destination.GetStackIndex()), source.AsRegister<Register>());
}
+ } else if (source.IsFpuRegister()) {
+ if (destination.IsFpuRegister()) {
+ __ movaps(destination.AsFpuRegister<XmmRegister>(), source.AsFpuRegister<XmmRegister>());
+ } else if (destination.IsStackSlot()) {
+ __ movss(Address(ESP, destination.GetStackIndex()), source.AsFpuRegister<XmmRegister>());
+ } else {
+ DCHECK(destination.IsDoubleStackSlot());
+ __ movsd(Address(ESP, destination.GetStackIndex()), source.AsFpuRegister<XmmRegister>());
+ }
} else if (source.IsStackSlot()) {
if (destination.IsRegister()) {
__ movl(destination.AsRegister<Register>(), Address(ESP, source.GetStackIndex()));
+ } else if (destination.IsFpuRegister()) {
+ __ movss(destination.AsFpuRegister<XmmRegister>(), Address(ESP, source.GetStackIndex()));
} else {
DCHECK(destination.IsStackSlot());
- MoveMemoryToMemory(destination.GetStackIndex(),
- source.GetStackIndex());
+ MoveMemoryToMemory32(destination.GetStackIndex(), source.GetStackIndex());
+ }
+ } else if (source.IsDoubleStackSlot()) {
+ if (destination.IsFpuRegister()) {
+ __ movsd(destination.AsFpuRegister<XmmRegister>(), Address(ESP, source.GetStackIndex()));
+ } else {
+ DCHECK(destination.IsDoubleStackSlot()) << destination;
+ MoveMemoryToMemory64(destination.GetStackIndex(), source.GetStackIndex());
}
} else if (source.IsConstant()) {
- HIntConstant* instruction = source.GetConstant()->AsIntConstant();
- Immediate imm(instruction->AsIntConstant()->GetValue());
- if (destination.IsRegister()) {
- __ movl(destination.AsRegister<Register>(), imm);
+ HConstant* constant = source.GetConstant();
+ if (constant->IsIntConstant()) {
+ Immediate imm(constant->AsIntConstant()->GetValue());
+ if (destination.IsRegister()) {
+ __ movl(destination.AsRegister<Register>(), imm);
+ } else {
+ DCHECK(destination.IsStackSlot()) << destination;
+ __ movl(Address(ESP, destination.GetStackIndex()), imm);
+ }
} else {
- __ movl(Address(ESP, destination.GetStackIndex()), imm);
+ DCHECK(constant->IsFloatConstant());
+ float value = constant->AsFloatConstant()->GetValue();
+ Immediate imm(bit_cast<float, int32_t>(value));
+ if (destination.IsFpuRegister()) {
+ ScratchRegisterScope ensure_scratch(
+ this, kNoRegister, EAX, codegen_->GetNumberOfCoreRegisters());
+ Register temp = static_cast<Register>(ensure_scratch.GetRegister());
+ __ movl(temp, imm);
+ __ movd(destination.AsFpuRegister<XmmRegister>(), temp);
+ } else {
+ DCHECK(destination.IsStackSlot()) << destination;
+ __ movl(Address(ESP, destination.GetStackIndex()), imm);
+ }
}
} else {
- LOG(FATAL) << "Unimplemented";
+ LOG(FATAL) << "Unimplemented move: " << destination << " <- " << source;
}
}
@@ -3199,6 +3539,17 @@
__ movl(reg, static_cast<Register>(ensure_scratch.GetRegister()));
}
+void ParallelMoveResolverX86::Exchange32(XmmRegister reg, int mem) {
+ ScratchRegisterScope ensure_scratch(
+ this, kNoRegister, EAX, codegen_->GetNumberOfCoreRegisters());
+
+ Register temp_reg = static_cast<Register>(ensure_scratch.GetRegister());
+ int stack_offset = ensure_scratch.IsSpilled() ? kX86WordSize : 0;
+ __ movl(temp_reg, Address(ESP, mem + stack_offset));
+ __ movss(Address(ESP, mem + stack_offset), reg);
+ __ movd(reg, temp_reg);
+}
+
void ParallelMoveResolverX86::Exchange(int mem1, int mem2) {
ScratchRegisterScope ensure_scratch1(
this, kNoRegister, EAX, codegen_->GetNumberOfCoreRegisters());
@@ -3228,8 +3579,18 @@
Exchange(destination.AsRegister<Register>(), source.GetStackIndex());
} else if (source.IsStackSlot() && destination.IsStackSlot()) {
Exchange(destination.GetStackIndex(), source.GetStackIndex());
+ } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
+ // Use XOR Swap algorithm to avoid a temporary.
+ DCHECK_NE(source.reg(), destination.reg());
+ __ xorpd(destination.AsFpuRegister<XmmRegister>(), source.AsFpuRegister<XmmRegister>());
+ __ xorpd(source.AsFpuRegister<XmmRegister>(), destination.AsFpuRegister<XmmRegister>());
+ __ xorpd(destination.AsFpuRegister<XmmRegister>(), source.AsFpuRegister<XmmRegister>());
+ } else if (source.IsFpuRegister() && destination.IsStackSlot()) {
+ Exchange32(source.AsFpuRegister<XmmRegister>(), destination.GetStackIndex());
+ } else if (destination.IsFpuRegister() && source.IsStackSlot()) {
+ Exchange32(destination.AsFpuRegister<XmmRegister>(), source.GetStackIndex());
} else {
- LOG(FATAL) << "Unimplemented";
+ LOG(FATAL) << "Unimplemented: source: " << source << ", destination: " << destination;
}
}
@@ -3303,159 +3664,6 @@
// No need for memory fence, thanks to the X86 memory model.
}
-void LocationsBuilderX86::VisitStaticFieldGet(HStaticFieldGet* instruction) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-void InstructionCodeGeneratorX86::VisitStaticFieldGet(HStaticFieldGet* instruction) {
- LocationSummary* locations = instruction->GetLocations();
- Register cls = locations->InAt(0).AsRegister<Register>();
- uint32_t offset = instruction->GetFieldOffset().Uint32Value();
-
- switch (instruction->GetType()) {
- case Primitive::kPrimBoolean: {
- Register out = locations->Out().AsRegister<Register>();
- __ movzxb(out, Address(cls, offset));
- break;
- }
-
- case Primitive::kPrimByte: {
- Register out = locations->Out().AsRegister<Register>();
- __ movsxb(out, Address(cls, offset));
- break;
- }
-
- case Primitive::kPrimShort: {
- Register out = locations->Out().AsRegister<Register>();
- __ movsxw(out, Address(cls, offset));
- break;
- }
-
- case Primitive::kPrimChar: {
- Register out = locations->Out().AsRegister<Register>();
- __ movzxw(out, Address(cls, offset));
- break;
- }
-
- case Primitive::kPrimInt:
- case Primitive::kPrimNot: {
- Register out = locations->Out().AsRegister<Register>();
- __ movl(out, Address(cls, offset));
- break;
- }
-
- case Primitive::kPrimLong: {
- // TODO: support volatile.
- __ movl(locations->Out().AsRegisterPairLow<Register>(), Address(cls, offset));
- __ movl(locations->Out().AsRegisterPairHigh<Register>(), Address(cls, kX86WordSize + offset));
- break;
- }
-
- case Primitive::kPrimFloat: {
- XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
- __ movss(out, Address(cls, offset));
- break;
- }
-
- case Primitive::kPrimDouble: {
- XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
- __ movsd(out, Address(cls, offset));
- break;
- }
-
- case Primitive::kPrimVoid:
- LOG(FATAL) << "Unreachable type " << instruction->GetType();
- UNREACHABLE();
- }
-}
-
-void LocationsBuilderX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- locations->SetInAt(0, Location::RequiresRegister());
- Primitive::Type field_type = instruction->GetFieldType();
- bool needs_write_barrier =
- CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
- bool is_byte_type = (field_type == Primitive::kPrimBoolean)
- || (field_type == Primitive::kPrimByte);
- // The register allocator does not support multiple
- // inputs that die at entry with one in a specific register.
- if (is_byte_type) {
- // Ensure the value is in a byte register.
- locations->SetInAt(1, Location::RegisterLocation(EAX));
- } else {
- locations->SetInAt(1, Location::RequiresRegister());
- }
- // Temporary registers for the write barrier.
- if (needs_write_barrier) {
- locations->AddTemp(Location::RequiresRegister());
- // Ensure the card is in a byte register.
- locations->AddTemp(Location::RegisterLocation(ECX));
- }
-}
-
-void InstructionCodeGeneratorX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- LocationSummary* locations = instruction->GetLocations();
- Register cls = locations->InAt(0).AsRegister<Register>();
- uint32_t offset = instruction->GetFieldOffset().Uint32Value();
- Primitive::Type field_type = instruction->GetFieldType();
-
- switch (field_type) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte: {
- ByteRegister value = locations->InAt(1).AsRegister<ByteRegister>();
- __ movb(Address(cls, offset), value);
- break;
- }
-
- case Primitive::kPrimShort:
- case Primitive::kPrimChar: {
- Register value = locations->InAt(1).AsRegister<Register>();
- __ movw(Address(cls, offset), value);
- break;
- }
-
- case Primitive::kPrimInt:
- case Primitive::kPrimNot: {
- Register value = locations->InAt(1).AsRegister<Register>();
- __ movl(Address(cls, offset), value);
-
- if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
- Register temp = locations->GetTemp(0).AsRegister<Register>();
- Register card = locations->GetTemp(1).AsRegister<Register>();
- codegen_->MarkGCCard(temp, card, cls, value);
- }
- break;
- }
-
- case Primitive::kPrimLong: {
- Location value = locations->InAt(1);
- __ movl(Address(cls, offset), value.AsRegisterPairLow<Register>());
- __ movl(Address(cls, kX86WordSize + offset), value.AsRegisterPairHigh<Register>());
- break;
- }
-
- case Primitive::kPrimFloat: {
- XmmRegister value = locations->InAt(1).AsFpuRegister<XmmRegister>();
- __ movss(Address(cls, offset), value);
- break;
- }
-
- case Primitive::kPrimDouble: {
- XmmRegister value = locations->InAt(1).AsFpuRegister<XmmRegister>();
- __ movsd(Address(cls, offset), value);
- break;
- }
-
- case Primitive::kPrimVoid:
- LOG(FATAL) << "Unreachable type " << field_type;
- UNREACHABLE();
- }
-}
-
void LocationsBuilderX86::VisitLoadString(HLoadString* load) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index aed06c0..107ddaf 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -18,6 +18,8 @@
#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_X86_H_
#include "code_generator.h"
+#include "dex/compiler_enums.h"
+#include "driver/compiler_options.h"
#include "nodes.h"
#include "parallel_move_resolver.h"
#include "utils/x86/assembler_x86.h"
@@ -34,8 +36,8 @@
static constexpr Register kParameterCoreRegisters[] = { ECX, EDX, EBX };
static constexpr RegisterPair kParameterCorePairRegisters[] = { ECX_EDX, EDX_EBX };
static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters);
-static constexpr XmmRegister kParameterFpuRegisters[] = { };
-static constexpr size_t kParameterFpuRegistersLength = 0;
+static constexpr XmmRegister kParameterFpuRegisters[] = { XMM0, XMM1, XMM2, XMM3 };
+static constexpr size_t kParameterFpuRegistersLength = arraysize(kParameterFpuRegisters);
class InvokeDexCallingConvention : public CallingConvention<Register, XmmRegister> {
public:
@@ -56,13 +58,18 @@
class InvokeDexCallingConventionVisitor {
public:
- InvokeDexCallingConventionVisitor() : gp_index_(0) {}
+ InvokeDexCallingConventionVisitor() : gp_index_(0), fp_index_(0), stack_index_(0) {}
Location GetNextLocation(Primitive::Type type);
private:
InvokeDexCallingConvention calling_convention;
+ // The current index for cpu registers.
uint32_t gp_index_;
+ // The current index for fpu registers.
+ uint32_t fp_index_;
+ // The current stack index.
+ uint32_t stack_index_;
DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitor);
};
@@ -82,7 +89,9 @@
private:
void Exchange(Register reg, int mem);
void Exchange(int mem1, int mem2);
- void MoveMemoryToMemory(int dst, int src);
+ void Exchange32(XmmRegister reg, int mem);
+ void MoveMemoryToMemory32(int dst, int src);
+ void MoveMemoryToMemory64(int dst, int src);
CodeGeneratorX86* const codegen_;
@@ -105,6 +114,8 @@
void HandleBitwiseOperation(HBinaryOperation* instruction);
void HandleInvoke(HInvoke* invoke);
void HandleShift(HBinaryOperation* instruction);
+ void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+ void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
CodeGeneratorX86* const codegen_;
InvokeDexCallingConventionVisitor parameter_visitor_;
@@ -133,10 +144,19 @@
void GenerateClassInitializationCheck(SlowPathCodeX86* slow_path, Register class_reg);
void HandleBitwiseOperation(HBinaryOperation* instruction);
void GenerateDivRemIntegral(HBinaryOperation* instruction);
+ void GenerateRemFP(HRem *rem);
void HandleShift(HBinaryOperation* instruction);
void GenerateShlLong(const Location& loc, Register shifter);
void GenerateShrLong(const Location& loc, Register shifter);
void GenerateUShrLong(const Location& loc, Register shifter);
+ void GenerateMemoryBarrier(MemBarrierKind kind);
+ void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+ void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
+ void PushOntoFPStack(Location source, uint32_t temp_offset,
+ uint32_t stack_adjustment, bool is_float);
+
+ void GenerateImplicitNullCheck(HNullCheck* instruction);
+ void GenerateExplicitNullCheck(HNullCheck* instruction);
X86Assembler* const assembler_;
CodeGeneratorX86* const codegen_;
@@ -146,7 +166,7 @@
class CodeGeneratorX86 : public CodeGenerator {
public:
- explicit CodeGeneratorX86(HGraph* graph);
+ CodeGeneratorX86(HGraph* graph, const CompilerOptions& compiler_options);
virtual ~CodeGeneratorX86() {}
void GenerateFrameEntry() OVERRIDE;
@@ -155,12 +175,17 @@
void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+ size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+ size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t GetWordSize() const OVERRIDE {
return kX86WordSize;
}
- size_t FrameEntrySpillSize() const OVERRIDE;
+ size_t GetFloatingPointSpillSlotSize() const OVERRIDE {
+ // 8 bytes == 2 words for each spill.
+ return 2 * kX86WordSize;
+ }
HGraphVisitor* GetLocationBuilder() OVERRIDE {
return &location_builder_;
@@ -178,7 +203,7 @@
return GetLabelOf(block)->Position();
}
- void SetupBlockedRegisters() const OVERRIDE;
+ void SetupBlockedRegisters(bool is_baseline) const OVERRIDE;
Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE;
@@ -216,9 +241,16 @@
block_labels_.SetSize(GetGraph()->GetBlocks().Size());
}
+ bool NeedsTwoRegisters(Primitive::Type type) const OVERRIDE {
+ return type == Primitive::kPrimLong;
+ }
+
+ Label* GetFrameEntryLabel() { return &frame_entry_label_; }
+
private:
// Labels for each block that will be compiled.
GrowableArray<Label> block_labels_;
+ Label frame_entry_label_;
LocationsBuilderX86 location_builder_;
InstructionCodeGeneratorX86 instruction_visitor_;
ParallelMoveResolverX86 move_resolver_;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 47fd304..88f1753 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -18,6 +18,8 @@
#include "entrypoints/quick/quick_entrypoints.h"
#include "gc/accounting/card_table.h"
+#include "intrinsics.h"
+#include "intrinsics_x86_64.h"
#include "mirror/array-inl.h"
#include "mirror/art_method.h"
#include "mirror/class.h"
@@ -32,19 +34,21 @@
namespace x86_64 {
-static constexpr bool kExplicitStackOverflowCheck = false;
-
// Some x86_64 instructions require a register to be available as temp.
static constexpr Register TMP = R11;
-static constexpr int kNumberOfPushedRegistersAtEntry = 1;
static constexpr int kCurrentMethodStackOffset = 0;
static constexpr Register kRuntimeParameterCoreRegisters[] = { RDI, RSI, RDX };
static constexpr size_t kRuntimeParameterCoreRegistersLength =
arraysize(kRuntimeParameterCoreRegisters);
-static constexpr FloatRegister kRuntimeParameterFpuRegisters[] = { };
-static constexpr size_t kRuntimeParameterFpuRegistersLength = 0;
+static constexpr FloatRegister kRuntimeParameterFpuRegisters[] = { XMM0, XMM1 };
+static constexpr size_t kRuntimeParameterFpuRegistersLength =
+ arraysize(kRuntimeParameterFpuRegisters);
+static constexpr Register kCoreCalleeSaves[] = { RBX, RBP, R12, R13, R14, R15 };
+static constexpr FloatRegister kFpuCalleeSaves[] = { XMM12, XMM13, XMM14, XMM15 };
+
+static constexpr int kC2ConditionMask = 0x400;
class InvokeRuntimeCallingConvention : public CallingConvention<Register, FloatRegister> {
public:
@@ -60,20 +64,6 @@
#define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())->
-class SlowPathCodeX86_64 : public SlowPathCode {
- public:
- SlowPathCodeX86_64() : entry_label_(), exit_label_() {}
-
- Label* GetEntryLabel() { return &entry_label_; }
- Label* GetExitLabel() { return &exit_label_; }
-
- private:
- Label entry_label_;
- Label exit_label_;
-
- DISALLOW_COPY_AND_ASSIGN(SlowPathCodeX86_64);
-};
-
class NullCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
public:
explicit NullCheckSlowPathX86_64(HNullCheck* instruction) : instruction_(instruction) {}
@@ -138,22 +128,6 @@
DISALLOW_COPY_AND_ASSIGN(DivRemMinusOneSlowPathX86_64);
};
-class StackOverflowCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
- public:
- StackOverflowCheckSlowPathX86_64() {}
-
- virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
- __ Bind(GetEntryLabel());
- __ addq(CpuRegister(RSP),
- Immediate(codegen->GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
- __ gs()->jmp(
- Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowStackOverflow), true));
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86_64);
-};
-
class SuspendCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
public:
explicit SuspendCheckSlowPathX86_64(HSuspendCheck* instruction, HBasicBlock* successor)
@@ -284,8 +258,8 @@
codegen->SaveLiveRegisters(locations);
InvokeRuntimeCallingConvention calling_convention;
- x64_codegen->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(0)));
- __ movl(CpuRegister(calling_convention.GetRegisterAt(1)),
+ x64_codegen->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
+ __ movl(CpuRegister(calling_convention.GetRegisterAt(0)),
Immediate(instruction_->GetStringIndex()));
__ gs()->call(Address::Absolute(
QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pResolveString), true));
@@ -374,6 +348,35 @@
return kEqual;
}
+void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
+ CpuRegister temp) {
+ // All registers are assumed to be correctly set up.
+
+ // TODO: Implement all kinds of calls:
+ // 1) boot -> boot
+ // 2) app -> boot
+ // 3) app -> app
+ //
+ // Currently we implement the app -> app logic, which looks up in the resolve cache.
+
+ // temp = method;
+ LoadCurrentMethod(temp);
+ if (!invoke->IsRecursive()) {
+ // temp = temp->dex_cache_resolved_methods_;
+ __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue()));
+ // temp = temp[index_in_cache]
+ __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex())));
+ // (temp + offset_of_quick_compiled_code)()
+ __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kX86_64WordSize).SizeValue()));
+ } else {
+ __ call(&frame_entry_label_);
+ }
+
+ DCHECK(!IsLeafMethod());
+ RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
void CodeGeneratorX86_64::DumpCoreRegister(std::ostream& stream, int reg) const {
stream << X86_64ManagedRegister::FromCpuRegister(Register(reg));
}
@@ -402,15 +405,25 @@
return kX86_64WordSize;
}
-CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph)
- : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfFloatRegisters, 0),
+static constexpr int kNumberOfCpuRegisterPairs = 0;
+// Use a fake return address register to mimic Quick.
+static constexpr Register kFakeReturnRegister = Register(kLastCpuRegister + 1);
+CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph, const CompilerOptions& compiler_options)
+ : CodeGenerator(graph,
+ kNumberOfCpuRegisters,
+ kNumberOfFloatRegisters,
+ kNumberOfCpuRegisterPairs,
+ ComputeRegisterMask(reinterpret_cast<const int*>(kCoreCalleeSaves),
+ arraysize(kCoreCalleeSaves))
+ | (1 << kFakeReturnRegister),
+ ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves),
+ arraysize(kFpuCalleeSaves)),
+ compiler_options),
block_labels_(graph->GetArena(), 0),
location_builder_(graph, this),
instruction_visitor_(graph, this),
- move_resolver_(graph->GetArena(), this) {}
-
-size_t CodeGeneratorX86_64::FrameEntrySpillSize() const {
- return kNumberOfPushedRegistersAtEntry * kX86_64WordSize;
+ move_resolver_(graph->GetArena(), this) {
+ AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister));
}
InstructionCodeGeneratorX86_64::InstructionCodeGeneratorX86_64(HGraph* graph,
@@ -445,60 +458,81 @@
return Location();
}
-void CodeGeneratorX86_64::SetupBlockedRegisters() const {
+void CodeGeneratorX86_64::SetupBlockedRegisters(bool is_baseline) const {
// Stack register is always reserved.
blocked_core_registers_[RSP] = true;
// Block the register used as TMP.
blocked_core_registers_[TMP] = true;
- // TODO: We currently don't use Quick's callee saved registers.
- blocked_core_registers_[RBX] = true;
- blocked_core_registers_[RBP] = true;
- blocked_core_registers_[R12] = true;
- blocked_core_registers_[R13] = true;
- blocked_core_registers_[R14] = true;
- blocked_core_registers_[R15] = true;
-
- blocked_fpu_registers_[XMM12] = true;
- blocked_fpu_registers_[XMM13] = true;
- blocked_fpu_registers_[XMM14] = true;
- blocked_fpu_registers_[XMM15] = true;
+ if (is_baseline) {
+ for (size_t i = 0; i < arraysize(kCoreCalleeSaves); ++i) {
+ blocked_core_registers_[kCoreCalleeSaves[i]] = true;
+ }
+ for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
+ blocked_fpu_registers_[kFpuCalleeSaves[i]] = true;
+ }
+ }
}
void CodeGeneratorX86_64::GenerateFrameEntry() {
- // Create a fake register to mimic Quick.
- static const int kFakeReturnRegister = 16;
- core_spill_mask_ |= (1 << kFakeReturnRegister);
-
+ __ Bind(&frame_entry_label_);
bool skip_overflow_check = IsLeafMethod()
&& !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86_64);
+ DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
- if (!skip_overflow_check && !kExplicitStackOverflowCheck) {
+ if (!skip_overflow_check) {
__ testq(CpuRegister(RAX), Address(
CpuRegister(RSP), -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86_64))));
RecordPcInfo(nullptr, 0);
}
- // The return PC has already been pushed on the stack.
- __ subq(CpuRegister(RSP),
- Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
+ if (HasEmptyFrame()) {
+ return;
+ }
- if (!skip_overflow_check && kExplicitStackOverflowCheck) {
- SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86_64();
- AddSlowPath(slow_path);
+ for (int i = arraysize(kCoreCalleeSaves) - 1; i >= 0; --i) {
+ Register reg = kCoreCalleeSaves[i];
+ if (allocated_registers_.ContainsCoreRegister(reg)) {
+ __ pushq(CpuRegister(reg));
+ }
+ }
- __ gs()->cmpq(CpuRegister(RSP),
- Address::Absolute(Thread::StackEndOffset<kX86_64WordSize>(), true));
- __ j(kLess, slow_path->GetEntryLabel());
+ __ subq(CpuRegister(RSP), Immediate(GetFrameSize() - GetCoreSpillSize()));
+ uint32_t xmm_spill_location = GetFpuSpillStart();
+ size_t xmm_spill_slot_size = GetFloatingPointSpillSlotSize();
+
+ for (int i = arraysize(kFpuCalleeSaves) - 1; i >= 0; --i) {
+ if (allocated_registers_.ContainsFloatingPointRegister(kFpuCalleeSaves[i])) {
+ __ movsd(Address(CpuRegister(RSP), xmm_spill_location + (xmm_spill_slot_size * i)),
+ XmmRegister(kFpuCalleeSaves[i]));
+ }
}
__ movl(Address(CpuRegister(RSP), kCurrentMethodStackOffset), CpuRegister(RDI));
}
void CodeGeneratorX86_64::GenerateFrameExit() {
- __ addq(CpuRegister(RSP),
- Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
+ if (HasEmptyFrame()) {
+ return;
+ }
+ uint32_t xmm_spill_location = GetFpuSpillStart();
+ size_t xmm_spill_slot_size = GetFloatingPointSpillSlotSize();
+ for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
+ if (allocated_registers_.ContainsFloatingPointRegister(kFpuCalleeSaves[i])) {
+ __ movsd(XmmRegister(kFpuCalleeSaves[i]),
+ Address(CpuRegister(RSP), xmm_spill_location + (xmm_spill_slot_size * i)));
+ }
+ }
+
+ __ addq(CpuRegister(RSP), Immediate(GetFrameSize() - GetCoreSpillSize()));
+
+ for (size_t i = 0; i < arraysize(kCoreCalleeSaves); ++i) {
+ Register reg = kCoreCalleeSaves[i];
+ if (allocated_registers_.ContainsCoreRegister(reg)) {
+ __ popq(CpuRegister(reg));
+ }
+ }
}
void CodeGeneratorX86_64::Bind(HBasicBlock* block) {
@@ -506,6 +540,7 @@
}
void CodeGeneratorX86_64::LoadCurrentMethod(CpuRegister reg) {
+ DCHECK(RequiresCurrentMethod());
__ movl(reg, Address(CpuRegister(RSP), kCurrentMethodStackOffset));
}
@@ -570,8 +605,18 @@
} else if (source.IsFpuRegister()) {
__ movss(Address(CpuRegister(RSP), destination.GetStackIndex()),
source.AsFpuRegister<XmmRegister>());
+ } else if (source.IsConstant()) {
+ HConstant* constant = source.GetConstant();
+ int32_t value;
+ if (constant->IsFloatConstant()) {
+ value = bit_cast<float, int32_t>(constant->AsFloatConstant()->GetValue());
+ } else {
+ DCHECK(constant->IsIntConstant());
+ value = constant->AsIntConstant()->GetValue();
+ }
+ __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), Immediate(value));
} else {
- DCHECK(source.IsStackSlot());
+ DCHECK(source.IsStackSlot()) << source;
__ movl(CpuRegister(TMP), Address(CpuRegister(RSP), source.GetStackIndex()));
__ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP));
}
@@ -583,6 +628,17 @@
} else if (source.IsFpuRegister()) {
__ movsd(Address(CpuRegister(RSP), destination.GetStackIndex()),
source.AsFpuRegister<XmmRegister>());
+ } else if (source.IsConstant()) {
+ HConstant* constant = source.GetConstant();
+ int64_t value = constant->AsLongConstant()->GetValue();
+ if (constant->IsDoubleConstant()) {
+ value = bit_cast<double, int64_t>(constant->AsDoubleConstant()->GetValue());
+ } else {
+ DCHECK(constant->IsLongConstant());
+ value = constant->AsLongConstant()->GetValue();
+ }
+ __ movq(CpuRegister(TMP), Immediate(value));
+ __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP));
} else {
DCHECK(source.IsDoubleStackSlot());
__ movq(CpuRegister(TMP), Address(CpuRegister(RSP), source.GetStackIndex()));
@@ -742,7 +798,7 @@
// Materialized condition, compare against 0.
Location lhs = if_instr->GetLocations()->InAt(0);
if (lhs.IsRegister()) {
- __ cmpl(lhs.AsRegister<CpuRegister>(), Immediate(0));
+ __ testl(lhs.AsRegister<CpuRegister>(), lhs.AsRegister<CpuRegister>());
} else {
__ cmpl(Address(CpuRegister(RSP), lhs.GetStackIndex()),
Immediate(0));
@@ -758,8 +814,12 @@
if (rhs.IsRegister()) {
__ cmpl(lhs.AsRegister<CpuRegister>(), rhs.AsRegister<CpuRegister>());
} else if (rhs.IsConstant()) {
- __ cmpl(lhs.AsRegister<CpuRegister>(),
- Immediate(rhs.GetConstant()->AsIntConstant()->GetValue()));
+ int32_t constant = rhs.GetConstant()->AsIntConstant()->GetValue();
+ if (constant == 0) {
+ __ testl(lhs.AsRegister<CpuRegister>(), lhs.AsRegister<CpuRegister>());
+ } else {
+ __ cmpl(lhs.AsRegister<CpuRegister>(), Immediate(constant));
+ }
} else {
__ cmpl(lhs.AsRegister<CpuRegister>(),
Address(CpuRegister(RSP), rhs.GetStackIndex()));
@@ -835,15 +895,19 @@
CpuRegister reg = locations->Out().AsRegister<CpuRegister>();
// Clear register: setcc only sets the low byte.
__ xorq(reg, reg);
- if (locations->InAt(1).IsRegister()) {
- __ cmpl(locations->InAt(0).AsRegister<CpuRegister>(),
- locations->InAt(1).AsRegister<CpuRegister>());
- } else if (locations->InAt(1).IsConstant()) {
- __ cmpl(locations->InAt(0).AsRegister<CpuRegister>(),
- Immediate(locations->InAt(1).GetConstant()->AsIntConstant()->GetValue()));
+ Location lhs = locations->InAt(0);
+ Location rhs = locations->InAt(1);
+ if (rhs.IsRegister()) {
+ __ cmpl(lhs.AsRegister<CpuRegister>(), rhs.AsRegister<CpuRegister>());
+ } else if (rhs.IsConstant()) {
+ int32_t constant = rhs.GetConstant()->AsIntConstant()->GetValue();
+ if (constant == 0) {
+ __ testl(lhs.AsRegister<CpuRegister>(), lhs.AsRegister<CpuRegister>());
+ } else {
+ __ cmpl(lhs.AsRegister<CpuRegister>(), Immediate(constant));
+ }
} else {
- __ cmpl(locations->InAt(0).AsRegister<CpuRegister>(),
- Address(CpuRegister(RSP), locations->InAt(1).GetStackIndex()));
+ __ cmpl(lhs.AsRegister<CpuRegister>(), Address(CpuRegister(RSP), rhs.GetStackIndex()));
}
__ setcc(X86_64Condition(comp->GetCondition()), reg);
}
@@ -1121,31 +1185,32 @@
return Location();
}
-void LocationsBuilderX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
+void LocationsBuilderX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+ IntrinsicLocationsBuilderX86_64 intrinsic(GetGraph()->GetArena());
+ if (intrinsic.TryDispatch(invoke)) {
+ return;
+ }
+
HandleInvoke(invoke);
}
-void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
- CpuRegister temp = invoke->GetLocations()->GetTemp(0).AsRegister<CpuRegister>();
- // TODO: Implement all kinds of calls:
- // 1) boot -> boot
- // 2) app -> boot
- // 3) app -> app
- //
- // Currently we implement the app -> app logic, which looks up in the resolve cache.
+static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorX86_64* codegen) {
+ if (invoke->GetLocations()->Intrinsified()) {
+ IntrinsicCodeGeneratorX86_64 intrinsic(codegen);
+ intrinsic.Dispatch(invoke);
+ return true;
+ }
+ return false;
+}
- // temp = method;
- codegen_->LoadCurrentMethod(temp);
- // temp = temp->dex_cache_resolved_methods_;
- __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue()));
- // temp = temp[index_in_cache]
- __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache())));
- // (temp + offset_of_quick_compiled_code)()
- __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
- kX86_64WordSize).SizeValue()));
+void InstructionCodeGeneratorX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+ if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+ return;
+ }
- DCHECK(!codegen_->IsLeafMethod());
- codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+ codegen_->GenerateStaticOrDirectCall(
+ invoke,
+ invoke->GetLocations()->GetTemp(0).AsRegister<CpuRegister>());
}
void LocationsBuilderX86_64::HandleInvoke(HInvoke* invoke) {
@@ -1181,10 +1246,19 @@
}
void LocationsBuilderX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+ IntrinsicLocationsBuilderX86_64 intrinsic(GetGraph()->GetArena());
+ if (intrinsic.TryDispatch(invoke)) {
+ return;
+ }
+
HandleInvoke(invoke);
}
void InstructionCodeGeneratorX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+ if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+ return;
+ }
+
CpuRegister temp = invoke->GetLocations()->GetTemp(0).AsRegister<CpuRegister>();
size_t method_offset = mirror::Class::EmbeddedVTableOffset().SizeValue() +
invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry);
@@ -1198,6 +1272,7 @@
} else {
__ movl(temp, Address(receiver.AsRegister<CpuRegister>(), class_offset));
}
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
// temp = temp->GetMethodAt(method_offset);
__ movl(temp, Address(temp, method_offset));
// call temp->GetEntryPoint();
@@ -1234,6 +1309,7 @@
} else {
__ movl(temp, Address(receiver.AsRegister<CpuRegister>(), class_offset));
}
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
// temp = temp->GetImtEntryAt(method_offset);
__ movl(temp, Address(temp, method_offset));
// call temp->GetEntryPoint();
@@ -1370,8 +1446,10 @@
break;
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ // Processing a Dex `double-to-int' instruction.
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresFpuRegister());
break;
default:
@@ -1394,9 +1472,17 @@
break;
case Primitive::kPrimFloat:
+ // Processing a Dex `float-to-long' instruction.
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresFpuRegister());
+ break;
+
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type << " to "
- << result_type << " not yet implemented";
+ // Processing a Dex `double-to-long' instruction.
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresFpuRegister());
break;
default:
@@ -1439,8 +1525,9 @@
break;
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ // Processing a Dex `double-to-float' instruction.
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
break;
default:
@@ -1467,8 +1554,9 @@
break;
case Primitive::kPrimFloat:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ // Processing a Dex `float-to-double' instruction.
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
break;
default:
@@ -1565,14 +1653,14 @@
__ movl(output, Immediate(kPrimIntMax));
// temp = int-to-float(output)
- __ cvtsi2ss(temp, output);
+ __ cvtsi2ss(temp, output, false);
// if input >= temp goto done
__ comiss(input, temp);
__ j(kAboveEqual, &done);
// if input == NaN goto nan
__ j(kUnordered, &nan);
// output = float-to-int-truncate(input)
- __ cvttss2si(output, input);
+ __ cvttss2si(output, input, false);
__ jmp(&done);
__ Bind(&nan);
// output = 0
@@ -1581,10 +1669,30 @@
break;
}
- case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ case Primitive::kPrimDouble: {
+ // Processing a Dex `double-to-int' instruction.
+ XmmRegister input = in.AsFpuRegister<XmmRegister>();
+ CpuRegister output = out.AsRegister<CpuRegister>();
+ XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ Label done, nan;
+
+ __ movl(output, Immediate(kPrimIntMax));
+ // temp = int-to-double(output)
+ __ cvtsi2sd(temp, output);
+ // if input >= temp goto done
+ __ comisd(input, temp);
+ __ j(kAboveEqual, &done);
+ // if input == NaN goto nan
+ __ j(kUnordered, &nan);
+ // output = double-to-int-truncate(input)
+ __ cvttsd2si(output, input);
+ __ jmp(&done);
+ __ Bind(&nan);
+ // output = 0
+ __ xorl(output, output);
+ __ Bind(&done);
break;
+ }
default:
LOG(FATAL) << "Unexpected type conversion from " << input_type
@@ -1604,11 +1712,55 @@
__ movsxd(out.AsRegister<CpuRegister>(), in.AsRegister<CpuRegister>());
break;
- case Primitive::kPrimFloat:
- case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type << " to "
- << result_type << " not yet implemented";
+ case Primitive::kPrimFloat: {
+ // Processing a Dex `float-to-long' instruction.
+ XmmRegister input = in.AsFpuRegister<XmmRegister>();
+ CpuRegister output = out.AsRegister<CpuRegister>();
+ XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ Label done, nan;
+
+ __ movq(output, Immediate(kPrimLongMax));
+ // temp = long-to-float(output)
+ __ cvtsi2ss(temp, output, true);
+ // if input >= temp goto done
+ __ comiss(input, temp);
+ __ j(kAboveEqual, &done);
+ // if input == NaN goto nan
+ __ j(kUnordered, &nan);
+ // output = float-to-long-truncate(input)
+ __ cvttss2si(output, input, true);
+ __ jmp(&done);
+ __ Bind(&nan);
+ // output = 0
+ __ xorq(output, output);
+ __ Bind(&done);
break;
+ }
+
+ case Primitive::kPrimDouble: {
+ // Processing a Dex `double-to-long' instruction.
+ XmmRegister input = in.AsFpuRegister<XmmRegister>();
+ CpuRegister output = out.AsRegister<CpuRegister>();
+ XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ Label done, nan;
+
+ __ movq(output, Immediate(kPrimLongMax));
+ // temp = long-to-double(output)
+ __ cvtsi2sd(temp, output, true);
+ // if input >= temp goto done
+ __ comisd(input, temp);
+ __ j(kAboveEqual, &done);
+ // if input == NaN goto nan
+ __ j(kUnordered, &nan);
+ // output = double-to-long-truncate(input)
+ __ cvttsd2si(output, input, true);
+ __ jmp(&done);
+ __ Bind(&nan);
+ // output = 0
+ __ xorq(output, output);
+ __ Bind(&done);
+ break;
+ }
default:
LOG(FATAL) << "Unexpected type conversion from " << input_type
@@ -1656,8 +1808,8 @@
break;
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ // Processing a Dex `double-to-float' instruction.
+ __ cvtsd2ss(out.AsFpuRegister<XmmRegister>(), in.AsFpuRegister<XmmRegister>());
break;
default:
@@ -1682,8 +1834,8 @@
break;
case Primitive::kPrimFloat:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ // Processing a Dex `float-to-double' instruction.
+ __ cvtss2sd(out.AsFpuRegister<XmmRegister>(), in.AsFpuRegister<XmmRegister>());
break;
default:
@@ -1704,8 +1856,8 @@
switch (add->GetResultType()) {
case Primitive::kPrimInt: {
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::Any());
- locations->SetOut(Location::SameAsFirstInput());
+ locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
break;
}
@@ -1733,16 +1885,27 @@
LocationSummary* locations = add->GetLocations();
Location first = locations->InAt(0);
Location second = locations->InAt(1);
- DCHECK(first.Equals(locations->Out()));
+ Location out = locations->Out();
switch (add->GetResultType()) {
case Primitive::kPrimInt: {
if (second.IsRegister()) {
- __ addl(first.AsRegister<CpuRegister>(), second.AsRegister<CpuRegister>());
+ if (out.AsRegister<Register>() == first.AsRegister<Register>()) {
+ __ addl(out.AsRegister<CpuRegister>(), second.AsRegister<CpuRegister>());
+ } else {
+ __ leal(out.AsRegister<CpuRegister>(), Address(
+ first.AsRegister<CpuRegister>(), second.AsRegister<CpuRegister>(), TIMES_1, 0));
+ }
} else if (second.IsConstant()) {
- Immediate imm(second.GetConstant()->AsIntConstant()->GetValue());
- __ addl(first.AsRegister<CpuRegister>(), imm);
+ if (out.AsRegister<Register>() == first.AsRegister<Register>()) {
+ __ addl(out.AsRegister<CpuRegister>(),
+ Immediate(second.GetConstant()->AsIntConstant()->GetValue()));
+ } else {
+ __ leal(out.AsRegister<CpuRegister>(), Address(
+ first.AsRegister<CpuRegister>(), second.GetConstant()->AsIntConstant()->GetValue()));
+ }
} else {
+ DCHECK(first.Equals(locations->Out()));
__ addl(first.AsRegister<CpuRegister>(), Address(CpuRegister(RSP), second.GetStackIndex()));
}
break;
@@ -1901,6 +2064,81 @@
}
}
+void InstructionCodeGeneratorX86_64::PushOntoFPStack(Location source, uint32_t temp_offset,
+ uint32_t stack_adjustment, bool is_float) {
+ if (source.IsStackSlot()) {
+ DCHECK(is_float);
+ __ flds(Address(CpuRegister(RSP), source.GetStackIndex() + stack_adjustment));
+ } else if (source.IsDoubleStackSlot()) {
+ DCHECK(!is_float);
+ __ fldl(Address(CpuRegister(RSP), source.GetStackIndex() + stack_adjustment));
+ } else {
+ // Write the value to the temporary location on the stack and load to FP stack.
+ if (is_float) {
+ Location stack_temp = Location::StackSlot(temp_offset);
+ codegen_->Move(stack_temp, source);
+ __ flds(Address(CpuRegister(RSP), temp_offset));
+ } else {
+ Location stack_temp = Location::DoubleStackSlot(temp_offset);
+ codegen_->Move(stack_temp, source);
+ __ fldl(Address(CpuRegister(RSP), temp_offset));
+ }
+ }
+}
+
+void InstructionCodeGeneratorX86_64::GenerateRemFP(HRem *rem) {
+ Primitive::Type type = rem->GetResultType();
+ bool is_float = type == Primitive::kPrimFloat;
+ size_t elem_size = Primitive::ComponentSize(type);
+ LocationSummary* locations = rem->GetLocations();
+ Location first = locations->InAt(0);
+ Location second = locations->InAt(1);
+ Location out = locations->Out();
+
+ // Create stack space for 2 elements.
+ // TODO: enhance register allocator to ask for stack temporaries.
+ __ subq(CpuRegister(RSP), Immediate(2 * elem_size));
+
+ // Load the values to the FP stack in reverse order, using temporaries if needed.
+ PushOntoFPStack(second, elem_size, 2 * elem_size, is_float);
+ PushOntoFPStack(first, 0, 2 * elem_size, is_float);
+
+ // Loop doing FPREM until we stabilize.
+ Label retry;
+ __ Bind(&retry);
+ __ fprem();
+
+ // Move FP status to AX.
+ __ fstsw();
+
+ // And see if the argument reduction is complete. This is signaled by the
+ // C2 FPU flag bit set to 0.
+ __ andl(CpuRegister(RAX), Immediate(kC2ConditionMask));
+ __ j(kNotEqual, &retry);
+
+ // We have settled on the final value. Retrieve it into an XMM register.
+ // Store FP top of stack to real stack.
+ if (is_float) {
+ __ fsts(Address(CpuRegister(RSP), 0));
+ } else {
+ __ fstl(Address(CpuRegister(RSP), 0));
+ }
+
+ // Pop the 2 items from the FP stack.
+ __ fucompp();
+
+ // Load the value from the stack into an XMM register.
+ DCHECK(out.IsFpuRegister()) << out;
+ if (is_float) {
+ __ movss(out.AsFpuRegister<XmmRegister>(), Address(CpuRegister(RSP), 0));
+ } else {
+ __ movsd(out.AsFpuRegister<XmmRegister>(), Address(CpuRegister(RSP), 0));
+ }
+
+ // And remove the temporary stack space we allocated.
+ __ addq(CpuRegister(RSP), Immediate(2 * elem_size));
+}
+
void InstructionCodeGeneratorX86_64::GenerateDivRemIntegral(HBinaryOperation* instruction) {
DCHECK(instruction->IsDiv() || instruction->IsRem());
Primitive::Type type = instruction->GetResultType();
@@ -1923,16 +2161,16 @@
// 0x80000000(00000000)/-1 triggers an arithmetic exception!
// Dividing by -1 is actually negation and -0x800000000(00000000) = 0x80000000(00000000)
// so it's safe to just use negl instead of more complex comparisons.
-
- __ cmpl(second_reg, Immediate(-1));
- __ j(kEqual, slow_path->GetEntryLabel());
-
if (type == Primitive::kPrimInt) {
+ __ cmpl(second_reg, Immediate(-1));
+ __ j(kEqual, slow_path->GetEntryLabel());
// edx:eax <- sign-extended of eax
__ cdq();
// eax = quotient, edx = remainder
__ idivl(second_reg);
} else {
+ __ cmpq(second_reg, Immediate(-1));
+ __ j(kEqual, slow_path->GetEntryLabel());
// rdx:rax <- sign-extended of rax
__ cqo();
// rax = quotient, rdx = remainder
@@ -1999,9 +2237,11 @@
}
void LocationsBuilderX86_64::VisitRem(HRem* rem) {
+ Primitive::Type type = rem->GetResultType();
LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(rem, LocationSummary::kNoCall);
- switch (rem->GetResultType()) {
+ new (GetGraph()->GetArena()) LocationSummary(rem, LocationSummary::kNoCall);
+
+ switch (type) {
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RegisterLocation(RAX));
@@ -2013,12 +2253,15 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
- LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType();
+ locations->SetInAt(0, Location::Any());
+ locations->SetInAt(1, Location::Any());
+ locations->SetOut(Location::RequiresFpuRegister());
+ locations->AddTemp(Location::RegisterLocation(RAX));
break;
}
default:
- LOG(FATAL) << "Unexpected rem type " << rem->GetResultType();
+ LOG(FATAL) << "Unexpected rem type " << type;
}
}
@@ -2030,13 +2273,11 @@
GenerateDivRemIntegral(rem);
break;
}
-
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
- LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType();
+ GenerateRemFP(rem);
break;
}
-
default:
LOG(FATAL) << "Unexpected rem type " << rem->GetResultType();
}
@@ -2134,7 +2375,7 @@
__ shrl(first_reg, second_reg);
}
} else {
- Immediate imm(second.GetConstant()->AsIntConstant()->GetValue());
+ Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftValue);
if (op->IsShl()) {
__ shll(first_reg, imm);
} else if (op->IsShr()) {
@@ -2156,7 +2397,7 @@
__ shrq(first_reg, second_reg);
}
} else {
- Immediate imm(second.GetConstant()->AsIntConstant()->GetValue());
+ Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftValue);
if (op->IsShl()) {
__ shlq(first_reg, imm);
} else if (op->IsShr()) {
@@ -2210,8 +2451,8 @@
codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
__ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex()));
- __ gs()->call(Address::Absolute(
- QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocObjectWithAccessCheck), true));
+ __ gs()->call(
+ Address::Absolute(GetThreadOffset<kX86_64WordSize>(instruction->GetEntrypoint()), true));
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
@@ -2222,18 +2463,18 @@
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
- locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+ locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
locations->SetOut(Location::RegisterLocation(RAX));
- locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+ locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
}
void InstructionCodeGeneratorX86_64::VisitNewArray(HNewArray* instruction) {
InvokeRuntimeCallingConvention calling_convention;
- codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
+ codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(2)));
__ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex()));
- __ gs()->call(Address::Absolute(
- QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocArrayWithAccessCheck), true));
+ __ gs()->call(
+ Address::Absolute(GetThreadOffset<kX86_64WordSize>(instruction->GetEntrypoint()), true));
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
@@ -2269,10 +2510,6 @@
locations->Out().AsRegister<CpuRegister>().AsRegister());
Location out = locations->Out();
switch (not_->InputAt(0)->GetType()) {
- case Primitive::kPrimBoolean:
- __ xorq(out.AsRegister<CpuRegister>(), Immediate(1));
- break;
-
case Primitive::kPrimInt:
__ notl(out.AsRegister<CpuRegister>());
break;
@@ -2300,12 +2537,111 @@
LOG(FATAL) << "Unimplemented";
}
-void LocationsBuilderX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+void InstructionCodeGeneratorX86_64::GenerateMemoryBarrier(MemBarrierKind kind) {
+ /*
+ * According to the JSR-133 Cookbook, for x86 only StoreLoad/AnyAny barriers need memory fence.
+ * All other barriers (LoadAny, AnyStore, StoreStore) are nops due to the x86 memory model.
+ * For those cases, all we need to ensure is that there is a scheduling barrier in place.
+ */
+ switch (kind) {
+ case MemBarrierKind::kAnyAny: {
+ __ mfence();
+ break;
+ }
+ case MemBarrierKind::kAnyStore:
+ case MemBarrierKind::kLoadAny:
+ case MemBarrierKind::kStoreStore: {
+ // nop
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected memory barier " << kind;
+ }
+}
+
+void LocationsBuilderX86_64::HandleFieldGet(HInstruction* instruction) {
+ DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
+
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- Primitive::Type field_type = instruction->GetFieldType();
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorX86_64::HandleFieldGet(HInstruction* instruction,
+ const FieldInfo& field_info) {
+ DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
+
+ LocationSummary* locations = instruction->GetLocations();
+ CpuRegister base = locations->InAt(0).AsRegister<CpuRegister>();
+ Location out = locations->Out();
+ bool is_volatile = field_info.IsVolatile();
+ Primitive::Type field_type = field_info.GetFieldType();
+ uint32_t offset = field_info.GetFieldOffset().Uint32Value();
+
+ switch (field_type) {
+ case Primitive::kPrimBoolean: {
+ __ movzxb(out.AsRegister<CpuRegister>(), Address(base, offset));
+ break;
+ }
+
+ case Primitive::kPrimByte: {
+ __ movsxb(out.AsRegister<CpuRegister>(), Address(base, offset));
+ break;
+ }
+
+ case Primitive::kPrimShort: {
+ __ movsxw(out.AsRegister<CpuRegister>(), Address(base, offset));
+ break;
+ }
+
+ case Primitive::kPrimChar: {
+ __ movzxw(out.AsRegister<CpuRegister>(), Address(base, offset));
+ break;
+ }
+
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ __ movl(out.AsRegister<CpuRegister>(), Address(base, offset));
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ __ movq(out.AsRegister<CpuRegister>(), Address(base, offset));
+ break;
+ }
+
+ case Primitive::kPrimFloat: {
+ __ movss(out.AsFpuRegister<XmmRegister>(), Address(base, offset));
+ break;
+ }
+
+ case Primitive::kPrimDouble: {
+ __ movsd(out.AsFpuRegister<XmmRegister>(), Address(base, offset));
+ break;
+ }
+
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << field_type;
+ UNREACHABLE();
+ }
+
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+
+ if (is_volatile) {
+ GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
+ }
+}
+
+void LocationsBuilderX86_64::HandleFieldSet(HInstruction* instruction,
+ const FieldInfo& field_info) {
+ DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
+
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
bool needs_write_barrier =
- CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue());
+ CodeGenerator::StoreNeedsWriteBarrier(field_info.GetFieldType(), instruction->InputAt(1));
+
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
if (needs_write_barrier) {
@@ -2315,54 +2651,52 @@
}
}
-void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction,
+ const FieldInfo& field_info) {
+ DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
+
LocationSummary* locations = instruction->GetLocations();
- CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>();
- size_t offset = instruction->GetFieldOffset().SizeValue();
- Primitive::Type field_type = instruction->GetFieldType();
+ CpuRegister base = locations->InAt(0).AsRegister<CpuRegister>();
+ Location value = locations->InAt(1);
+ bool is_volatile = field_info.IsVolatile();
+ Primitive::Type field_type = field_info.GetFieldType();
+ uint32_t offset = field_info.GetFieldOffset().Uint32Value();
+
+ if (is_volatile) {
+ GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
+ }
switch (field_type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte: {
- CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
- __ movb(Address(obj, offset), value);
+ __ movb(Address(base, offset), value.AsRegister<CpuRegister>());
break;
}
case Primitive::kPrimShort:
case Primitive::kPrimChar: {
- CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
- __ movw(Address(obj, offset), value);
+ __ movw(Address(base, offset), value.AsRegister<CpuRegister>());
break;
}
case Primitive::kPrimInt:
case Primitive::kPrimNot: {
- CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
- __ movl(Address(obj, offset), value);
- if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue())) {
- CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
- CpuRegister card = locations->GetTemp(1).AsRegister<CpuRegister>();
- codegen_->MarkGCCard(temp, card, obj, value);
- }
+ __ movl(Address(base, offset), value.AsRegister<CpuRegister>());
break;
}
case Primitive::kPrimLong: {
- CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
- __ movq(Address(obj, offset), value);
+ __ movq(Address(base, offset), value.AsRegister<CpuRegister>());
break;
}
case Primitive::kPrimFloat: {
- XmmRegister value = locations->InAt(1).AsFpuRegister<XmmRegister>();
- __ movss(Address(obj, offset), value);
+ __ movss(Address(base, offset), value.AsFpuRegister<XmmRegister>());
break;
}
case Primitive::kPrimDouble: {
- XmmRegister value = locations->InAt(1).AsFpuRegister<XmmRegister>();
- __ movsd(Address(obj, offset), value);
+ __ movsd(Address(base, offset), value.AsFpuRegister<XmmRegister>());
break;
}
@@ -2370,86 +2704,76 @@
LOG(FATAL) << "Unreachable type " << field_type;
UNREACHABLE();
}
+
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
+ CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
+ CpuRegister card = locations->GetTemp(1).AsRegister<CpuRegister>();
+ codegen_->MarkGCCard(temp, card, base, value.AsRegister<CpuRegister>());
+ }
+
+ if (is_volatile) {
+ GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+ }
+}
+
+void LocationsBuilderX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+ HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+ HandleFieldSet(instruction, instruction->GetFieldInfo());
}
void LocationsBuilderX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ HandleFieldGet(instruction);
}
void InstructionCodeGeneratorX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
- LocationSummary* locations = instruction->GetLocations();
- CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>();
- size_t offset = instruction->GetFieldOffset().SizeValue();
+ HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
- switch (instruction->GetType()) {
- case Primitive::kPrimBoolean: {
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
- __ movzxb(out, Address(obj, offset));
- break;
- }
+void LocationsBuilderX86_64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+ HandleFieldGet(instruction);
+}
- case Primitive::kPrimByte: {
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
- __ movsxb(out, Address(obj, offset));
- break;
- }
+void InstructionCodeGeneratorX86_64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+ HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
- case Primitive::kPrimShort: {
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
- __ movsxw(out, Address(obj, offset));
- break;
- }
+void LocationsBuilderX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+ HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
- case Primitive::kPrimChar: {
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
- __ movzxw(out, Address(obj, offset));
- break;
- }
-
- case Primitive::kPrimInt:
- case Primitive::kPrimNot: {
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
- __ movl(out, Address(obj, offset));
- break;
- }
-
- case Primitive::kPrimLong: {
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
- __ movq(out, Address(obj, offset));
- break;
- }
-
- case Primitive::kPrimFloat: {
- XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
- __ movss(out, Address(obj, offset));
- break;
- }
-
- case Primitive::kPrimDouble: {
- XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
- __ movsd(out, Address(obj, offset));
- break;
- }
-
- case Primitive::kPrimVoid:
- LOG(FATAL) << "Unreachable type " << instruction->GetType();
- UNREACHABLE();
- }
+void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+ HandleFieldSet(instruction, instruction->GetFieldInfo());
}
void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- locations->SetInAt(0, Location::Any());
+ Location loc = codegen_->GetCompilerOptions().GetImplicitNullChecks()
+ ? Location::RequiresRegister()
+ : Location::Any();
+ locations->SetInAt(0, loc);
if (instruction->HasUses()) {
locations->SetOut(Location::SameAsFirstInput());
}
}
-void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) {
+void InstructionCodeGeneratorX86_64::GenerateImplicitNullCheck(HNullCheck* instruction) {
+ if (codegen_->CanMoveNullCheckToUser(instruction)) {
+ return;
+ }
+ LocationSummary* locations = instruction->GetLocations();
+ Location obj = locations->InAt(0);
+
+ __ testl(CpuRegister(RAX), Address(obj.AsRegister<CpuRegister>(), 0));
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+}
+
+void InstructionCodeGeneratorX86_64::GenerateExplicitNullCheck(HNullCheck* instruction) {
SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction);
codegen_->AddSlowPath(slow_path);
@@ -2457,7 +2781,7 @@
Location obj = locations->InAt(0);
if (obj.IsRegister()) {
- __ cmpl(obj.AsRegister<CpuRegister>(), Immediate(0));
+ __ testl(obj.AsRegister<CpuRegister>(), obj.AsRegister<CpuRegister>());
} else if (obj.IsStackSlot()) {
__ cmpl(Address(CpuRegister(RSP), obj.GetStackIndex()), Immediate(0));
} else {
@@ -2469,6 +2793,14 @@
__ j(kEqual, slow_path->GetEntryLabel());
}
+void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) {
+ if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
+ GenerateImplicitNullCheck(instruction);
+ } else {
+ GenerateExplicitNullCheck(instruction);
+ }
+}
+
void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
@@ -2586,6 +2918,7 @@
LOG(FATAL) << "Unreachable type " << instruction->GetType();
UNREACHABLE();
}
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
}
void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) {
@@ -2654,6 +2987,7 @@
Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
}
}
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
@@ -2680,6 +3014,7 @@
Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
}
}
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
@@ -2708,7 +3043,7 @@
Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
}
}
-
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
if (needs_write_barrier) {
DCHECK_EQ(value_type, Primitive::kPrimNot);
CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
@@ -2736,6 +3071,7 @@
__ movq(Address(obj, index.AsRegister<CpuRegister>(), TIMES_8, data_offset),
value.AsRegister<CpuRegister>());
}
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
@@ -2750,6 +3086,7 @@
__ movss(Address(obj, index.AsRegister<CpuRegister>(), TIMES_4, data_offset),
value.AsFpuRegister<XmmRegister>());
}
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
@@ -2764,6 +3101,7 @@
__ movsd(Address(obj, index.AsRegister<CpuRegister>(), TIMES_8, data_offset),
value.AsFpuRegister<XmmRegister>());
}
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
@@ -2786,6 +3124,7 @@
CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>();
CpuRegister out = locations->Out().AsRegister<CpuRegister>();
__ movl(out, Address(obj, offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
}
void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
@@ -2925,12 +3264,16 @@
} else if (source.IsConstant()) {
HConstant* constant = source.GetConstant();
if (constant->IsIntConstant()) {
- Immediate imm(constant->AsIntConstant()->GetValue());
+ int32_t value = constant->AsIntConstant()->GetValue();
if (destination.IsRegister()) {
- __ movl(destination.AsRegister<CpuRegister>(), imm);
+ if (value == 0) {
+ __ xorl(destination.AsRegister<CpuRegister>(), destination.AsRegister<CpuRegister>());
+ } else {
+ __ movl(destination.AsRegister<CpuRegister>(), Immediate(value));
+ }
} else {
DCHECK(destination.IsStackSlot()) << destination;
- __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), imm);
+ __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), Immediate(value));
}
} else if (constant->IsLongConstant()) {
int64_t value = constant->AsLongConstant()->GetValue();
@@ -3133,146 +3476,6 @@
check->GetLocations()->InAt(0).AsRegister<CpuRegister>());
}
-void LocationsBuilderX86_64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-void InstructionCodeGeneratorX86_64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
- LocationSummary* locations = instruction->GetLocations();
- CpuRegister cls = locations->InAt(0).AsRegister<CpuRegister>();
- size_t offset = instruction->GetFieldOffset().SizeValue();
-
- switch (instruction->GetType()) {
- case Primitive::kPrimBoolean: {
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
- __ movzxb(out, Address(cls, offset));
- break;
- }
-
- case Primitive::kPrimByte: {
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
- __ movsxb(out, Address(cls, offset));
- break;
- }
-
- case Primitive::kPrimShort: {
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
- __ movsxw(out, Address(cls, offset));
- break;
- }
-
- case Primitive::kPrimChar: {
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
- __ movzxw(out, Address(cls, offset));
- break;
- }
-
- case Primitive::kPrimInt:
- case Primitive::kPrimNot: {
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
- __ movl(out, Address(cls, offset));
- break;
- }
-
- case Primitive::kPrimLong: {
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
- __ movq(out, Address(cls, offset));
- break;
- }
-
- case Primitive::kPrimFloat: {
- XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
- __ movss(out, Address(cls, offset));
- break;
- }
-
- case Primitive::kPrimDouble: {
- XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
- __ movsd(out, Address(cls, offset));
- break;
- }
-
- case Primitive::kPrimVoid:
- LOG(FATAL) << "Unreachable type " << instruction->GetType();
- UNREACHABLE();
- }
-}
-
-void LocationsBuilderX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- Primitive::Type field_type = instruction->GetFieldType();
- bool needs_write_barrier =
- CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue());
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- if (needs_write_barrier) {
- // Temporary registers for the write barrier.
- locations->AddTemp(Location::RequiresRegister());
- locations->AddTemp(Location::RequiresRegister());
- }
-}
-
-void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- LocationSummary* locations = instruction->GetLocations();
- CpuRegister cls = locations->InAt(0).AsRegister<CpuRegister>();
- size_t offset = instruction->GetFieldOffset().SizeValue();
- Primitive::Type field_type = instruction->GetFieldType();
-
- switch (field_type) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte: {
- CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
- __ movb(Address(cls, offset), value);
- break;
- }
-
- case Primitive::kPrimShort:
- case Primitive::kPrimChar: {
- CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
- __ movw(Address(cls, offset), value);
- break;
- }
-
- case Primitive::kPrimInt:
- case Primitive::kPrimNot: {
- CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
- __ movl(Address(cls, offset), value);
- if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue())) {
- CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
- CpuRegister card = locations->GetTemp(1).AsRegister<CpuRegister>();
- codegen_->MarkGCCard(temp, card, cls, value);
- }
- break;
- }
-
- case Primitive::kPrimLong: {
- CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
- __ movq(Address(cls, offset), value);
- break;
- }
-
- case Primitive::kPrimFloat: {
- XmmRegister value = locations->InAt(1).AsFpuRegister<XmmRegister>();
- __ movss(Address(cls, offset), value);
- break;
- }
-
- case Primitive::kPrimDouble: {
- XmmRegister value = locations->InAt(1).AsFpuRegister<XmmRegister>();
- __ movsd(Address(cls, offset), value);
- break;
- }
-
- case Primitive::kPrimVoid:
- LOG(FATAL) << "Unreachable type " << field_type;
- UNREACHABLE();
- }
-}
-
void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 794b81f..dbdbf86 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -18,6 +18,8 @@
#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_X86_64_H_
#include "code_generator.h"
+#include "dex/compiler_enums.h"
+#include "driver/compiler_options.h"
#include "nodes.h"
#include "parallel_move_resolver.h"
#include "utils/x86_64/assembler_x86_64.h"
@@ -66,7 +68,20 @@
};
class CodeGeneratorX86_64;
-class SlowPathCodeX86_64;
+
+class SlowPathCodeX86_64 : public SlowPathCode {
+ public:
+ SlowPathCodeX86_64() : entry_label_(), exit_label_() {}
+
+ Label* GetEntryLabel() { return &entry_label_; }
+ Label* GetExitLabel() { return &exit_label_; }
+
+ private:
+ Label entry_label_;
+ Label exit_label_;
+
+ DISALLOW_COPY_AND_ASSIGN(SlowPathCodeX86_64);
+};
class ParallelMoveResolverX86_64 : public ParallelMoveResolver {
public:
@@ -109,6 +124,8 @@
void HandleInvoke(HInvoke* invoke);
void HandleBitwiseOperation(HBinaryOperation* operation);
void HandleShift(HBinaryOperation* operation);
+ void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+ void HandleFieldGet(HInstruction* instruction);
CodeGeneratorX86_64* const codegen_;
InvokeDexCallingConventionVisitor parameter_visitor_;
@@ -136,8 +153,16 @@
void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
void GenerateClassInitializationCheck(SlowPathCodeX86_64* slow_path, CpuRegister class_reg);
void HandleBitwiseOperation(HBinaryOperation* operation);
+ void GenerateRemFP(HRem *rem);
void GenerateDivRemIntegral(HBinaryOperation* instruction);
void HandleShift(HBinaryOperation* operation);
+ void GenerateMemoryBarrier(MemBarrierKind kind);
+ void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+ void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
+ void GenerateImplicitNullCheck(HNullCheck* instruction);
+ void GenerateExplicitNullCheck(HNullCheck* instruction);
+ void PushOntoFPStack(Location source, uint32_t temp_offset,
+ uint32_t stack_adjustment, bool is_float);
X86_64Assembler* const assembler_;
CodeGeneratorX86_64* const codegen_;
@@ -147,7 +172,7 @@
class CodeGeneratorX86_64 : public CodeGenerator {
public:
- explicit CodeGeneratorX86_64(HGraph* graph);
+ CodeGeneratorX86_64(HGraph* graph, const CompilerOptions& compiler_options);
virtual ~CodeGeneratorX86_64() {}
void GenerateFrameEntry() OVERRIDE;
@@ -163,7 +188,9 @@
return kX86_64WordSize;
}
- size_t FrameEntrySpillSize() const OVERRIDE;
+ size_t GetFloatingPointSpillSlotSize() const OVERRIDE {
+ return kX86_64WordSize;
+ }
HGraphVisitor* GetLocationBuilder() OVERRIDE {
return &location_builder_;
@@ -187,7 +214,7 @@
Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
- void SetupBlockedRegisters() const OVERRIDE;
+ void SetupBlockedRegisters(bool is_baseline) const OVERRIDE;
Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE;
void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
@@ -212,9 +239,16 @@
block_labels_.SetSize(GetGraph()->GetBlocks().Size());
}
+ bool NeedsTwoRegisters(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+ return false;
+ }
+
+ void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, CpuRegister temp);
+
private:
// Labels for each block that will be compiled.
GrowableArray<Label> block_labels_;
+ Label frame_entry_label_;
LocationsBuilderX86_64 location_builder_;
InstructionCodeGeneratorX86_64 instruction_visitor_;
ParallelMoveResolverX86_64 move_resolver_;
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index fee3ea6..e0e0b4c 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -17,6 +17,7 @@
#include <functional>
#include "arch/instruction_set.h"
+#include "arch/arm/instruction_set_features_arm.h"
#include "base/macros.h"
#include "builder.h"
#include "code_generator_arm.h"
@@ -26,6 +27,7 @@
#include "common_compiler_test.h"
#include "dex_file.h"
#include "dex_instruction.h"
+#include "driver/compiler_options.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "prepare_for_register_allocation.h"
@@ -37,6 +39,31 @@
namespace art {
+// Provide our own codegen, that ensures the C calling conventions
+// are preserved. Currently, ART and C do not match as R4 is caller-save
+// in ART, and callee-save in C. Alternatively, we could use or write
+// the stub that saves and restores all registers, but it is easier
+// to just overwrite the code generator.
+class TestCodeGeneratorARM : public arm::CodeGeneratorARM {
+ public:
+ TestCodeGeneratorARM(HGraph* graph,
+ const ArmInstructionSetFeatures& isa_features,
+ const CompilerOptions& compiler_options)
+ : arm::CodeGeneratorARM(graph, isa_features, compiler_options) {
+ AddAllocatedRegister(Location::RegisterLocation(6));
+ AddAllocatedRegister(Location::RegisterLocation(7));
+ }
+
+ void SetupBlockedRegisters(bool is_baseline) const OVERRIDE {
+ arm::CodeGeneratorARM::SetupBlockedRegisters(is_baseline);
+ blocked_core_registers_[4] = true;
+ blocked_core_registers_[6] = false;
+ blocked_core_registers_[7] = false;
+ // Makes pair R6-R7 available.
+ blocked_register_pairs_[6 >> 1] = false;
+ }
+};
+
class InternalCodeAllocator : public CodeAllocator {
public:
InternalCodeAllocator() : size_(0) { }
@@ -79,7 +106,8 @@
static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) {
InternalCodeAllocator allocator;
- x86::CodeGeneratorX86 codegenX86(graph);
+ CompilerOptions compiler_options;
+ x86::CodeGeneratorX86 codegenX86(graph, compiler_options);
// We avoid doing a stack overflow check that requires the runtime being setup,
// by making sure the compiler knows the methods we are running are leaf methods.
codegenX86.CompileBaseline(&allocator, true);
@@ -87,19 +115,21 @@
Run(allocator, codegenX86, has_result, expected);
}
- arm::CodeGeneratorARM codegenARM(graph);
+ std::unique_ptr<const ArmInstructionSetFeatures> features(
+ ArmInstructionSetFeatures::FromCppDefines());
+ TestCodeGeneratorARM codegenARM(graph, *features.get(), compiler_options);
codegenARM.CompileBaseline(&allocator, true);
if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) {
Run(allocator, codegenARM, has_result, expected);
}
- x86_64::CodeGeneratorX86_64 codegenX86_64(graph);
+ x86_64::CodeGeneratorX86_64 codegenX86_64(graph, compiler_options);
codegenX86_64.CompileBaseline(&allocator, true);
if (kRuntimeISA == kX86_64) {
Run(allocator, codegenX86_64, has_result, expected);
}
- arm64::CodeGeneratorARM64 codegenARM64(graph);
+ arm64::CodeGeneratorARM64 codegenARM64(graph, compiler_options);
codegenARM64.CompileBaseline(&allocator, true);
if (kRuntimeISA == kArm64) {
Run(allocator, codegenARM64, has_result, expected);
@@ -129,14 +159,20 @@
std::function<void(HGraph*)> hook_before_codegen,
bool has_result,
Expected expected) {
- if (kRuntimeISA == kX86) {
- x86::CodeGeneratorX86 codegenX86(graph);
- RunCodeOptimized(&codegenX86, graph, hook_before_codegen, has_result, expected);
- } else if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) {
- arm::CodeGeneratorARM codegenARM(graph);
+ CompilerOptions compiler_options;
+ if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) {
+ TestCodeGeneratorARM codegenARM(graph,
+ *ArmInstructionSetFeatures::FromCppDefines(),
+ compiler_options);
RunCodeOptimized(&codegenARM, graph, hook_before_codegen, has_result, expected);
+ } else if (kRuntimeISA == kArm64) {
+ arm64::CodeGeneratorARM64 codegenARM64(graph, compiler_options);
+ RunCodeOptimized(&codegenARM64, graph, hook_before_codegen, has_result, expected);
+ } else if (kRuntimeISA == kX86) {
+ x86::CodeGeneratorX86 codegenX86(graph, compiler_options);
+ RunCodeOptimized(&codegenX86, graph, hook_before_codegen, has_result, expected);
} else if (kRuntimeISA == kX86_64) {
- x86_64::CodeGeneratorX86_64 codegenX86_64(graph);
+ x86_64::CodeGeneratorX86_64 codegenX86_64(graph, compiler_options);
RunCodeOptimized(&codegenX86_64, graph, hook_before_codegen, has_result, expected);
}
}
@@ -144,10 +180,11 @@
static void TestCode(const uint16_t* data, bool has_result = false, int32_t expected = 0) {
ArenaPool pool;
ArenaAllocator arena(&pool);
- HGraphBuilder builder(&arena);
+ HGraph* graph = new (&arena) HGraph(&arena);
+ HGraphBuilder builder(graph);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- HGraph* graph = builder.BuildGraph(*item);
- ASSERT_NE(graph, nullptr);
+ bool graph_built = builder.BuildGraph(*item);
+ ASSERT_TRUE(graph_built);
// Remove suspend checks, they cannot be executed in this context.
RemoveSuspendChecks(graph);
RunCodeBaseline(graph, has_result, expected);
@@ -156,10 +193,11 @@
static void TestCodeLong(const uint16_t* data, bool has_result, int64_t expected) {
ArenaPool pool;
ArenaAllocator arena(&pool);
- HGraphBuilder builder(&arena, Primitive::kPrimLong);
+ HGraph* graph = new (&arena) HGraph(&arena);
+ HGraphBuilder builder(graph, Primitive::kPrimLong);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- HGraph* graph = builder.BuildGraph(*item);
- ASSERT_NE(graph, nullptr);
+ bool graph_built = builder.BuildGraph(*item);
+ ASSERT_TRUE(graph_built);
// Remove suspend checks, they cannot be executed in this context.
RemoveSuspendChecks(graph);
RunCodeBaseline(graph, has_result, expected);
@@ -362,11 +400,7 @@
#undef NOT_LONG_TEST
-#if defined(__aarch64__)
-TEST(CodegenTest, DISABLED_IntToLongOfLongToInt) {
-#else
TEST(CodegenTest, IntToLongOfLongToInt) {
-#endif
const int64_t input = INT64_C(4294967296); // 2^32
const uint16_t word0 = Low16Bits(Low32Bits(input)); // LSW.
const uint16_t word1 = High16Bits(Low32Bits(input));
@@ -493,10 +527,8 @@
TestCode(data, true, 12); \
}
-#if !defined(__aarch64__)
MUL_TEST(INT, MulInt);
MUL_TEST(LONG, MulLong);
-#endif
TEST(CodegenTest, ReturnMulIntLit8) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
@@ -633,11 +665,7 @@
}
}
-#if defined(__aarch64__)
-TEST(CodegenTest, DISABLED_ReturnDivIntLit8) {
-#else
TEST(CodegenTest, ReturnDivIntLit8) {
-#endif
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 4 << 12 | 0 << 8,
Instruction::DIV_INT_LIT8, 3 << 8 | 0,
@@ -646,11 +674,7 @@
TestCode(data, true, 1);
}
-#if defined(__aarch64__)
-TEST(CodegenTest, DISABLED_ReturnDivInt2Addr) {
-#else
TEST(CodegenTest, ReturnDivInt2Addr) {
-#endif
const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 4 << 12 | 0,
Instruction::CONST_4 | 2 << 12 | 1 << 8,
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
new file mode 100644
index 0000000..007324e
--- /dev/null
+++ b/compiler/optimizing/common_arm64.h
@@ -0,0 +1,184 @@
+/*
+ * 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_COMMON_ARM64_H_
+#define ART_COMPILER_OPTIMIZING_COMMON_ARM64_H_
+
+#include "locations.h"
+#include "nodes.h"
+#include "utils/arm64/assembler_arm64.h"
+#include "a64/disasm-a64.h"
+#include "a64/macro-assembler-a64.h"
+
+namespace art {
+namespace arm64 {
+namespace helpers {
+
+// Convenience helpers to ease conversion to and from VIXL operands.
+static_assert((SP == 31) && (WSP == 31) && (XZR == 32) && (WZR == 32),
+ "Unexpected values for register codes.");
+
+static inline int VIXLRegCodeFromART(int code) {
+ if (code == SP) {
+ return vixl::kSPRegInternalCode;
+ }
+ if (code == XZR) {
+ return vixl::kZeroRegCode;
+ }
+ return code;
+}
+
+static inline int ARTRegCodeFromVIXL(int code) {
+ if (code == vixl::kSPRegInternalCode) {
+ return SP;
+ }
+ if (code == vixl::kZeroRegCode) {
+ return XZR;
+ }
+ return code;
+}
+
+static inline vixl::Register XRegisterFrom(Location location) {
+ DCHECK(location.IsRegister());
+ return vixl::Register::XRegFromCode(VIXLRegCodeFromART(location.reg()));
+}
+
+static inline vixl::Register WRegisterFrom(Location location) {
+ DCHECK(location.IsRegister());
+ return vixl::Register::WRegFromCode(VIXLRegCodeFromART(location.reg()));
+}
+
+static inline vixl::Register RegisterFrom(Location location, Primitive::Type type) {
+ DCHECK(type != Primitive::kPrimVoid && !Primitive::IsFloatingPointType(type));
+ return type == Primitive::kPrimLong ? XRegisterFrom(location) : WRegisterFrom(location);
+}
+
+static inline vixl::Register OutputRegister(HInstruction* instr) {
+ return RegisterFrom(instr->GetLocations()->Out(), instr->GetType());
+}
+
+static inline vixl::Register InputRegisterAt(HInstruction* instr, int input_index) {
+ return RegisterFrom(instr->GetLocations()->InAt(input_index),
+ instr->InputAt(input_index)->GetType());
+}
+
+static inline vixl::FPRegister DRegisterFrom(Location location) {
+ DCHECK(location.IsFpuRegister());
+ return vixl::FPRegister::DRegFromCode(location.reg());
+}
+
+static inline vixl::FPRegister SRegisterFrom(Location location) {
+ DCHECK(location.IsFpuRegister());
+ return vixl::FPRegister::SRegFromCode(location.reg());
+}
+
+static inline vixl::FPRegister FPRegisterFrom(Location location, Primitive::Type type) {
+ DCHECK(Primitive::IsFloatingPointType(type));
+ return type == Primitive::kPrimDouble ? DRegisterFrom(location) : SRegisterFrom(location);
+}
+
+static inline vixl::FPRegister OutputFPRegister(HInstruction* instr) {
+ return FPRegisterFrom(instr->GetLocations()->Out(), instr->GetType());
+}
+
+static inline vixl::FPRegister InputFPRegisterAt(HInstruction* instr, int input_index) {
+ return FPRegisterFrom(instr->GetLocations()->InAt(input_index),
+ instr->InputAt(input_index)->GetType());
+}
+
+static inline vixl::CPURegister CPURegisterFrom(Location location, Primitive::Type type) {
+ return Primitive::IsFloatingPointType(type) ? vixl::CPURegister(FPRegisterFrom(location, type))
+ : vixl::CPURegister(RegisterFrom(location, type));
+}
+
+static inline vixl::CPURegister OutputCPURegister(HInstruction* instr) {
+ return Primitive::IsFloatingPointType(instr->GetType())
+ ? static_cast<vixl::CPURegister>(OutputFPRegister(instr))
+ : static_cast<vixl::CPURegister>(OutputRegister(instr));
+}
+
+static inline vixl::CPURegister InputCPURegisterAt(HInstruction* instr, int index) {
+ return Primitive::IsFloatingPointType(instr->InputAt(index)->GetType())
+ ? static_cast<vixl::CPURegister>(InputFPRegisterAt(instr, index))
+ : static_cast<vixl::CPURegister>(InputRegisterAt(instr, index));
+}
+
+static inline int64_t Int64ConstantFrom(Location location) {
+ HConstant* instr = location.GetConstant();
+ return instr->IsIntConstant() ? instr->AsIntConstant()->GetValue()
+ : instr->AsLongConstant()->GetValue();
+}
+
+static inline vixl::Operand OperandFrom(Location location, Primitive::Type type) {
+ if (location.IsRegister()) {
+ return vixl::Operand(RegisterFrom(location, type));
+ } else {
+ return vixl::Operand(Int64ConstantFrom(location));
+ }
+}
+
+static inline vixl::Operand InputOperandAt(HInstruction* instr, int input_index) {
+ return OperandFrom(instr->GetLocations()->InAt(input_index),
+ instr->InputAt(input_index)->GetType());
+}
+
+static inline vixl::MemOperand StackOperandFrom(Location location) {
+ return vixl::MemOperand(vixl::sp, location.GetStackIndex());
+}
+
+static inline vixl::MemOperand HeapOperand(const vixl::Register& base, size_t offset = 0) {
+ // A heap reference must be 32bit, so fit in a W register.
+ DCHECK(base.IsW());
+ return vixl::MemOperand(base.X(), offset);
+}
+
+static inline vixl::MemOperand HeapOperand(const vixl::Register& base, Offset offset) {
+ return HeapOperand(base, offset.SizeValue());
+}
+
+static inline vixl::MemOperand HeapOperandFrom(Location location, Offset offset) {
+ return HeapOperand(RegisterFrom(location, Primitive::kPrimNot), offset);
+}
+
+static inline Location LocationFrom(const vixl::Register& reg) {
+ return Location::RegisterLocation(ARTRegCodeFromVIXL(reg.code()));
+}
+
+static inline Location LocationFrom(const vixl::FPRegister& fpreg) {
+ return Location::FpuRegisterLocation(fpreg.code());
+}
+
+static inline vixl::Operand OperandFromMemOperand(const vixl::MemOperand& mem_op) {
+ if (mem_op.IsImmediateOffset()) {
+ return vixl::Operand(mem_op.offset());
+ } else {
+ DCHECK(mem_op.IsRegisterOffset());
+ if (mem_op.extend() != vixl::NO_EXTEND) {
+ return vixl::Operand(mem_op.regoffset(), mem_op.extend(), mem_op.shift_amount());
+ } else if (mem_op.shift() != vixl::NO_SHIFT) {
+ return vixl::Operand(mem_op.regoffset(), mem_op.shift(), mem_op.shift_amount());
+ } else {
+ LOG(FATAL) << "Should not reach here";
+ UNREACHABLE();
+ }
+ }
+}
+
+} // namespace helpers
+} // namespace arm64
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_COMMON_ARM64_H_
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
index a56b9d9..6ceccfb 100644
--- a/compiler/optimizing/constant_folding_test.cc
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -19,6 +19,7 @@
#include "code_generator_x86.h"
#include "constant_folding.h"
#include "dead_code_elimination.h"
+#include "driver/compiler_options.h"
#include "graph_checker.h"
#include "optimizing_unit_test.h"
#include "pretty_printer.h"
@@ -38,19 +39,18 @@
HGraph* graph = CreateCFG(&allocator, data, return_type);
ASSERT_NE(graph, nullptr);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
+ graph->TryBuildingSsa();
StringPrettyPrinter printer_before(graph);
printer_before.VisitInsertionOrder();
std::string actual_before = printer_before.str();
ASSERT_EQ(expected_before, actual_before);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
HConstantFolding(graph).Run();
- SSAChecker ssa_checker(&allocator, graph);
- ssa_checker.Run();
- ASSERT_TRUE(ssa_checker.IsValid());
+ SSAChecker ssa_checker_cf(&allocator, graph);
+ ssa_checker_cf.Run();
+ ASSERT_TRUE(ssa_checker_cf.IsValid());
StringPrettyPrinter printer_after_cf(graph);
printer_after_cf.VisitInsertionOrder();
@@ -60,8 +60,9 @@
check_after_cf(graph);
HDeadCodeElimination(graph).Run();
- ssa_checker.Run();
- ASSERT_TRUE(ssa_checker.IsValid());
+ SSAChecker ssa_checker_dce(&allocator, graph);
+ ssa_checker_dce.Run();
+ ASSERT_TRUE(ssa_checker_dce.IsValid());
StringPrettyPrinter printer_after_dce(graph);
printer_after_dce.VisitInsertionOrder();
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
index 5d4b9cb..a644719 100644
--- a/compiler/optimizing/dead_code_elimination_test.cc
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -16,6 +16,7 @@
#include "code_generator_x86.h"
#include "dead_code_elimination.h"
+#include "driver/compiler_options.h"
#include "graph_checker.h"
#include "optimizing_unit_test.h"
#include "pretty_printer.h"
@@ -32,15 +33,14 @@
HGraph* graph = CreateCFG(&allocator, data);
ASSERT_NE(graph, nullptr);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
+ graph->TryBuildingSsa();
StringPrettyPrinter printer_before(graph);
printer_before.VisitInsertionOrder();
std::string actual_before = printer_before.str();
ASSERT_EQ(actual_before, expected_before);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
HDeadCodeElimination(graph).Run();
SSAChecker ssa_checker(&allocator, graph);
ssa_checker.Run();
diff --git a/compiler/optimizing/dominator_test.cc b/compiler/optimizing/dominator_test.cc
index 3062e37..b246c6f 100644
--- a/compiler/optimizing/dominator_test.cc
+++ b/compiler/optimizing/dominator_test.cc
@@ -27,10 +27,11 @@
static void TestCode(const uint16_t* data, const int* blocks, size_t blocks_length) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraphBuilder builder(&allocator);
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+ HGraphBuilder builder(graph);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- HGraph* graph = builder.BuildGraph(*item);
- ASSERT_NE(graph, nullptr);
+ bool graph_built = builder.BuildGraph(*item);
+ ASSERT_TRUE(graph_built);
graph->BuildDominatorTree();
ASSERT_EQ(graph->GetBlocks().Size(), blocks_length);
for (size_t i = 0, e = blocks_length; i < e; ++i) {
diff --git a/compiler/optimizing/find_loops_test.cc b/compiler/optimizing/find_loops_test.cc
index 82fe03c..e05d9b3 100644
--- a/compiler/optimizing/find_loops_test.cc
+++ b/compiler/optimizing/find_loops_test.cc
@@ -28,9 +28,10 @@
namespace art {
static HGraph* TestCode(const uint16_t* data, ArenaAllocator* allocator) {
- HGraphBuilder builder(allocator);
+ HGraph* graph = new (allocator) HGraph(allocator);
+ HGraphBuilder builder(graph);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- HGraph* graph = builder.BuildGraph(*item);
+ builder.BuildGraph(*item);
graph->BuildDominatorTree();
graph->AnalyzeNaturalLoops();
return graph;
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 5d712fe..4ebb136 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -16,11 +16,11 @@
#include "graph_checker.h"
-#include <string>
#include <map>
-#include <sstream>
+#include <string>
#include "base/bit_vector-inl.h"
+#include "base/stringprintf.h"
namespace art {
@@ -45,15 +45,11 @@
}
}
if (p_count_in_block_predecessors != block_count_in_p_successors) {
- std::stringstream error;
- error << "Block " << block->GetBlockId()
- << " lists " << p_count_in_block_predecessors
- << " occurrences of block " << p->GetBlockId()
- << " in its predecessors, whereas block " << p->GetBlockId()
- << " lists " << block_count_in_p_successors
- << " occurrences of block " << block->GetBlockId()
- << " in its successors.";
- errors_.push_back(error.str());
+ AddError(StringPrintf(
+ "Block %d lists %zu occurrences of block %d in its predecessors, whereas "
+ "block %d lists %zu occurrences of block %d in its successors.",
+ block->GetBlockId(), p_count_in_block_predecessors, p->GetBlockId(),
+ p->GetBlockId(), block_count_in_p_successors, block->GetBlockId()));
}
}
@@ -75,35 +71,27 @@
}
}
if (s_count_in_block_successors != block_count_in_s_predecessors) {
- std::stringstream error;
- error << "Block " << block->GetBlockId()
- << " lists " << s_count_in_block_successors
- << " occurrences of block " << s->GetBlockId()
- << " in its successors, whereas block " << s->GetBlockId()
- << " lists " << block_count_in_s_predecessors
- << " occurrences of block " << block->GetBlockId()
- << " in its predecessors.";
- errors_.push_back(error.str());
+ AddError(StringPrintf(
+ "Block %d lists %zu occurrences of block %d in its successors, whereas "
+ "block %d lists %zu occurrences of block %d in its predecessors.",
+ block->GetBlockId(), s_count_in_block_successors, s->GetBlockId(),
+ s->GetBlockId(), block_count_in_s_predecessors, block->GetBlockId()));
}
}
// Ensure `block` ends with a branch instruction.
HInstruction* last_inst = block->GetLastInstruction();
if (last_inst == nullptr || !last_inst->IsControlFlow()) {
- std::stringstream error;
- error << "Block " << block->GetBlockId()
- << " does not end with a branch instruction.";
- errors_.push_back(error.str());
+ AddError(StringPrintf("Block %d does not end with a branch instruction.",
+ block->GetBlockId()));
}
// Visit this block's list of phis.
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
// Ensure this block's list of phis contains only phis.
if (!it.Current()->IsPhi()) {
- std::stringstream error;
- error << "Block " << current_block_->GetBlockId()
- << " has a non-phi in its phi list.";
- errors_.push_back(error.str());
+ AddError(StringPrintf("Block %d has a non-phi in its phi list.",
+ current_block_->GetBlockId()));
}
it.Current()->Accept(this);
}
@@ -113,33 +101,33 @@
it.Advance()) {
// Ensure this block's list of instructions does not contains phis.
if (it.Current()->IsPhi()) {
- std::stringstream error;
- error << "Block " << current_block_->GetBlockId()
- << " has a phi in its non-phi list.";
- errors_.push_back(error.str());
+ AddError(StringPrintf("Block %d has a phi in its non-phi list.",
+ current_block_->GetBlockId()));
}
it.Current()->Accept(this);
}
}
void GraphChecker::VisitInstruction(HInstruction* instruction) {
+ if (seen_ids_.IsBitSet(instruction->GetId())) {
+ AddError(StringPrintf("Instruction id %d is duplicate in graph.",
+ instruction->GetId()));
+ } else {
+ seen_ids_.SetBit(instruction->GetId());
+ }
+
// Ensure `instruction` is associated with `current_block_`.
- if (instruction->GetBlock() != current_block_) {
- std::stringstream error;
- if (instruction->IsPhi()) {
- error << "Phi ";
- } else {
- error << "Instruction ";
- }
- error << instruction->GetId() << " in block "
- << current_block_->GetBlockId();
- if (instruction->GetBlock() != nullptr) {
- error << " associated with block "
- << instruction->GetBlock()->GetBlockId() << ".";
- } else {
- error << " not associated with any block.";
- }
- errors_.push_back(error.str());
+ if (instruction->GetBlock() == nullptr) {
+ AddError(StringPrintf("%s %d in block %d not associated with any block.",
+ instruction->IsPhi() ? "Phi" : "Instruction",
+ instruction->GetId(),
+ current_block_->GetBlockId()));
+ } else if (instruction->GetBlock() != current_block_) {
+ AddError(StringPrintf("%s %d in block %d associated with block %d.",
+ instruction->IsPhi() ? "Phi" : "Instruction",
+ instruction->GetId(),
+ current_block_->GetBlockId(),
+ instruction->GetBlock()->GetBlockId()));
}
// Ensure the inputs of `instruction` are defined in a block of the graph.
@@ -150,27 +138,26 @@
? input->GetBlock()->GetPhis()
: input->GetBlock()->GetInstructions();
if (!list.Contains(input)) {
- std::stringstream error;
- error << "Input " << input->GetId()
- << " of instruction " << instruction->GetId()
- << " is not defined in a basic block of the control-flow graph.";
- errors_.push_back(error.str());
+ AddError(StringPrintf("Input %d of instruction %d is not defined "
+ "in a basic block of the control-flow graph.",
+ input->GetId(),
+ instruction->GetId()));
}
}
// Ensure the uses of `instruction` are defined in a block of the graph.
- for (HUseIterator<HInstruction> use_it(instruction->GetUses());
+ for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
!use_it.Done(); use_it.Advance()) {
HInstruction* use = use_it.Current()->GetUser();
const HInstructionList& list = use->IsPhi()
? use->GetBlock()->GetPhis()
: use->GetBlock()->GetInstructions();
if (!list.Contains(use)) {
- std::stringstream error;
- error << "User " << use->GetId()
- << " of instruction " << instruction->GetId()
- << " is not defined in a basic block of the control-flow graph.";
- errors_.push_back(error.str());
+ AddError(StringPrintf("User %s:%d of instruction %d is not defined "
+ "in a basic block of the control-flow graph.",
+ use->DebugName(),
+ use->GetId(),
+ instruction->GetId()));
}
}
}
@@ -185,10 +172,9 @@
for (size_t j = 0; j < block->GetSuccessors().Size(); ++j) {
HBasicBlock* successor = block->GetSuccessors().Get(j);
if (successor->GetPredecessors().Size() > 1) {
- std::stringstream error;
- error << "Critical edge between blocks " << block->GetBlockId()
- << " and " << successor->GetBlockId() << ".";
- errors_.push_back(error.str());
+ AddError(StringPrintf("Critical edge between blocks %d and %d.",
+ block->GetBlockId(),
+ successor->GetBlockId()));
}
}
}
@@ -204,47 +190,52 @@
// Ensure the pre-header block is first in the list of
// predecessors of a loop header.
if (!loop_header->IsLoopPreHeaderFirstPredecessor()) {
- std::stringstream error;
- error << "Loop pre-header is not the first predecessor of the loop header "
- << id << ".";
- errors_.push_back(error.str());
+ AddError(StringPrintf(
+ "Loop pre-header is not the first predecessor of the loop header %d.",
+ id));
}
// Ensure the loop header has only two predecessors and that only the
// second one is a back edge.
- if (loop_header->GetPredecessors().Size() < 2) {
- std::stringstream error;
- error << "Loop header " << id << " has less than two predecessors.";
- errors_.push_back(error.str());
- } else if (loop_header->GetPredecessors().Size() > 2) {
- std::stringstream error;
- error << "Loop header " << id << " has more than two predecessors.";
- errors_.push_back(error.str());
+ size_t num_preds = loop_header->GetPredecessors().Size();
+ if (num_preds < 2) {
+ AddError(StringPrintf(
+ "Loop header %d has less than two predecessors: %zu.",
+ id,
+ num_preds));
+ } else if (num_preds > 2) {
+ AddError(StringPrintf(
+ "Loop header %d has more than two predecessors: %zu.",
+ id,
+ num_preds));
} else {
HLoopInformation* loop_information = loop_header->GetLoopInformation();
HBasicBlock* first_predecessor = loop_header->GetPredecessors().Get(0);
if (loop_information->IsBackEdge(first_predecessor)) {
- std::stringstream error;
- error << "First predecessor of loop header " << id << " is a back edge.";
- errors_.push_back(error.str());
+ AddError(StringPrintf(
+ "First predecessor of loop header %d is a back edge.",
+ id));
}
HBasicBlock* second_predecessor = loop_header->GetPredecessors().Get(1);
if (!loop_information->IsBackEdge(second_predecessor)) {
- std::stringstream error;
- error << "Second predecessor of loop header " << id
- << " is not a back edge.";
- errors_.push_back(error.str());
+ AddError(StringPrintf(
+ "Second predecessor of loop header %d is not a back edge.",
+ id));
}
}
// Ensure there is only one back edge per loop.
size_t num_back_edges =
loop_header->GetLoopInformation()->GetBackEdges().Size();
- if (num_back_edges != 1) {
- std::stringstream error;
- error << "Loop defined by header " << id << " has "
- << num_back_edges << " back edge(s).";
- errors_.push_back(error.str());
+ if (num_back_edges == 0) {
+ AddError(StringPrintf(
+ "Loop defined by header %d has no back edge.",
+ id));
+ } else if (num_back_edges > 1) {
+ AddError(StringPrintf(
+ "Loop defined by header %d has several back edges: %zu.",
+ id,
+ num_back_edges));
}
// Ensure all blocks in the loop are dominated by the loop header.
@@ -253,10 +244,9 @@
for (uint32_t i : loop_blocks.Indexes()) {
HBasicBlock* loop_block = GetGraph()->GetBlocks().Get(i);
if (!loop_header->Dominates(loop_block)) {
- std::stringstream error;
- error << "Loop block " << loop_block->GetBlockId()
- << " not dominated by loop header " << id;
- errors_.push_back(error.str());
+ AddError(StringPrintf("Loop block %d not dominated by loop header %d.",
+ loop_block->GetBlockId(),
+ id));
}
}
}
@@ -265,16 +255,14 @@
super_type::VisitInstruction(instruction);
// Ensure an instruction dominates all its uses.
- for (HUseIterator<HInstruction> use_it(instruction->GetUses());
+ for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
!use_it.Done(); use_it.Advance()) {
HInstruction* use = use_it.Current()->GetUser();
if (!use->IsPhi() && !instruction->StrictlyDominates(use)) {
- std::stringstream error;
- error << "Instruction " << instruction->GetId()
- << " in block " << current_block_->GetBlockId()
- << " does not dominate use " << use->GetId()
- << " in block " << use->GetBlock()->GetBlockId() << ".";
- errors_.push_back(error.str());
+ AddError(StringPrintf("Instruction %d in block %d does not dominate "
+ "use %d in block %d.",
+ instruction->GetId(), current_block_->GetBlockId(),
+ use->GetId(), use->GetBlock()->GetBlockId()));
}
}
@@ -286,13 +274,12 @@
HInstruction* env_instruction = environment->GetInstructionAt(i);
if (env_instruction != nullptr
&& !env_instruction->StrictlyDominates(instruction)) {
- std::stringstream error;
- error << "Instruction " << env_instruction->GetId()
- << " in environment of instruction " << instruction->GetId()
- << " from block " << current_block_->GetBlockId()
- << " does not dominate instruction " << instruction->GetId()
- << ".";
- errors_.push_back(error.str());
+ AddError(StringPrintf("Instruction %d in environment of instruction %d "
+ "from block %d does not dominate instruction %d.",
+ env_instruction->GetId(),
+ instruction->GetId(),
+ current_block_->GetBlockId(),
+ instruction->GetId()));
}
}
}
@@ -303,25 +290,21 @@
// Ensure the first input of a phi is not itself.
if (phi->InputAt(0) == phi) {
- std::stringstream error;
- error << "Loop phi " << phi->GetId()
- << " in block " << phi->GetBlock()->GetBlockId()
- << " is its own first input.";
- errors_.push_back(error.str());
+ AddError(StringPrintf("Loop phi %d in block %d is its own first input.",
+ phi->GetId(),
+ phi->GetBlock()->GetBlockId()));
}
- // Ensure the number of phi inputs is the same as the number of
+ // Ensure the number of inputs of a phi is the same as the number of
// its predecessors.
const GrowableArray<HBasicBlock*>& predecessors =
phi->GetBlock()->GetPredecessors();
if (phi->InputCount() != predecessors.Size()) {
- std::stringstream error;
- error << "Phi " << phi->GetId()
- << " in block " << phi->GetBlock()->GetBlockId()
- << " has " << phi->InputCount() << " inputs, but block "
- << phi->GetBlock()->GetBlockId() << " has "
- << predecessors.Size() << " predecessors.";
- errors_.push_back(error.str());
+ AddError(StringPrintf(
+ "Phi %d in block %d has %zu inputs, "
+ "but block %d has %zu predecessors.",
+ phi->GetId(), phi->GetBlock()->GetBlockId(), phi->InputCount(),
+ phi->GetBlock()->GetBlockId(), predecessors.Size()));
} else {
// Ensure phi input at index I either comes from the Ith
// predecessor or from a block that dominates this predecessor.
@@ -330,13 +313,11 @@
HBasicBlock* predecessor = predecessors.Get(i);
if (!(input->GetBlock() == predecessor
|| input->GetBlock()->Dominates(predecessor))) {
- std::stringstream error;
- error << "Input " << input->GetId() << " at index " << i
- << " of phi " << phi->GetId()
- << " from block " << phi->GetBlock()->GetBlockId()
- << " is not defined in predecessor number " << i
- << " nor in a block dominating it.";
- errors_.push_back(error.str());
+ AddError(StringPrintf(
+ "Input %d at index %zu of phi %d from block %d is not defined in "
+ "predecessor number %zu nor in a block dominating it.",
+ input->GetId(), i, phi->GetId(), phi->GetBlock()->GetBlockId(),
+ i));
}
}
}
@@ -355,15 +336,67 @@
}
}
+void SSAChecker::VisitIf(HIf* instruction) {
+ VisitInstruction(instruction);
+ HInstruction* input = instruction->InputAt(0);
+ if (input->IsIntConstant()) {
+ int value = input->AsIntConstant()->GetValue();
+ if (value != 0 && value != 1) {
+ AddError(StringPrintf(
+ "If instruction %d has a non-Boolean constant input "
+ "whose value is: %d.",
+ instruction->GetId(),
+ value));
+ }
+ } else if (instruction->InputAt(0)->GetType() != Primitive::kPrimBoolean) {
+ AddError(StringPrintf(
+ "If instruction %d has a non-Boolean input type: %s.",
+ instruction->GetId(),
+ Primitive::PrettyDescriptor(instruction->InputAt(0)->GetType())));
+ }
+}
+
void SSAChecker::VisitCondition(HCondition* op) {
VisitInstruction(op);
- // TODO: check inputs types, and special case the `null` check.
if (op->GetType() != Primitive::kPrimBoolean) {
- std::stringstream error;
- error << "Condition " << op->DebugName() << " " << op->GetId()
- << " has a non-boolean result type: "
- << op->GetType() << ".";
- errors_.push_back(error.str());
+ AddError(StringPrintf(
+ "Condition %s %d has a non-Boolean result type: %s.",
+ op->DebugName(), op->GetId(),
+ Primitive::PrettyDescriptor(op->GetType())));
+ }
+ HInstruction* lhs = op->InputAt(0);
+ HInstruction* rhs = op->InputAt(1);
+ if (lhs->GetType() == Primitive::kPrimNot) {
+ if (!op->IsEqual() && !op->IsNotEqual()) {
+ AddError(StringPrintf(
+ "Condition %s %d uses an object as left-hand side input.",
+ op->DebugName(), op->GetId()));
+ }
+ if (rhs->IsIntConstant() && rhs->AsIntConstant()->GetValue() != 0) {
+ AddError(StringPrintf(
+ "Condition %s %d compares an object with a non-zero integer: %d.",
+ op->DebugName(), op->GetId(),
+ rhs->AsIntConstant()->GetValue()));
+ }
+ } else if (rhs->GetType() == Primitive::kPrimNot) {
+ if (!op->IsEqual() && !op->IsNotEqual()) {
+ AddError(StringPrintf(
+ "Condition %s %d uses an object as right-hand side input.",
+ op->DebugName(), op->GetId()));
+ }
+ if (lhs->IsIntConstant() && lhs->AsIntConstant()->GetValue() != 0) {
+ AddError(StringPrintf(
+ "Condition %s %d compares a non-zero integer with an object: %d.",
+ op->DebugName(), op->GetId(),
+ lhs->AsIntConstant()->GetValue()));
+ }
+ } else if (PrimitiveKind(lhs->GetType()) != PrimitiveKind(rhs->GetType())) {
+ AddError(StringPrintf(
+ "Condition %s %d has inputs of different types: "
+ "%s, and %s.",
+ op->DebugName(), op->GetId(),
+ Primitive::PrettyDescriptor(lhs->GetType()),
+ Primitive::PrettyDescriptor(rhs->GetType())));
}
}
@@ -371,41 +404,40 @@
VisitInstruction(op);
if (op->IsUShr() || op->IsShr() || op->IsShl()) {
if (PrimitiveKind(op->InputAt(1)->GetType()) != Primitive::kPrimInt) {
- std::stringstream error;
- error << "Shift operation " << op->DebugName() << " " << op->GetId()
- << " has a non-int kind second input: "
- << op->InputAt(1)->DebugName() << " of type " << op->InputAt(1)->GetType()
- << ".";
- errors_.push_back(error.str());
+ AddError(StringPrintf(
+ "Shift operation %s %d has a non-int kind second input: "
+ "%s of type %s.",
+ op->DebugName(), op->GetId(),
+ op->InputAt(1)->DebugName(),
+ Primitive::PrettyDescriptor(op->InputAt(1)->GetType())));
}
} else {
if (PrimitiveKind(op->InputAt(1)->GetType()) != PrimitiveKind(op->InputAt(0)->GetType())) {
- std::stringstream error;
- error << "Binary operation " << op->DebugName() << " " << op->GetId()
- << " has inputs of different type: "
- << op->InputAt(0)->GetType() << ", and " << op->InputAt(1)->GetType()
- << ".";
- errors_.push_back(error.str());
+ AddError(StringPrintf(
+ "Binary operation %s %d has inputs of different types: "
+ "%s, and %s.",
+ op->DebugName(), op->GetId(),
+ Primitive::PrettyDescriptor(op->InputAt(0)->GetType()),
+ Primitive::PrettyDescriptor(op->InputAt(1)->GetType())));
}
}
if (op->IsCompare()) {
if (op->GetType() != Primitive::kPrimInt) {
- std::stringstream error;
- error << "Compare operation " << op->GetId()
- << " has a non-int result type: "
- << op->GetType() << ".";
- errors_.push_back(error.str());
+ AddError(StringPrintf(
+ "Compare operation %d has a non-int result type: %s.",
+ op->GetId(),
+ Primitive::PrettyDescriptor(op->GetType())));
}
} else {
// Use the first input, so that we can also make this check for shift operations.
if (PrimitiveKind(op->GetType()) != PrimitiveKind(op->InputAt(0)->GetType())) {
- std::stringstream error;
- error << "Binary operation " << op->DebugName() << " " << op->GetId()
- << " has a result type different than its input type: "
- << op->GetType() << ", and " << op->InputAt(1)->GetType()
- << ".";
- errors_.push_back(error.str());
+ AddError(StringPrintf(
+ "Binary operation %s %d has a result type different "
+ "from its input type: %s vs %s.",
+ op->DebugName(), op->GetId(),
+ Primitive::PrettyDescriptor(op->GetType()),
+ Primitive::PrettyDescriptor(op->InputAt(1)->GetType())));
}
}
}
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index b6c9f17..5ec3003 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -30,7 +30,8 @@
const char* dump_prefix = "art::GraphChecker: ")
: HGraphDelegateVisitor(graph),
allocator_(allocator),
- dump_prefix_(dump_prefix) {}
+ dump_prefix_(dump_prefix),
+ seen_ids_(allocator, graph->GetCurrentInstructionId(), false) {}
// Check the whole graph (in insertion order).
virtual void Run() { VisitInsertionOrder(); }
@@ -59,6 +60,11 @@
}
protected:
+ // Report a new error.
+ void AddError(const std::string& error) {
+ errors_.push_back(error);
+ }
+
ArenaAllocator* const allocator_;
// The block currently visited.
HBasicBlock* current_block_ = nullptr;
@@ -68,6 +74,7 @@
private:
// String displayed before dumped errors.
const char* const dump_prefix_;
+ ArenaBitVector seen_ids_;
DISALLOW_COPY_AND_ASSIGN(GraphChecker);
};
@@ -99,6 +106,7 @@
void VisitPhi(HPhi* phi) OVERRIDE;
void VisitBinaryOperation(HBinaryOperation* op) OVERRIDE;
void VisitCondition(HCondition* op) OVERRIDE;
+ void VisitIf(HIf* instruction) OVERRIDE;
private:
DISALLOW_COPY_AND_ASSIGN(SSAChecker);
diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc
index 39def82..923468f 100644
--- a/compiler/optimizing/graph_checker_test.cc
+++ b/compiler/optimizing/graph_checker_test.cc
@@ -62,7 +62,7 @@
ASSERT_NE(graph, nullptr);
graph->BuildDominatorTree();
- graph->TransformToSSA();
+ graph->TransformToSsa();
SSAChecker ssa_checker(&allocator, graph);
ssa_checker.Run();
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 4ed2156..835bca6 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -17,8 +17,8 @@
#include "graph_visualizer.h"
#include "code_generator.h"
-#include "driver/dex_compilation_unit.h"
#include "nodes.h"
+#include "optimization.h"
#include "ssa_liveness_analysis.h"
namespace art {
@@ -31,10 +31,12 @@
HGraphVisualizerPrinter(HGraph* graph,
std::ostream& output,
const char* pass_name,
+ bool is_after_pass,
const CodeGenerator& codegen)
: HGraphVisitor(graph),
output_(output),
pass_name_(pass_name),
+ is_after_pass_(is_after_pass),
codegen_(codegen),
indent_(0) {}
@@ -67,7 +69,7 @@
void PrintTime(const char* name) {
AddIndent();
- output_ << name << " " << time(NULL) << std::endl;
+ output_ << name << " " << time(nullptr) << std::endl;
}
void PrintInt(const char* name, int value) {
@@ -137,14 +139,21 @@
output_ << "invalid";
} else if (location.IsStackSlot()) {
output_ << location.GetStackIndex() << "(sp)";
+ } else if (location.IsFpuRegisterPair()) {
+ codegen_.DumpFloatingPointRegister(output_, location.low());
+ output_ << " and ";
+ codegen_.DumpFloatingPointRegister(output_, location.high());
+ } else if (location.IsRegisterPair()) {
+ codegen_.DumpCoreRegister(output_, location.low());
+ output_ << " and ";
+ codegen_.DumpCoreRegister(output_, location.high());
} else {
DCHECK(location.IsDoubleStackSlot());
output_ << "2x" << location.GetStackIndex() << "(sp)";
}
}
- void VisitParallelMove(HParallelMove* instruction) {
- output_ << instruction->DebugName();
+ void VisitParallelMove(HParallelMove* instruction) OVERRIDE {
output_ << " (";
for (size_t i = 0, e = instruction->NumMoves(); i < e; ++i) {
MoveOperands* move = instruction->MoveOperandsAt(i);
@@ -159,8 +168,25 @@
output_ << " (liveness: " << instruction->GetLifetimePosition() << ")";
}
- void VisitInstruction(HInstruction* instruction) {
+ void VisitIntConstant(HIntConstant* instruction) OVERRIDE {
+ output_ << " " << instruction->GetValue();
+ }
+
+ void VisitLongConstant(HLongConstant* instruction) OVERRIDE {
+ output_ << " " << instruction->GetValue();
+ }
+
+ void VisitFloatConstant(HFloatConstant* instruction) OVERRIDE {
+ output_ << " " << instruction->GetValue();
+ }
+
+ void VisitDoubleConstant(HDoubleConstant* instruction) OVERRIDE {
+ output_ << " " << instruction->GetValue();
+ }
+
+ void PrintInstruction(HInstruction* instruction) {
output_ << instruction->DebugName();
+ instruction->Accept(this);
if (instruction->InputCount() > 0) {
output_ << " [ ";
for (HInputIterator inputs(instruction); !inputs.Done(); inputs.Advance()) {
@@ -168,7 +194,22 @@
}
output_ << "]";
}
- if (pass_name_ == kLivenessPassName && instruction->GetLifetimePosition() != kNoLifetime) {
+ if (instruction->HasEnvironment()) {
+ HEnvironment* env = instruction->GetEnvironment();
+ output_ << " (env: [ ";
+ for (size_t i = 0, e = env->Size(); i < e; ++i) {
+ HInstruction* insn = env->GetInstructionAt(i);
+ if (insn != nullptr) {
+ output_ << GetTypeId(insn->GetType()) << insn->GetId() << " ";
+ } else {
+ output_ << " _ ";
+ }
+ }
+ output_ << "])";
+ }
+ if (pass_name_ == kLivenessPassName
+ && is_after_pass_
+ && instruction->GetLifetimePosition() != kNoLifetime) {
output_ << " (liveness: " << instruction->GetLifetimePosition();
if (instruction->HasLiveInterval()) {
output_ << " ";
@@ -176,7 +217,7 @@
interval.Dump(output_);
}
output_ << ")";
- } else if (pass_name_ == kRegisterAllocatorPassName) {
+ } else if (pass_name_ == kRegisterAllocatorPassName && is_after_pass_) {
LocationSummary* locations = instruction->GetLocations();
if (locations != nullptr) {
output_ << " ( ";
@@ -191,6 +232,14 @@
}
}
output_ << " (liveness: " << instruction->GetLifetimePosition() << ")";
+ } else if (pass_name_ == kLoopInvariantCodeMotionPassName) {
+ output_ << " ( loop_header:";
+ HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
+ if (info == nullptr) {
+ output_ << "null )";
+ } else {
+ output_ << "B" << info->GetHeader()->GetBlockId() << " )";
+ }
}
}
@@ -198,23 +247,30 @@
const char* kEndInstructionMarker = "<|@";
for (HInstructionIterator it(list); !it.Done(); it.Advance()) {
HInstruction* instruction = it.Current();
- AddIndent();
int bci = 0;
- output_ << bci << " " << instruction->NumberOfUses()
- << " " << GetTypeId(instruction->GetType()) << instruction->GetId() << " ";
- instruction->Accept(this);
+ size_t num_uses = 0;
+ for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
+ !use_it.Done();
+ use_it.Advance()) {
+ ++num_uses;
+ }
+ AddIndent();
+ output_ << bci << " " << num_uses << " "
+ << GetTypeId(instruction->GetType()) << instruction->GetId() << " ";
+ PrintInstruction(instruction);
output_ << kEndInstructionMarker << std::endl;
}
}
void Run() {
StartTag("cfg");
- PrintProperty("name", pass_name_);
+ std::string pass_desc = std::string(pass_name_) + (is_after_pass_ ? " (after)" : " (before)");
+ PrintProperty("name", pass_desc.c_str());
VisitInsertionOrder();
EndTag("cfg");
}
- void VisitBasicBlock(HBasicBlock* block) {
+ void VisitBasicBlock(HBasicBlock* block) OVERRIDE {
StartTag("block");
PrintProperty("name", "B", block->GetBlockId());
if (block->GetLifetimeStart() != kNoLifetime) {
@@ -260,6 +316,7 @@
private:
std::ostream& output_;
const char* pass_name_;
+ const bool is_after_pass_;
const CodeGenerator& codegen_;
size_t indent_;
@@ -268,51 +325,27 @@
HGraphVisualizer::HGraphVisualizer(std::ostream* output,
HGraph* graph,
- const char* string_filter,
const CodeGenerator& codegen,
- const DexCompilationUnit& cu)
- : output_(output), graph_(graph), codegen_(codegen), is_enabled_(false) {
- if (output == nullptr) {
- return;
- }
- std::string pretty_name = PrettyMethod(cu.GetDexMethodIndex(), *cu.GetDexFile());
- if (pretty_name.find(string_filter) == std::string::npos) {
- return;
- }
-
- is_enabled_ = true;
- HGraphVisualizerPrinter printer(graph, *output_, "", codegen_);
- printer.StartTag("compilation");
- printer.PrintProperty("name", pretty_name.c_str());
- printer.PrintProperty("method", pretty_name.c_str());
- printer.PrintTime("date");
- printer.EndTag("compilation");
-}
-
-HGraphVisualizer::HGraphVisualizer(std::ostream* output,
- HGraph* graph,
- const CodeGenerator& codegen,
- const char* name)
- : output_(output), graph_(graph), codegen_(codegen), is_enabled_(false) {
+ const char* method_name)
+ : output_(output), graph_(graph), codegen_(codegen) {
if (output == nullptr) {
return;
}
- is_enabled_ = true;
- HGraphVisualizerPrinter printer(graph, *output_, "", codegen_);
+ HGraphVisualizerPrinter printer(graph_, *output_, "", true, codegen_);
printer.StartTag("compilation");
- printer.PrintProperty("name", name);
- printer.PrintProperty("method", name);
+ printer.PrintProperty("name", method_name);
+ printer.PrintProperty("method", method_name);
printer.PrintTime("date");
printer.EndTag("compilation");
}
-void HGraphVisualizer::DumpGraph(const char* pass_name) const {
- if (!is_enabled_) {
- return;
+void HGraphVisualizer::DumpGraph(const char* pass_name, bool is_after_pass) const {
+ DCHECK(output_ != nullptr);
+ if (!graph_->GetBlocks().IsEmpty()) {
+ HGraphVisualizerPrinter printer(graph_, *output_, pass_name, is_after_pass, codegen_);
+ printer.Run();
}
- HGraphVisualizerPrinter printer(graph_, *output_, pass_name, codegen_);
- printer.Run();
}
} // namespace art
diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h
index 60d996b..bc553ae 100644
--- a/compiler/optimizing/graph_visualizer.h
+++ b/compiler/optimizing/graph_visualizer.h
@@ -27,52 +27,24 @@
class DexCompilationUnit;
class HGraph;
-// TODO: Create an analysis/optimization abstraction.
-static const char* kLivenessPassName = "liveness";
-static const char* kRegisterAllocatorPassName = "register";
-
/**
- * If enabled, emits compilation information suitable for the c1visualizer tool
- * and IRHydra.
- * Currently only works if the compiler is single threaded.
+ * This class outputs the HGraph in the C1visualizer format.
+ * Note: Currently only works if the compiler is single threaded.
*/
class HGraphVisualizer : public ValueObject {
public:
- /**
- * If output is not null, and the method name of the dex compilation
- * unit contains `string_filter`, the compilation information will be
- * emitted.
- */
- HGraphVisualizer(std::ostream* output,
- HGraph* graph,
- const char* string_filter,
- const CodeGenerator& codegen,
- const DexCompilationUnit& cu);
-
- /**
- * Version of `HGraphVisualizer` for unit testing, that is when a
- * `DexCompilationUnit` is not available.
- */
HGraphVisualizer(std::ostream* output,
HGraph* graph,
const CodeGenerator& codegen,
- const char* name);
+ const char* method_name);
- /**
- * If this visualizer is enabled, emit the compilation information
- * in `output_`.
- */
- void DumpGraph(const char* pass_name) const;
+ void DumpGraph(const char* pass_name, bool is_after_pass = true) const;
private:
std::ostream* const output_;
HGraph* const graph_;
const CodeGenerator& codegen_;
- // Is true when `output_` is not null, and the compiled method's name
- // contains the string_filter given in the constructor.
- bool is_enabled_;
-
DISALLOW_COPY_AND_ASSIGN(HGraphVisualizer);
};
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
index 6e5f1bd..89bba2d 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -15,82 +15,251 @@
*/
#include "gvn.h"
+#include "side_effects_analysis.h"
namespace art {
-void GlobalValueNumberer::Run() {
- ComputeSideEffects();
+/**
+ * A node in the collision list of a ValueSet. Encodes the instruction,
+ * the hash code, and the next node in the collision list.
+ */
+class ValueSetNode : public ArenaObject<kArenaAllocMisc> {
+ public:
+ ValueSetNode(HInstruction* instruction, size_t hash_code, ValueSetNode* next)
+ : instruction_(instruction), hash_code_(hash_code), next_(next) {}
+ size_t GetHashCode() const { return hash_code_; }
+ HInstruction* GetInstruction() const { return instruction_; }
+ ValueSetNode* GetNext() const { return next_; }
+ void SetNext(ValueSetNode* node) { next_ = node; }
+
+ private:
+ HInstruction* const instruction_;
+ const size_t hash_code_;
+ ValueSetNode* next_;
+
+ DISALLOW_COPY_AND_ASSIGN(ValueSetNode);
+};
+
+/**
+ * A ValueSet holds instructions that can replace other instructions. It is updated
+ * through the `Add` method, and the `Kill` method. The `Kill` method removes
+ * instructions that are affected by the given side effect.
+ *
+ * The `Lookup` method returns an equivalent instruction to the given instruction
+ * if there is one in the set. In GVN, we would say those instructions have the
+ * same "number".
+ */
+class ValueSet : public ArenaObject<kArenaAllocMisc> {
+ public:
+ explicit ValueSet(ArenaAllocator* allocator)
+ : allocator_(allocator), number_of_entries_(0), collisions_(nullptr) {
+ for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+ table_[i] = nullptr;
+ }
+ }
+
+ // Adds an instruction in the set.
+ void Add(HInstruction* instruction) {
+ DCHECK(Lookup(instruction) == nullptr);
+ size_t hash_code = instruction->ComputeHashCode();
+ size_t index = hash_code % kDefaultNumberOfEntries;
+ if (table_[index] == nullptr) {
+ table_[index] = instruction;
+ } else {
+ collisions_ = new (allocator_) ValueSetNode(instruction, hash_code, collisions_);
+ }
+ ++number_of_entries_;
+ }
+
+ // If in the set, returns an equivalent instruction to the given instruction. Returns
+ // null otherwise.
+ HInstruction* Lookup(HInstruction* instruction) const {
+ size_t hash_code = instruction->ComputeHashCode();
+ size_t index = hash_code % kDefaultNumberOfEntries;
+ HInstruction* existing = table_[index];
+ if (existing != nullptr && existing->Equals(instruction)) {
+ return existing;
+ }
+
+ for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
+ if (node->GetHashCode() == hash_code) {
+ existing = node->GetInstruction();
+ if (existing->Equals(instruction)) {
+ return existing;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ // Returns whether `instruction` is in the set.
+ HInstruction* IdentityLookup(HInstruction* instruction) const {
+ size_t hash_code = instruction->ComputeHashCode();
+ size_t index = hash_code % kDefaultNumberOfEntries;
+ HInstruction* existing = table_[index];
+ if (existing != nullptr && existing == instruction) {
+ return existing;
+ }
+
+ for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
+ if (node->GetHashCode() == hash_code) {
+ existing = node->GetInstruction();
+ if (existing == instruction) {
+ return existing;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ // Removes all instructions in the set that are affected by the given side effects.
+ void Kill(SideEffects side_effects) {
+ for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+ HInstruction* instruction = table_[i];
+ if (instruction != nullptr && instruction->GetSideEffects().DependsOn(side_effects)) {
+ table_[i] = nullptr;
+ --number_of_entries_;
+ }
+ }
+
+ for (ValueSetNode* current = collisions_, *previous = nullptr;
+ current != nullptr;
+ current = current->GetNext()) {
+ HInstruction* instruction = current->GetInstruction();
+ if (instruction->GetSideEffects().DependsOn(side_effects)) {
+ if (previous == nullptr) {
+ collisions_ = current->GetNext();
+ } else {
+ previous->SetNext(current->GetNext());
+ }
+ --number_of_entries_;
+ } else {
+ previous = current;
+ }
+ }
+ }
+
+ // Returns a copy of this set.
+ ValueSet* Copy() const {
+ ValueSet* copy = new (allocator_) ValueSet(allocator_);
+
+ for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+ copy->table_[i] = table_[i];
+ }
+
+ // Note that the order will be inverted in the copy. This is fine, as the order is not
+ // relevant for a ValueSet.
+ for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
+ copy->collisions_ = new (allocator_) ValueSetNode(
+ node->GetInstruction(), node->GetHashCode(), copy->collisions_);
+ }
+
+ copy->number_of_entries_ = number_of_entries_;
+ return copy;
+ }
+
+ void Clear() {
+ number_of_entries_ = 0;
+ collisions_ = nullptr;
+ for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+ table_[i] = nullptr;
+ }
+ }
+
+ // Update this `ValueSet` by intersecting with instructions in `other`.
+ void IntersectionWith(ValueSet* other) {
+ if (IsEmpty()) {
+ return;
+ } else if (other->IsEmpty()) {
+ Clear();
+ } else {
+ for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+ if (table_[i] != nullptr && other->IdentityLookup(table_[i]) == nullptr) {
+ --number_of_entries_;
+ table_[i] = nullptr;
+ }
+ }
+ for (ValueSetNode* current = collisions_, *previous = nullptr;
+ current != nullptr;
+ current = current->GetNext()) {
+ if (other->IdentityLookup(current->GetInstruction()) == nullptr) {
+ if (previous == nullptr) {
+ collisions_ = current->GetNext();
+ } else {
+ previous->SetNext(current->GetNext());
+ }
+ --number_of_entries_;
+ } else {
+ previous = current;
+ }
+ }
+ }
+ }
+
+ bool IsEmpty() const { return number_of_entries_ == 0; }
+ size_t GetNumberOfEntries() const { return number_of_entries_; }
+
+ private:
+ static constexpr size_t kDefaultNumberOfEntries = 8;
+
+ ArenaAllocator* const allocator_;
+
+ // The number of entries in the set.
+ size_t number_of_entries_;
+
+ // The internal implementation of the set. It uses a combination of a hash code based
+ // fixed-size list, and a linked list to handle hash code collisions.
+ // TODO: Tune the fixed size list original size, and support growing it.
+ ValueSetNode* collisions_;
+ HInstruction* table_[kDefaultNumberOfEntries];
+
+ DISALLOW_COPY_AND_ASSIGN(ValueSet);
+};
+
+/**
+ * Optimization phase that removes redundant instruction.
+ */
+class GlobalValueNumberer : public ValueObject {
+ public:
+ GlobalValueNumberer(ArenaAllocator* allocator,
+ HGraph* graph,
+ const SideEffectsAnalysis& side_effects)
+ : graph_(graph),
+ allocator_(allocator),
+ side_effects_(side_effects),
+ sets_(allocator, graph->GetBlocks().Size(), nullptr) {}
+
+ void Run();
+
+ private:
+ // Per-block GVN. Will also update the ValueSet of the dominated and
+ // successor blocks.
+ void VisitBasicBlock(HBasicBlock* block);
+
+ HGraph* graph_;
+ ArenaAllocator* const allocator_;
+ const SideEffectsAnalysis& side_effects_;
+
+ // ValueSet for blocks. Initially null, but for an individual block they
+ // are allocated and populated by the dominator, and updated by all blocks
+ // in the path from the dominator to the block.
+ GrowableArray<ValueSet*> sets_;
+
+ DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer);
+};
+
+void GlobalValueNumberer::Run() {
+ DCHECK(side_effects_.HasRun());
sets_.Put(graph_->GetEntryBlock()->GetBlockId(), new (allocator_) ValueSet(allocator_));
- // Do reverse post order to ensure the non back-edge predecessors of a block are
+ // Use the reverse post order to ensure the non back-edge predecessors of a block are
// visited before the block itself.
for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
VisitBasicBlock(it.Current());
}
}
-void GlobalValueNumberer::UpdateLoopEffects(HLoopInformation* info, SideEffects effects) {
- int id = info->GetHeader()->GetBlockId();
- loop_effects_.Put(id, loop_effects_.Get(id).Union(effects));
-}
-
-void GlobalValueNumberer::ComputeSideEffects() {
- if (kIsDebugBuild) {
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
- SideEffects effects = GetBlockEffects(block);
- DCHECK(!effects.HasSideEffects() && !effects.HasDependencies());
- if (block->IsLoopHeader()) {
- effects = GetLoopEffects(block);
- DCHECK(!effects.HasSideEffects() && !effects.HasDependencies());
- }
- }
- }
-
- // Do a post order visit to ensure we visit a loop header after its loop body.
- for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
-
- SideEffects effects = SideEffects::None();
- // Update `effects` with the side effects of all instructions in this block.
- for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
- inst_it.Advance()) {
- HInstruction* instruction = inst_it.Current();
- effects = effects.Union(instruction->GetSideEffects());
- if (effects.HasAllSideEffects()) {
- break;
- }
- }
-
- block_effects_.Put(block->GetBlockId(), effects);
-
- if (block->IsLoopHeader()) {
- // The side effects of the loop header are part of the loop.
- UpdateLoopEffects(block->GetLoopInformation(), effects);
- HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader();
- if (pre_header->IsInLoop()) {
- // Update the side effects of the outer loop with the side effects of the inner loop.
- // Note that this works because we know all the blocks of the inner loop are visited
- // before the loop header of the outer loop.
- UpdateLoopEffects(pre_header->GetLoopInformation(), GetLoopEffects(block));
- }
- } else if (block->IsInLoop()) {
- // Update the side effects of the loop with the side effects of this block.
- UpdateLoopEffects(block->GetLoopInformation(), effects);
- }
- }
-}
-
-SideEffects GlobalValueNumberer::GetLoopEffects(HBasicBlock* block) const {
- DCHECK(block->IsLoopHeader());
- return loop_effects_.Get(block->GetBlockId());
-}
-
-SideEffects GlobalValueNumberer::GetBlockEffects(HBasicBlock* block) const {
- return block_effects_.Get(block->GetBlockId());
-}
-
void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) {
ValueSet* set = nullptr;
const GrowableArray<HBasicBlock*>& predecessors = block->GetPredecessors();
@@ -110,7 +279,7 @@
if (!set->IsEmpty()) {
if (block->IsLoopHeader()) {
DCHECK_EQ(block->GetDominator(), block->GetLoopInformation()->GetPreHeader());
- set->Kill(GetLoopEffects(block));
+ set->Kill(side_effects_.GetLoopEffects(block));
} else if (predecessors.Size() > 1) {
for (size_t i = 0, e = predecessors.Size(); i < e; ++i) {
set->IntersectionWith(sets_.Get(predecessors.Get(i)->GetBlockId()));
@@ -142,4 +311,9 @@
}
}
+void GVNOptimization::Run() {
+ GlobalValueNumberer gvn(graph_->GetArena(), graph_, side_effects_);
+ gvn.Run();
+}
+
} // namespace art
diff --git a/compiler/optimizing/gvn.h b/compiler/optimizing/gvn.h
index 81f2c3f..57e0487 100644
--- a/compiler/optimizing/gvn.h
+++ b/compiler/optimizing/gvn.h
@@ -22,272 +22,18 @@
namespace art {
-/**
- * A node in the collision list of a ValueSet. Encodes the instruction,
- * the hash code, and the next node in the collision list.
- */
-class ValueSetNode : public ArenaObject<kArenaAllocMisc> {
- public:
- ValueSetNode(HInstruction* instruction, size_t hash_code, ValueSetNode* next)
- : instruction_(instruction), hash_code_(hash_code), next_(next) {}
-
- size_t GetHashCode() const { return hash_code_; }
- HInstruction* GetInstruction() const { return instruction_; }
- ValueSetNode* GetNext() const { return next_; }
- void SetNext(ValueSetNode* node) { next_ = node; }
-
- private:
- HInstruction* const instruction_;
- const size_t hash_code_;
- ValueSetNode* next_;
-
- DISALLOW_COPY_AND_ASSIGN(ValueSetNode);
-};
-
-/**
- * A ValueSet holds instructions that can replace other instructions. It is updated
- * through the `Add` method, and the `Kill` method. The `Kill` method removes
- * instructions that are affected by the given side effect.
- *
- * The `Lookup` method returns an equivalent instruction to the given instruction
- * if there is one in the set. In GVN, we would say those instructions have the
- * same "number".
- */
-class ValueSet : public ArenaObject<kArenaAllocMisc> {
- public:
- explicit ValueSet(ArenaAllocator* allocator)
- : allocator_(allocator), number_of_entries_(0), collisions_(nullptr) {
- for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
- table_[i] = nullptr;
- }
- }
-
- // Adds an instruction in the set.
- void Add(HInstruction* instruction) {
- DCHECK(Lookup(instruction) == nullptr);
- size_t hash_code = instruction->ComputeHashCode();
- size_t index = hash_code % kDefaultNumberOfEntries;
- if (table_[index] == nullptr) {
- table_[index] = instruction;
- } else {
- collisions_ = new (allocator_) ValueSetNode(instruction, hash_code, collisions_);
- }
- ++number_of_entries_;
- }
-
- // If in the set, returns an equivalent instruction to the given instruction. Returns
- // null otherwise.
- HInstruction* Lookup(HInstruction* instruction) const {
- size_t hash_code = instruction->ComputeHashCode();
- size_t index = hash_code % kDefaultNumberOfEntries;
- HInstruction* existing = table_[index];
- if (existing != nullptr && existing->Equals(instruction)) {
- return existing;
- }
-
- for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
- if (node->GetHashCode() == hash_code) {
- existing = node->GetInstruction();
- if (existing->Equals(instruction)) {
- return existing;
- }
- }
- }
- return nullptr;
- }
-
- // Returns whether `instruction` is in the set.
- HInstruction* IdentityLookup(HInstruction* instruction) const {
- size_t hash_code = instruction->ComputeHashCode();
- size_t index = hash_code % kDefaultNumberOfEntries;
- HInstruction* existing = table_[index];
- if (existing != nullptr && existing == instruction) {
- return existing;
- }
-
- for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
- if (node->GetHashCode() == hash_code) {
- existing = node->GetInstruction();
- if (existing == instruction) {
- return existing;
- }
- }
- }
- return nullptr;
- }
-
- // Removes all instructions in the set that are affected by the given side effects.
- void Kill(SideEffects side_effects) {
- for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
- HInstruction* instruction = table_[i];
- if (instruction != nullptr && instruction->GetSideEffects().DependsOn(side_effects)) {
- table_[i] = nullptr;
- --number_of_entries_;
- }
- }
-
- for (ValueSetNode* current = collisions_, *previous = nullptr;
- current != nullptr;
- current = current->GetNext()) {
- HInstruction* instruction = current->GetInstruction();
- if (instruction->GetSideEffects().DependsOn(side_effects)) {
- if (previous == nullptr) {
- collisions_ = current->GetNext();
- } else {
- previous->SetNext(current->GetNext());
- }
- --number_of_entries_;
- } else {
- previous = current;
- }
- }
- }
-
- // Returns a copy of this set.
- ValueSet* Copy() const {
- ValueSet* copy = new (allocator_) ValueSet(allocator_);
-
- for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
- copy->table_[i] = table_[i];
- }
-
- // Note that the order will be inverted in the copy. This is fine, as the order is not
- // relevant for a ValueSet.
- for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
- copy->collisions_ = new (allocator_) ValueSetNode(
- node->GetInstruction(), node->GetHashCode(), copy->collisions_);
- }
-
- copy->number_of_entries_ = number_of_entries_;
- return copy;
- }
-
- void Clear() {
- number_of_entries_ = 0;
- collisions_ = nullptr;
- for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
- table_[i] = nullptr;
- }
- }
-
- // Update this `ValueSet` by intersecting with instructions in `other`.
- void IntersectionWith(ValueSet* other) {
- if (IsEmpty()) {
- return;
- } else if (other->IsEmpty()) {
- Clear();
- } else {
- for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
- if (table_[i] != nullptr && other->IdentityLookup(table_[i]) == nullptr) {
- --number_of_entries_;
- table_[i] = nullptr;
- }
- }
- for (ValueSetNode* current = collisions_, *previous = nullptr;
- current != nullptr;
- current = current->GetNext()) {
- if (other->IdentityLookup(current->GetInstruction()) == nullptr) {
- if (previous == nullptr) {
- collisions_ = current->GetNext();
- } else {
- previous->SetNext(current->GetNext());
- }
- --number_of_entries_;
- } else {
- previous = current;
- }
- }
- }
- }
-
- bool IsEmpty() const { return number_of_entries_ == 0; }
- size_t GetNumberOfEntries() const { return number_of_entries_; }
-
- private:
- static constexpr size_t kDefaultNumberOfEntries = 8;
-
- ArenaAllocator* const allocator_;
-
- // The number of entries in the set.
- size_t number_of_entries_;
-
- // The internal implementation of the set. It uses a combination of a hash code based
- // fixed-size list, and a linked list to handle hash code collisions.
- // TODO: Tune the fixed size list original size, and support growing it.
- ValueSetNode* collisions_;
- HInstruction* table_[kDefaultNumberOfEntries];
-
- DISALLOW_COPY_AND_ASSIGN(ValueSet);
-};
-
-/**
- * Optimization phase that removes redundant instruction.
- */
-class GlobalValueNumberer : public ValueObject {
- public:
- GlobalValueNumberer(ArenaAllocator* allocator, HGraph* graph)
- : graph_(graph),
- allocator_(allocator),
- block_effects_(allocator, graph->GetBlocks().Size()),
- loop_effects_(allocator, graph->GetBlocks().Size()),
- sets_(allocator, graph->GetBlocks().Size()) {
- size_t number_of_blocks = graph->GetBlocks().Size();
- block_effects_.SetSize(number_of_blocks);
- loop_effects_.SetSize(number_of_blocks);
- sets_.SetSize(number_of_blocks);
-
- for (size_t i = 0; i < number_of_blocks; ++i) {
- block_effects_.Put(i, SideEffects::None());
- loop_effects_.Put(i, SideEffects::None());
- }
- }
-
- void Run();
-
- private:
- // Per-block GVN. Will also update the ValueSet of the dominated and
- // successor blocks.
- void VisitBasicBlock(HBasicBlock* block);
-
- // Compute side effects of individual blocks and loops. The GVN algorithm
- // will use these side effects to update the ValueSet of individual blocks.
- void ComputeSideEffects();
-
- void UpdateLoopEffects(HLoopInformation* info, SideEffects effects);
- SideEffects GetLoopEffects(HBasicBlock* block) const;
- SideEffects GetBlockEffects(HBasicBlock* block) const;
-
- HGraph* graph_;
-
- ArenaAllocator* const allocator_;
-
- // Side effects of individual blocks, that is the union of the side effects
- // of the instructions in the block.
- GrowableArray<SideEffects> block_effects_;
-
- // Side effects of loops, that is the union of the side effects of the
- // blocks contained in that loop.
- GrowableArray<SideEffects> loop_effects_;
-
- // ValueSet for blocks. Initially null, but for an individual block they
- // are allocated and populated by the dominator, and updated by all blocks
- // in the path from the dominator to the block.
- GrowableArray<ValueSet*> sets_;
-
- ART_FRIEND_TEST(GVNTest, LoopSideEffects);
- DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer);
-};
+class SideEffectsAnalysis;
class GVNOptimization : public HOptimization {
public:
- explicit GVNOptimization(HGraph* graph) : HOptimization(graph, true, "GVN") {}
+ GVNOptimization(HGraph* graph, const SideEffectsAnalysis& side_effects)
+ : HOptimization(graph, true, "GVN"), side_effects_(side_effects) {}
- void Run() OVERRIDE {
- GlobalValueNumberer gvn(graph_->GetArena(), graph_);
- gvn.Run();
- }
+ void Run() OVERRIDE;
private:
+ const SideEffectsAnalysis& side_effects_;
+
DISALLOW_COPY_AND_ASSIGN(GVNOptimization);
};
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index a6a68ca..4a48fee 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -18,6 +18,7 @@
#include "gvn.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
+#include "side_effects_analysis.h"
#include "utils/arena_allocator.h"
#include "gtest/gtest.h"
@@ -40,18 +41,22 @@
entry->AddSuccessor(block);
block->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(42)));
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
+ MemberOffset(42), false));
block->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(42)));
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
+ MemberOffset(42), false));
HInstruction* to_remove = block->GetLastInstruction();
block->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(43)));
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
+ MemberOffset(43), false));
HInstruction* different_offset = block->GetLastInstruction();
// Kill the value.
block->AddInstruction(new (&allocator) HInstanceFieldSet(
- parameter, parameter, Primitive::kPrimNot, MemberOffset(42)));
+ parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false));
block->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(42)));
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
+ MemberOffset(42), false));
HInstruction* use_after_kill = block->GetLastInstruction();
block->AddInstruction(new (&allocator) HExit());
@@ -59,9 +64,10 @@
ASSERT_EQ(different_offset->GetBlock(), block);
ASSERT_EQ(use_after_kill->GetBlock(), block);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
- GlobalValueNumberer(&allocator, graph).Run();
+ graph->TryBuildingSsa();
+ SideEffectsAnalysis side_effects(graph);
+ side_effects.Run();
+ GVNOptimization(graph, side_effects).Run();
ASSERT_TRUE(to_remove->GetBlock() == nullptr);
ASSERT_EQ(different_offset->GetBlock(), block);
@@ -83,7 +89,8 @@
graph->AddBlock(block);
entry->AddSuccessor(block);
block->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
+ MemberOffset(42), false));
block->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
HBasicBlock* then = new (&allocator) HBasicBlock(graph);
@@ -99,18 +106,22 @@
else_->AddSuccessor(join);
then->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
+ MemberOffset(42), false));
then->AddInstruction(new (&allocator) HGoto());
else_->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
+ MemberOffset(42), false));
else_->AddInstruction(new (&allocator) HGoto());
join->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
+ MemberOffset(42), false));
join->AddInstruction(new (&allocator) HExit());
- graph->BuildDominatorTree();
- graph->TransformToSSA();
- GlobalValueNumberer(&allocator, graph).Run();
+ graph->TryBuildingSsa();
+ SideEffectsAnalysis side_effects(graph);
+ side_effects.Run();
+ GVNOptimization(graph, side_effects).Run();
// Check that all field get instructions have been GVN'ed.
ASSERT_TRUE(then->GetFirstInstruction()->IsGoto());
@@ -134,7 +145,8 @@
graph->AddBlock(block);
entry->AddSuccessor(block);
block->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
+ MemberOffset(42), false));
block->AddInstruction(new (&allocator) HGoto());
HBasicBlock* loop_header = new (&allocator) HBasicBlock(graph);
@@ -150,22 +162,25 @@
loop_body->AddSuccessor(loop_header);
loop_header->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
+ MemberOffset(42), false));
HInstruction* field_get_in_loop_header = loop_header->GetLastInstruction();
loop_header->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
// Kill inside the loop body to prevent field gets inside the loop header
// and the body to be GVN'ed.
loop_body->AddInstruction(new (&allocator) HInstanceFieldSet(
- parameter, parameter, Primitive::kPrimNot, MemberOffset(42)));
+ parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false));
HInstruction* field_set = loop_body->GetLastInstruction();
loop_body->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
+ MemberOffset(42), false));
HInstruction* field_get_in_loop_body = loop_body->GetLastInstruction();
loop_body->AddInstruction(new (&allocator) HGoto());
exit->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
+ MemberOffset(42), false));
HInstruction* field_get_in_exit = exit->GetLastInstruction();
exit->AddInstruction(new (&allocator) HExit());
@@ -173,10 +188,12 @@
ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
ASSERT_EQ(field_get_in_exit->GetBlock(), exit);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
- graph->AnalyzeNaturalLoops();
- GlobalValueNumberer(&allocator, graph).Run();
+ graph->TryBuildingSsa();
+ {
+ SideEffectsAnalysis side_effects(graph);
+ side_effects.Run();
+ GVNOptimization(graph, side_effects).Run();
+ }
// Check that all field get instructions are still there.
ASSERT_EQ(field_get_in_loop_header->GetBlock(), loop_header);
@@ -187,7 +204,11 @@
// Now remove the field set, and check that all field get instructions have been GVN'ed.
loop_body->RemoveInstruction(field_set);
- GlobalValueNumberer(&allocator, graph).Run();
+ {
+ SideEffectsAnalysis side_effects(graph);
+ side_effects.Run();
+ GVNOptimization(graph, side_effects).Run();
+ }
ASSERT_TRUE(field_get_in_loop_header->GetBlock() == nullptr);
ASSERT_TRUE(field_get_in_loop_body->GetBlock() == nullptr);
@@ -237,9 +258,7 @@
inner_loop_exit->AddInstruction(new (&allocator) HGoto());
outer_loop_exit->AddInstruction(new (&allocator) HExit());
- graph->BuildDominatorTree();
- graph->TransformToSSA();
- graph->AnalyzeNaturalLoops();
+ graph->TryBuildingSsa();
ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn(
*outer_loop_header->GetLoopInformation()));
@@ -248,30 +267,30 @@
{
// Make one block with a side effect.
entry->AddInstruction(new (&allocator) HInstanceFieldSet(
- parameter, parameter, Primitive::kPrimNot, MemberOffset(42)));
+ parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false));
- GlobalValueNumberer gvn(&allocator, graph);
- gvn.Run();
+ SideEffectsAnalysis side_effects(graph);
+ side_effects.Run();
- ASSERT_TRUE(gvn.GetBlockEffects(entry).HasSideEffects());
- ASSERT_FALSE(gvn.GetLoopEffects(outer_loop_header).HasSideEffects());
- ASSERT_FALSE(gvn.GetLoopEffects(inner_loop_header).HasSideEffects());
+ ASSERT_TRUE(side_effects.GetBlockEffects(entry).HasSideEffects());
+ ASSERT_FALSE(side_effects.GetLoopEffects(outer_loop_header).HasSideEffects());
+ ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).HasSideEffects());
}
// Check that the side effects of the outer loop does not affect the inner loop.
{
outer_loop_body->InsertInstructionBefore(
new (&allocator) HInstanceFieldSet(
- parameter, parameter, Primitive::kPrimNot, MemberOffset(42)),
+ parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false),
outer_loop_body->GetLastInstruction());
- GlobalValueNumberer gvn(&allocator, graph);
- gvn.Run();
+ SideEffectsAnalysis side_effects(graph);
+ side_effects.Run();
- ASSERT_TRUE(gvn.GetBlockEffects(entry).HasSideEffects());
- ASSERT_TRUE(gvn.GetBlockEffects(outer_loop_body).HasSideEffects());
- ASSERT_TRUE(gvn.GetLoopEffects(outer_loop_header).HasSideEffects());
- ASSERT_FALSE(gvn.GetLoopEffects(inner_loop_header).HasSideEffects());
+ ASSERT_TRUE(side_effects.GetBlockEffects(entry).HasSideEffects());
+ ASSERT_TRUE(side_effects.GetBlockEffects(outer_loop_body).HasSideEffects());
+ ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).HasSideEffects());
+ ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).HasSideEffects());
}
// Check that the side effects of the inner loop affects the outer loop.
@@ -279,16 +298,16 @@
outer_loop_body->RemoveInstruction(outer_loop_body->GetFirstInstruction());
inner_loop_body->InsertInstructionBefore(
new (&allocator) HInstanceFieldSet(
- parameter, parameter, Primitive::kPrimNot, MemberOffset(42)),
+ parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false),
inner_loop_body->GetLastInstruction());
- GlobalValueNumberer gvn(&allocator, graph);
- gvn.Run();
+ SideEffectsAnalysis side_effects(graph);
+ side_effects.Run();
- ASSERT_TRUE(gvn.GetBlockEffects(entry).HasSideEffects());
- ASSERT_FALSE(gvn.GetBlockEffects(outer_loop_body).HasSideEffects());
- ASSERT_TRUE(gvn.GetLoopEffects(outer_loop_header).HasSideEffects());
- ASSERT_TRUE(gvn.GetLoopEffects(inner_loop_header).HasSideEffects());
+ ASSERT_TRUE(side_effects.GetBlockEffects(entry).HasSideEffects());
+ ASSERT_FALSE(side_effects.GetBlockEffects(outer_loop_body).HasSideEffects());
+ ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).HasSideEffects());
+ ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).HasSideEffects());
}
}
} // namespace art
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
new file mode 100644
index 0000000..32f6972
--- /dev/null
+++ b/compiler/optimizing/inliner.cc
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "inliner.h"
+
+#include "builder.h"
+#include "class_linker.h"
+#include "constant_folding.h"
+#include "dead_code_elimination.h"
+#include "driver/compiler_driver-inl.h"
+#include "driver/dex_compilation_unit.h"
+#include "instruction_simplifier.h"
+#include "mirror/art_method-inl.h"
+#include "mirror/class_loader.h"
+#include "mirror/dex_cache.h"
+#include "nodes.h"
+#include "register_allocator.h"
+#include "ssa_phi_elimination.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
+
+namespace art {
+
+static constexpr int kMaxInlineCodeUnits = 100;
+static constexpr int kDepthLimit = 5;
+
+void HInliner::Run() {
+ const GrowableArray<HBasicBlock*>& blocks = graph_->GetReversePostOrder();
+ for (size_t i = 0; i < blocks.Size(); ++i) {
+ HBasicBlock* block = blocks.Get(i);
+ for (HInstruction* instruction = block->GetFirstInstruction(); instruction != nullptr;) {
+ HInstruction* next = instruction->GetNext();
+ HInvokeStaticOrDirect* call = instruction->AsInvokeStaticOrDirect();
+ if (call != nullptr) {
+ if (!TryInline(call, call->GetDexMethodIndex(), call->GetInvokeType())) {
+ if (kIsDebugBuild) {
+ std::string callee_name =
+ PrettyMethod(call->GetDexMethodIndex(), *outer_compilation_unit_.GetDexFile());
+ bool should_inline = callee_name.find("$inline$") != std::string::npos;
+ CHECK(!should_inline) << "Could not inline " << callee_name;
+ }
+ }
+ }
+ instruction = next;
+ }
+ }
+}
+
+bool HInliner::TryInline(HInvoke* invoke_instruction,
+ uint32_t method_index,
+ InvokeType invoke_type) const {
+ ScopedObjectAccess soa(Thread::Current());
+ const DexFile& outer_dex_file = *outer_compilation_unit_.GetDexFile();
+ VLOG(compiler) << "Try inlining " << PrettyMethod(method_index, outer_dex_file);
+
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache(
+ hs.NewHandle(outer_compilation_unit_.GetClassLinker()->FindDexCache(outer_dex_file)));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(outer_compilation_unit_.GetClassLoader())));
+ Handle<mirror::ArtMethod> resolved_method(hs.NewHandle(
+ compiler_driver_->ResolveMethod(
+ soa, dex_cache, class_loader, &outer_compilation_unit_, method_index, invoke_type)));
+
+ if (resolved_method.Get() == nullptr) {
+ VLOG(compiler) << "Method cannot be resolved " << PrettyMethod(method_index, outer_dex_file);
+ return false;
+ }
+
+ if (resolved_method->GetDexFile()->GetLocation().compare(outer_dex_file.GetLocation()) != 0) {
+ VLOG(compiler) << "Did not inline "
+ << PrettyMethod(method_index, outer_dex_file)
+ << " because it is in a different dex file";
+ return false;
+ }
+
+ const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
+
+ if (code_item == nullptr) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " is not inlined because it is native";
+ return false;
+ }
+
+ if (code_item->insns_size_in_code_units_ > kMaxInlineCodeUnits) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " is too big to inline";
+ return false;
+ }
+
+ if (code_item->tries_size_ != 0) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " is not inlined because of try block";
+ return false;
+ }
+
+ if (!resolved_method->GetDeclaringClass()->IsVerified()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " is not inlined because its class could not be verified";
+ return false;
+ }
+
+ DexCompilationUnit dex_compilation_unit(
+ nullptr,
+ outer_compilation_unit_.GetClassLoader(),
+ outer_compilation_unit_.GetClassLinker(),
+ outer_dex_file,
+ code_item,
+ resolved_method->GetDeclaringClass()->GetDexClassDefIndex(),
+ method_index,
+ resolved_method->GetAccessFlags(),
+ nullptr);
+
+ HGraph* callee_graph =
+ new (graph_->GetArena()) HGraph(graph_->GetArena(), graph_->GetCurrentInstructionId());
+
+ OptimizingCompilerStats inline_stats;
+ HGraphBuilder builder(callee_graph,
+ &dex_compilation_unit,
+ &outer_compilation_unit_,
+ &outer_dex_file,
+ compiler_driver_,
+ &inline_stats);
+
+ if (!builder.BuildGraph(*code_item)) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " could not be built, so cannot be inlined";
+ return false;
+ }
+
+ if (!RegisterAllocator::CanAllocateRegistersFor(*callee_graph,
+ compiler_driver_->GetInstructionSet())) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " cannot be inlined because of the register allocator";
+ return false;
+ }
+
+ if (!callee_graph->TryBuildingSsa()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " could not be transformed to SSA";
+ return false;
+ }
+
+ // Run simple optimizations on the graph.
+ SsaRedundantPhiElimination redundant_phi(callee_graph);
+ SsaDeadPhiElimination dead_phi(callee_graph);
+ HDeadCodeElimination dce(callee_graph);
+ HConstantFolding fold(callee_graph);
+ InstructionSimplifier simplify(callee_graph);
+
+ HOptimization* optimizations[] = {
+ &redundant_phi,
+ &dead_phi,
+ &dce,
+ &fold,
+ &simplify,
+ };
+
+ for (size_t i = 0; i < arraysize(optimizations); ++i) {
+ HOptimization* optimization = optimizations[i];
+ optimization->Run();
+ }
+
+ if (depth_ + 1 < kDepthLimit) {
+ HInliner inliner(
+ callee_graph, outer_compilation_unit_, compiler_driver_, outer_stats_, depth_ + 1);
+ inliner.Run();
+ }
+
+ HReversePostOrderIterator it(*callee_graph);
+ it.Advance(); // Past the entry block, it does not contain instructions that prevent inlining.
+ for (; !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ if (block->IsLoopHeader()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " could not be inlined because it contains a loop";
+ return false;
+ }
+
+ for (HInstructionIterator instr_it(block->GetInstructions());
+ !instr_it.Done();
+ instr_it.Advance()) {
+ HInstruction* current = instr_it.Current();
+ if (current->IsSuspendCheck()) {
+ continue;
+ }
+
+ if (current->CanThrow()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " could not be inlined because " << current->DebugName()
+ << " can throw";
+ return false;
+ }
+
+ if (current->NeedsEnvironment()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " could not be inlined because " << current->DebugName()
+ << " needs an environment";
+ return false;
+ }
+ }
+ }
+
+ callee_graph->InlineInto(graph_, invoke_instruction);
+
+ // Now that we have inlined the callee, we need to update the next
+ // instruction id of the caller, so that new instructions added
+ // after optimizations get a unique id.
+ graph_->SetCurrentInstructionId(callee_graph->GetNextInstructionId());
+ VLOG(compiler) << "Successfully inlined " << PrettyMethod(method_index, outer_dex_file);
+ outer_stats_->RecordStat(kInlinedInvoke);
+ return true;
+}
+
+} // namespace art
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
new file mode 100644
index 0000000..07d893e
--- /dev/null
+++ b/compiler/optimizing/inliner.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INLINER_H_
+#define ART_COMPILER_OPTIMIZING_INLINER_H_
+
+#include "invoke_type.h"
+#include "optimization.h"
+
+namespace art {
+
+class CompilerDriver;
+class DexCompilationUnit;
+class HGraph;
+class HInvoke;
+class OptimizingCompilerStats;
+
+class HInliner : public HOptimization {
+ public:
+ HInliner(HGraph* outer_graph,
+ const DexCompilationUnit& outer_compilation_unit,
+ CompilerDriver* compiler_driver,
+ OptimizingCompilerStats* stats,
+ size_t depth = 0)
+ : HOptimization(outer_graph, true, "inliner"),
+ outer_compilation_unit_(outer_compilation_unit),
+ compiler_driver_(compiler_driver),
+ outer_stats_(stats),
+ depth_(depth) {}
+
+ void Run() OVERRIDE;
+
+ private:
+ bool TryInline(HInvoke* invoke_instruction, uint32_t method_index, InvokeType invoke_type) const;
+
+ const DexCompilationUnit& outer_compilation_unit_;
+ CompilerDriver* const compiler_driver_;
+ OptimizingCompilerStats* const outer_stats_;
+ const size_t depth_;
+
+ DISALLOW_COPY_AND_ASSIGN(HInliner);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_INLINER_H_
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 49ca443..44dbb9d 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -27,6 +27,8 @@
void VisitEqual(HEqual* equal) OVERRIDE;
void VisitArraySet(HArraySet* equal) OVERRIDE;
void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE;
+ void VisitNullCheck(HNullCheck* instruction) OVERRIDE;
+ void VisitArrayLength(HArrayLength* instruction) OVERRIDE;
};
void InstructionSimplifier::Run() {
@@ -34,6 +36,14 @@
visitor.VisitInsertionOrder();
}
+void InstructionSimplifierVisitor::VisitNullCheck(HNullCheck* null_check) {
+ HInstruction* obj = null_check->InputAt(0);
+ if (!obj->CanBeNull()) {
+ null_check->ReplaceWith(obj);
+ null_check->GetBlock()->RemoveInstruction(null_check);
+ }
+}
+
void InstructionSimplifierVisitor::VisitSuspendCheck(HSuspendCheck* check) {
HBasicBlock* block = check->GetBlock();
// Currently always keep the suspend check at entry.
@@ -59,10 +69,21 @@
equal->ReplaceWith(equal->InputAt(0));
equal->GetBlock()->RemoveInstruction(equal);
} else {
- // Replace (bool_value == 0) with !bool_value
+ // We should replace (bool_value == 0) with !bool_value, but we unfortunately
+ // do not have such instruction.
DCHECK_EQ(input2->AsIntConstant()->GetValue(), 0);
- equal->GetBlock()->ReplaceAndRemoveInstructionWith(
- equal, new (GetGraph()->GetArena()) HNot(Primitive::kPrimBoolean, input1));
+ }
+ }
+}
+
+void InstructionSimplifierVisitor::VisitArrayLength(HArrayLength* instruction) {
+ HInstruction* input = instruction->InputAt(0);
+ // If the array is a NewArray with constant size, replace the array length
+ // with the constant instruction. This helps the bounds check elimination phase.
+ if (input->IsNewArray()) {
+ input = input->InputAt(0);
+ if (input->IsIntConstant()) {
+ instruction->ReplaceWith(input);
}
}
}
diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h
index 7068c7f..bca6697 100644
--- a/compiler/optimizing/instruction_simplifier.h
+++ b/compiler/optimizing/instruction_simplifier.h
@@ -27,8 +27,8 @@
*/
class InstructionSimplifier : public HOptimization {
public:
- explicit InstructionSimplifier(HGraph* graph)
- : HOptimization(graph, true, "instruction_simplifier") {}
+ explicit InstructionSimplifier(HGraph* graph, const char* name = "instruction_simplifier")
+ : HOptimization(graph, true, name) {}
void Run() OVERRIDE;
};
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
new file mode 100644
index 0000000..36cf856
--- /dev/null
+++ b/compiler/optimizing/intrinsics.cc
@@ -0,0 +1,368 @@
+/*
+ * 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.
+ */
+
+#include "intrinsics.h"
+
+#include "dex/quick/dex_file_method_inliner.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "driver/compiler_driver.h"
+#include "invoke_type.h"
+#include "nodes.h"
+#include "quick/inline_method_analyser.h"
+
+namespace art {
+
+// Function that returns whether an intrinsic is static/direct or virtual.
+static inline InvokeType GetIntrinsicInvokeType(Intrinsics i) {
+ switch (i) {
+ case Intrinsics::kNone:
+ return kInterface; // Non-sensical for intrinsic.
+#define OPTIMIZING_INTRINSICS(Name, IsStatic) \
+ case Intrinsics::k ## Name: \
+ return IsStatic;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+ }
+ return kInterface;
+}
+
+
+
+static Primitive::Type GetType(uint64_t data, bool is_op_size) {
+ if (is_op_size) {
+ switch (static_cast<OpSize>(data)) {
+ case kSignedByte:
+ return Primitive::kPrimByte;
+ case kSignedHalf:
+ return Primitive::kPrimShort;
+ case k32:
+ return Primitive::kPrimInt;
+ case k64:
+ return Primitive::kPrimLong;
+ default:
+ LOG(FATAL) << "Unknown/unsupported op size " << data;
+ UNREACHABLE();
+ }
+ } else {
+ if ((data & kIntrinsicFlagIsLong) != 0) {
+ return Primitive::kPrimLong;
+ }
+ if ((data & kIntrinsicFlagIsObject) != 0) {
+ return Primitive::kPrimNot;
+ }
+ return Primitive::kPrimInt;
+ }
+}
+
+static Intrinsics GetIntrinsic(InlineMethod method) {
+ switch (method.opcode) {
+ // Floating-point conversions.
+ case kIntrinsicDoubleCvt:
+ return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ?
+ Intrinsics::kDoubleDoubleToRawLongBits : Intrinsics::kDoubleLongBitsToDouble;
+ case kIntrinsicFloatCvt:
+ return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ?
+ Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat;
+
+ // Bit manipulations.
+ case kIntrinsicReverseBits:
+ switch (GetType(method.d.data, true)) {
+ case Primitive::kPrimInt:
+ return Intrinsics::kIntegerReverse;
+ case Primitive::kPrimLong:
+ return Intrinsics::kLongReverse;
+ default:
+ LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+ UNREACHABLE();
+ }
+ break;
+ case kIntrinsicReverseBytes:
+ switch (GetType(method.d.data, true)) {
+ case Primitive::kPrimShort:
+ return Intrinsics::kShortReverseBytes;
+ case Primitive::kPrimInt:
+ return Intrinsics::kIntegerReverseBytes;
+ case Primitive::kPrimLong:
+ return Intrinsics::kLongReverseBytes;
+ default:
+ LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+ UNREACHABLE();
+ }
+ break;
+
+ // Abs.
+ case kIntrinsicAbsDouble:
+ return Intrinsics::kMathAbsDouble;
+ case kIntrinsicAbsFloat:
+ return Intrinsics::kMathAbsFloat;
+ case kIntrinsicAbsInt:
+ return Intrinsics::kMathAbsInt;
+ case kIntrinsicAbsLong:
+ return Intrinsics::kMathAbsLong;
+
+ // Min/max.
+ case kIntrinsicMinMaxDouble:
+ return ((method.d.data & kIntrinsicFlagMin) == 0) ?
+ Intrinsics::kMathMaxDoubleDouble : Intrinsics::kMathMinDoubleDouble;
+ case kIntrinsicMinMaxFloat:
+ return ((method.d.data & kIntrinsicFlagMin) == 0) ?
+ Intrinsics::kMathMaxFloatFloat : Intrinsics::kMathMinFloatFloat;
+ case kIntrinsicMinMaxInt:
+ return ((method.d.data & kIntrinsicFlagMin) == 0) ?
+ Intrinsics::kMathMaxIntInt : Intrinsics::kMathMinIntInt;
+ case kIntrinsicMinMaxLong:
+ return ((method.d.data & kIntrinsicFlagMin) == 0) ?
+ Intrinsics::kMathMaxLongLong : Intrinsics::kMathMinLongLong;
+
+ // Misc math.
+ case kIntrinsicSqrt:
+ return Intrinsics::kMathSqrt;
+ case kIntrinsicCeil:
+ return Intrinsics::kMathCeil;
+ case kIntrinsicFloor:
+ return Intrinsics::kMathFloor;
+ case kIntrinsicRint:
+ return Intrinsics::kMathRint;
+ case kIntrinsicRoundDouble:
+ return Intrinsics::kMathRoundDouble;
+ case kIntrinsicRoundFloat:
+ return Intrinsics::kMathRoundFloat;
+
+ // System.arraycopy.
+ case kIntrinsicSystemArrayCopyCharArray:
+ return Intrinsics::kSystemArrayCopyChar;
+
+ // Thread.currentThread.
+ case kIntrinsicCurrentThread:
+ return Intrinsics::kThreadCurrentThread;
+
+ // Memory.peek.
+ case kIntrinsicPeek:
+ switch (GetType(method.d.data, true)) {
+ case Primitive::kPrimByte:
+ return Intrinsics::kMemoryPeekByte;
+ case Primitive::kPrimShort:
+ return Intrinsics::kMemoryPeekShortNative;
+ case Primitive::kPrimInt:
+ return Intrinsics::kMemoryPeekIntNative;
+ case Primitive::kPrimLong:
+ return Intrinsics::kMemoryPeekLongNative;
+ default:
+ LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+ UNREACHABLE();
+ }
+ break;
+
+ // Memory.poke.
+ case kIntrinsicPoke:
+ switch (GetType(method.d.data, true)) {
+ case Primitive::kPrimByte:
+ return Intrinsics::kMemoryPokeByte;
+ case Primitive::kPrimShort:
+ return Intrinsics::kMemoryPokeShortNative;
+ case Primitive::kPrimInt:
+ return Intrinsics::kMemoryPokeIntNative;
+ case Primitive::kPrimLong:
+ return Intrinsics::kMemoryPokeLongNative;
+ default:
+ LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+ UNREACHABLE();
+ }
+ break;
+
+ // String.
+ case kIntrinsicCharAt:
+ return Intrinsics::kStringCharAt;
+ case kIntrinsicCompareTo:
+ return Intrinsics::kStringCompareTo;
+ case kIntrinsicIsEmptyOrLength:
+ return ((method.d.data & kIntrinsicFlagIsEmpty) == 0) ?
+ Intrinsics::kStringLength : Intrinsics::kStringIsEmpty;
+ case kIntrinsicIndexOf:
+ return ((method.d.data & kIntrinsicFlagBase0) == 0) ?
+ Intrinsics::kStringIndexOfAfter : Intrinsics::kStringIndexOf;
+
+ case kIntrinsicCas:
+ switch (GetType(method.d.data, false)) {
+ case Primitive::kPrimNot:
+ return Intrinsics::kUnsafeCASObject;
+ case Primitive::kPrimInt:
+ return Intrinsics::kUnsafeCASInt;
+ case Primitive::kPrimLong:
+ return Intrinsics::kUnsafeCASLong;
+ default:
+ LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+ UNREACHABLE();
+ }
+ break;
+ case kIntrinsicUnsafeGet: {
+ const bool is_volatile = (method.d.data & kIntrinsicFlagIsVolatile);
+ switch (GetType(method.d.data, false)) {
+ case Primitive::kPrimInt:
+ return is_volatile ? Intrinsics::kUnsafeGetVolatile : Intrinsics::kUnsafeGet;
+ case Primitive::kPrimLong:
+ return is_volatile ? Intrinsics::kUnsafeGetLongVolatile : Intrinsics::kUnsafeGetLong;
+ case Primitive::kPrimNot:
+ return is_volatile ? Intrinsics::kUnsafeGetObjectVolatile : Intrinsics::kUnsafeGetObject;
+ default:
+ LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+ UNREACHABLE();
+ }
+ break;
+ }
+ case kIntrinsicUnsafePut: {
+ enum Sync { kNoSync, kVolatile, kOrdered };
+ const Sync sync =
+ ((method.d.data & kIntrinsicFlagIsVolatile) != 0) ? kVolatile :
+ ((method.d.data & kIntrinsicFlagIsOrdered) != 0) ? kOrdered :
+ kNoSync;
+ switch (GetType(method.d.data, false)) {
+ case Primitive::kPrimInt:
+ switch (sync) {
+ case kNoSync:
+ return Intrinsics::kUnsafePut;
+ case kVolatile:
+ return Intrinsics::kUnsafePutVolatile;
+ case kOrdered:
+ return Intrinsics::kUnsafePutOrdered;
+ }
+ break;
+ case Primitive::kPrimLong:
+ switch (sync) {
+ case kNoSync:
+ return Intrinsics::kUnsafePutLong;
+ case kVolatile:
+ return Intrinsics::kUnsafePutLongVolatile;
+ case kOrdered:
+ return Intrinsics::kUnsafePutLongOrdered;
+ }
+ break;
+ case Primitive::kPrimNot:
+ switch (sync) {
+ case kNoSync:
+ return Intrinsics::kUnsafePutObject;
+ case kVolatile:
+ return Intrinsics::kUnsafePutObjectVolatile;
+ case kOrdered:
+ return Intrinsics::kUnsafePutObjectOrdered;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+ UNREACHABLE();
+ }
+ break;
+ }
+
+ // Virtual cases.
+
+ case kIntrinsicReferenceGetReferent:
+ return Intrinsics::kReferenceGetReferent;
+
+ // Quick inliner cases. Remove after refactoring. They are here so that we can use the
+ // compiler to warn on missing cases.
+
+ case kInlineOpNop:
+ case kInlineOpReturnArg:
+ case kInlineOpNonWideConst:
+ case kInlineOpIGet:
+ case kInlineOpIPut:
+ return Intrinsics::kNone;
+
+ // No default case to make the compiler warn on missing cases.
+ }
+ return Intrinsics::kNone;
+}
+
+static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) {
+ // The DexFileMethodInliner should have checked whether the methods are agreeing with
+ // what we expect, i.e., static methods are called as such. Add another check here for
+ // our expectations:
+ // Whenever the intrinsic is marked as static-or-direct, report an error if we find an
+ // InvokeVirtual. The other direction is not possible: we have intrinsics for virtual
+ // functions that will perform a check inline. If the precise type is known, however,
+ // the instruction will be sharpened to an InvokeStaticOrDirect.
+ InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic);
+ InvokeType invoke_type = invoke->IsInvokeStaticOrDirect() ?
+ invoke->AsInvokeStaticOrDirect()->GetInvokeType() :
+ invoke->IsInvokeVirtual() ? kVirtual : kSuper;
+ switch (intrinsic_type) {
+ case kStatic:
+ return (invoke_type == kStatic);
+ case kDirect:
+ return (invoke_type == kDirect);
+ case kVirtual:
+ // Call might be devirtualized.
+ return (invoke_type == kVirtual || invoke_type == kDirect);
+
+ default:
+ return false;
+ }
+}
+
+// TODO: Refactor DexFileMethodInliner and have something nicer than InlineMethod.
+void IntrinsicsRecognizer::Run() {
+ DexFileMethodInliner* inliner = driver_->GetMethodInlinerMap()->GetMethodInliner(dex_file_);
+ DCHECK(inliner != nullptr);
+
+ for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
+ inst_it.Advance()) {
+ HInstruction* inst = inst_it.Current();
+ if (inst->IsInvoke()) {
+ HInvoke* invoke = inst->AsInvoke();
+ InlineMethod method;
+ if (inliner->IsIntrinsic(invoke->GetDexMethodIndex(), &method)) {
+ Intrinsics intrinsic = GetIntrinsic(method);
+
+ if (intrinsic != Intrinsics::kNone) {
+ if (!CheckInvokeType(intrinsic, invoke)) {
+ LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
+ << intrinsic << " for "
+ << PrettyMethod(invoke->GetDexMethodIndex(), *dex_file_);
+ } else {
+ invoke->SetIntrinsic(intrinsic);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) {
+ switch (intrinsic) {
+ case Intrinsics::kNone:
+ os << "No intrinsic.";
+ break;
+#define OPTIMIZING_INTRINSICS(Name, IsStatic) \
+ case Intrinsics::k ## Name: \
+ os << # Name; \
+ break;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef STATIC_INTRINSICS_LIST
+#undef VIRTUAL_INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+ }
+ return os;
+}
+
+} // namespace art
+
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
new file mode 100644
index 0000000..29cc8ef
--- /dev/null
+++ b/compiler/optimizing/intrinsics.h
@@ -0,0 +1,86 @@
+/*
+ * 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_INTRINSICS_H_
+#define ART_COMPILER_OPTIMIZING_INTRINSICS_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+class CompilerDriver;
+class DexFile;
+
+// Recognize intrinsics from HInvoke nodes.
+class IntrinsicsRecognizer : public HOptimization {
+ public:
+ IntrinsicsRecognizer(HGraph* graph, const DexFile* dex_file, CompilerDriver* driver)
+ : HOptimization(graph, true, "intrinsics_recognition"),
+ dex_file_(dex_file), driver_(driver) {}
+
+ void Run() OVERRIDE;
+
+ private:
+ const DexFile* dex_file_;
+ CompilerDriver* driver_;
+
+ DISALLOW_COPY_AND_ASSIGN(IntrinsicsRecognizer);
+};
+
+class IntrinsicVisitor : public ValueObject {
+ public:
+ virtual ~IntrinsicVisitor() {}
+
+ // Dispatch logic.
+
+ void Dispatch(HInvoke* invoke) {
+ switch (invoke->GetIntrinsic()) {
+ case Intrinsics::kNone:
+ return;
+#define OPTIMIZING_INTRINSICS(Name, IsStatic) \
+ case Intrinsics::k ## Name: \
+ Visit ## Name(invoke); \
+ return;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+ // Do not put a default case. That way the compiler will complain if we missed a case.
+ }
+ }
+
+ // Define visitor methods.
+
+#define OPTIMIZING_INTRINSICS(Name, IsStatic) \
+ virtual void Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
+ }
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+ protected:
+ IntrinsicVisitor() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(IntrinsicVisitor);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_INTRINSICS_H_
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
new file mode 100644
index 0000000..a82d80a
--- /dev/null
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -0,0 +1,883 @@
+/*
+ * 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.
+ */
+
+#include "intrinsics_arm.h"
+
+#include "arch/arm/instruction_set_features_arm.h"
+#include "code_generator_arm.h"
+#include "entrypoints/quick/quick_entrypoints.h"
+#include "intrinsics.h"
+#include "mirror/array-inl.h"
+#include "mirror/art_method.h"
+#include "mirror/string.h"
+#include "thread.h"
+#include "utils/arm/assembler_arm.h"
+
+namespace art {
+
+namespace arm {
+
+ArmAssembler* IntrinsicCodeGeneratorARM::GetAssembler() {
+ return codegen_->GetAssembler();
+}
+
+ArenaAllocator* IntrinsicCodeGeneratorARM::GetAllocator() {
+ return codegen_->GetGraph()->GetArena();
+}
+
+#define __ codegen->GetAssembler()->
+
+static void MoveFromReturnRegister(Location trg, Primitive::Type type, CodeGeneratorARM* codegen) {
+ if (!trg.IsValid()) {
+ DCHECK(type == Primitive::kPrimVoid);
+ return;
+ }
+
+ DCHECK_NE(type, Primitive::kPrimVoid);
+
+ if (Primitive::IsIntegralType(type)) {
+ if (type == Primitive::kPrimLong) {
+ Register trg_reg_lo = trg.AsRegisterPairLow<Register>();
+ Register trg_reg_hi = trg.AsRegisterPairHigh<Register>();
+ Register res_reg_lo = R0;
+ Register res_reg_hi = R1;
+ if (trg_reg_lo != res_reg_hi) {
+ if (trg_reg_lo != res_reg_lo) {
+ __ mov(trg_reg_lo, ShifterOperand(res_reg_lo));
+ __ mov(trg_reg_hi, ShifterOperand(res_reg_hi));
+ } else {
+ DCHECK_EQ(trg_reg_lo + 1, trg_reg_hi);
+ }
+ } else {
+ __ mov(trg_reg_hi, ShifterOperand(res_reg_hi));
+ __ mov(trg_reg_lo, ShifterOperand(res_reg_lo));
+ }
+ } else {
+ Register trg_reg = trg.AsRegister<Register>();
+ Register res_reg = R0;
+ if (trg_reg != res_reg) {
+ __ mov(trg_reg, ShifterOperand(res_reg));
+ }
+ }
+ } else {
+ UNIMPLEMENTED(FATAL) << "Floating-point return.";
+ }
+}
+
+static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorARM* codegen) {
+ if (invoke->InputCount() == 0) {
+ return;
+ }
+
+ LocationSummary* locations = invoke->GetLocations();
+ InvokeDexCallingConventionVisitor calling_convention_visitor;
+
+ // We're moving potentially two or more locations to locations that could overlap, so we need
+ // a parallel move resolver.
+ HParallelMove parallel_move(arena);
+
+ for (size_t i = 0; i < invoke->InputCount(); i++) {
+ HInstruction* input = invoke->InputAt(i);
+ Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType());
+ Location actual_loc = locations->InAt(i);
+
+ parallel_move.AddMove(actual_loc, cc_loc, nullptr);
+ }
+
+ codegen->GetMoveResolver()->EmitNativeCode(¶llel_move);
+}
+
+// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
+// call. This will copy the arguments into the positions for a regular call.
+//
+// Note: The actual parameters are required to be in the locations given by the invoke's location
+// summary. If an intrinsic modifies those locations before a slowpath call, they must be
+// restored!
+class IntrinsicSlowPathARM : public SlowPathCodeARM {
+ public:
+ explicit IntrinsicSlowPathARM(HInvoke* invoke) : invoke_(invoke) { }
+
+ void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
+ CodeGeneratorARM* codegen = down_cast<CodeGeneratorARM*>(codegen_in);
+ __ Bind(GetEntryLabel());
+
+ codegen->SaveLiveRegisters(invoke_->GetLocations());
+
+ MoveArguments(invoke_, codegen->GetGraph()->GetArena(), codegen);
+
+ if (invoke_->IsInvokeStaticOrDirect()) {
+ codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), kArtMethodRegister);
+ } else {
+ UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
+ UNREACHABLE();
+ }
+
+ // Copy the result back to the expected output.
+ Location out = invoke_->GetLocations()->Out();
+ if (out.IsValid()) {
+ DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
+ DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
+ MoveFromReturnRegister(out, invoke_->GetType(), codegen);
+ }
+
+ codegen->RestoreLiveRegisters(invoke_->GetLocations());
+ __ b(GetExitLabel());
+ }
+
+ private:
+ // The instruction where this slow path is happening.
+ HInvoke* const invoke_;
+
+ DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM);
+};
+
+#undef __
+
+bool IntrinsicLocationsBuilderARM::TryDispatch(HInvoke* invoke) {
+ Dispatch(invoke);
+ LocationSummary* res = invoke->GetLocations();
+ return res != nullptr && res->Intrinsified();
+}
+
+#define __ assembler->
+
+static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+}
+
+static void MoveFPToInt(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
+ Location input = locations->InAt(0);
+ Location output = locations->Out();
+ if (is64bit) {
+ __ vmovrrd(output.AsRegisterPairLow<Register>(),
+ output.AsRegisterPairHigh<Register>(),
+ FromLowSToD(input.AsFpuRegisterPairLow<SRegister>()));
+ } else {
+ __ vmovrs(output.AsRegister<Register>(), input.AsFpuRegister<SRegister>());
+ }
+}
+
+static void MoveIntToFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
+ Location input = locations->InAt(0);
+ Location output = locations->Out();
+ if (is64bit) {
+ __ vmovdrr(FromLowSToD(output.AsFpuRegisterPairLow<SRegister>()),
+ input.AsRegisterPairLow<Register>(),
+ input.AsRegisterPairHigh<Register>());
+ } else {
+ __ vmovsr(output.AsFpuRegister<SRegister>(), input.AsRegister<Register>());
+ }
+}
+
+void IntrinsicLocationsBuilderARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
+ CreateFPToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
+ CreateIntToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
+ MoveFPToInt(invoke->GetLocations(), true, GetAssembler());
+}
+void IntrinsicCodeGeneratorARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
+ MoveIntToFP(invoke->GetLocations(), true, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
+ CreateFPToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
+ CreateIntToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
+ MoveFPToInt(invoke->GetLocations(), false, GetAssembler());
+}
+void IntrinsicCodeGeneratorARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
+ MoveIntToFP(invoke->GetLocations(), false, GetAssembler());
+}
+
+static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+}
+
+static void MathAbsFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
+ Location in = locations->InAt(0);
+ Location out = locations->Out();
+
+ if (is64bit) {
+ __ vabsd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
+ FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
+ } else {
+ __ vabss(out.AsFpuRegister<SRegister>(), in.AsFpuRegister<SRegister>());
+ }
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathAbsDouble(HInvoke* invoke) {
+ CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathAbsDouble(HInvoke* invoke) {
+ MathAbsFP(invoke->GetLocations(), true, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathAbsFloat(HInvoke* invoke) {
+ CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathAbsFloat(HInvoke* invoke) {
+ MathAbsFP(invoke->GetLocations(), false, GetAssembler());
+}
+
+static void CreateIntToIntPlusTemp(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+static void GenAbsInteger(LocationSummary* locations,
+ bool is64bit,
+ ArmAssembler* assembler) {
+ Location in = locations->InAt(0);
+ Location output = locations->Out();
+
+ Register mask = locations->GetTemp(0).AsRegister<Register>();
+
+ if (is64bit) {
+ Register in_reg_lo = in.AsRegisterPairLow<Register>();
+ Register in_reg_hi = in.AsRegisterPairHigh<Register>();
+ Register out_reg_lo = output.AsRegisterPairLow<Register>();
+ Register out_reg_hi = output.AsRegisterPairHigh<Register>();
+
+ DCHECK_NE(out_reg_lo, in_reg_hi) << "Diagonal overlap unexpected.";
+
+ __ Asr(mask, in_reg_hi, 31);
+ __ adds(out_reg_lo, in_reg_lo, ShifterOperand(mask));
+ __ adc(out_reg_hi, in_reg_hi, ShifterOperand(mask));
+ __ eor(out_reg_lo, mask, ShifterOperand(out_reg_lo));
+ __ eor(out_reg_hi, mask, ShifterOperand(out_reg_hi));
+ } else {
+ Register in_reg = in.AsRegister<Register>();
+ Register out_reg = output.AsRegister<Register>();
+
+ __ Asr(mask, in_reg, 31);
+ __ add(out_reg, in_reg, ShifterOperand(mask));
+ __ eor(out_reg, mask, ShifterOperand(out_reg));
+ }
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathAbsInt(HInvoke* invoke) {
+ CreateIntToIntPlusTemp(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathAbsInt(HInvoke* invoke) {
+ GenAbsInteger(invoke->GetLocations(), false, GetAssembler());
+}
+
+
+void IntrinsicLocationsBuilderARM::VisitMathAbsLong(HInvoke* invoke) {
+ CreateIntToIntPlusTemp(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathAbsLong(HInvoke* invoke) {
+ GenAbsInteger(invoke->GetLocations(), true, GetAssembler());
+}
+
+static void GenMinMax(LocationSummary* locations,
+ bool is_min,
+ ArmAssembler* assembler) {
+ Register op1 = locations->InAt(0).AsRegister<Register>();
+ Register op2 = locations->InAt(1).AsRegister<Register>();
+ Register out = locations->Out().AsRegister<Register>();
+
+ __ cmp(op1, ShifterOperand(op2));
+
+ __ it((is_min) ? Condition::LT : Condition::GT, kItElse);
+ __ mov(out, ShifterOperand(op1), is_min ? Condition::LT : Condition::GT);
+ __ mov(out, ShifterOperand(op2), is_min ? Condition::GE : Condition::LE);
+}
+
+static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathMinIntInt(HInvoke* invoke) {
+ CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathMinIntInt(HInvoke* invoke) {
+ GenMinMax(invoke->GetLocations(), true, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathMaxIntInt(HInvoke* invoke) {
+ CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathMaxIntInt(HInvoke* invoke) {
+ GenMinMax(invoke->GetLocations(), false, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathSqrt(HInvoke* invoke) {
+ CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathSqrt(HInvoke* invoke) {
+ LocationSummary* locations = invoke->GetLocations();
+ ArmAssembler* assembler = GetAssembler();
+ __ vsqrtd(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
+ FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
+}
+
+void IntrinsicLocationsBuilderARM::VisitMemoryPeekByte(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMemoryPeekByte(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ // Ignore upper 4B of long address.
+ __ ldrsb(invoke->GetLocations()->Out().AsRegister<Register>(),
+ Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
+}
+
+void IntrinsicLocationsBuilderARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ // Ignore upper 4B of long address.
+ __ ldr(invoke->GetLocations()->Out().AsRegister<Register>(),
+ Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
+}
+
+void IntrinsicLocationsBuilderARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ // Ignore upper 4B of long address.
+ Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
+ // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
+ // exception. So we can't use ldrd as addr may be unaligned.
+ Register lo = invoke->GetLocations()->Out().AsRegisterPairLow<Register>();
+ Register hi = invoke->GetLocations()->Out().AsRegisterPairHigh<Register>();
+ if (addr == lo) {
+ __ ldr(hi, Address(addr, 4));
+ __ ldr(lo, Address(addr, 0));
+ } else {
+ __ ldr(lo, Address(addr, 0));
+ __ ldr(hi, Address(addr, 4));
+ }
+}
+
+void IntrinsicLocationsBuilderARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ // Ignore upper 4B of long address.
+ __ ldrsh(invoke->GetLocations()->Out().AsRegister<Register>(),
+ Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
+}
+
+static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+}
+
+void IntrinsicLocationsBuilderARM::VisitMemoryPokeByte(HInvoke* invoke) {
+ CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMemoryPokeByte(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ __ strb(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
+ Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
+}
+
+void IntrinsicLocationsBuilderARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
+ CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ __ str(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
+ Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
+}
+
+void IntrinsicLocationsBuilderARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
+ CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ // Ignore upper 4B of long address.
+ Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
+ // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
+ // exception. So we can't use ldrd as addr may be unaligned.
+ __ str(invoke->GetLocations()->InAt(1).AsRegisterPairLow<Register>(), Address(addr, 0));
+ __ str(invoke->GetLocations()->InAt(1).AsRegisterPairHigh<Register>(), Address(addr, 4));
+}
+
+void IntrinsicLocationsBuilderARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
+ CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ __ strh(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
+ Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
+}
+
+void IntrinsicLocationsBuilderARM::VisitThreadCurrentThread(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorARM::VisitThreadCurrentThread(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ __ LoadFromOffset(kLoadWord,
+ invoke->GetLocations()->Out().AsRegister<Register>(),
+ TR,
+ Thread::PeerOffset<kArmPointerSize>().Int32Value());
+}
+
+static void GenUnsafeGet(HInvoke* invoke,
+ Primitive::Type type,
+ bool is_volatile,
+ CodeGeneratorARM* codegen) {
+ LocationSummary* locations = invoke->GetLocations();
+ DCHECK((type == Primitive::kPrimInt) ||
+ (type == Primitive::kPrimLong) ||
+ (type == Primitive::kPrimNot));
+ ArmAssembler* assembler = codegen->GetAssembler();
+ Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
+ Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Long offset, lo part only.
+
+ if (type == Primitive::kPrimLong) {
+ Register trg_lo = locations->Out().AsRegisterPairLow<Register>();
+ __ add(IP, base, ShifterOperand(offset));
+ if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
+ Register trg_hi = locations->Out().AsRegisterPairHigh<Register>();
+ __ ldrexd(trg_lo, trg_hi, IP);
+ } else {
+ __ ldrd(trg_lo, Address(IP));
+ }
+ } else {
+ Register trg = locations->Out().AsRegister<Register>();
+ __ ldr(trg, Address(base, offset));
+ }
+
+ if (is_volatile) {
+ __ dmb(ISH);
+ }
+}
+
+static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicLocationsBuilderARM::VisitUnsafeGet(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafeGetLong(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafeGetObject(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitUnsafeGet(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimInt, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimInt, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafeGetLong(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimLong, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimLong, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafeGetObject(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimNot, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimNot, true, codegen_);
+}
+
+static void CreateIntIntIntIntToVoid(ArenaAllocator* arena,
+ const ArmInstructionSetFeatures& features,
+ Primitive::Type type,
+ bool is_volatile,
+ HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(3, Location::RequiresRegister());
+
+ if (type == Primitive::kPrimLong) {
+ // Potentially need temps for ldrexd-strexd loop.
+ if (is_volatile && !features.HasAtomicLdrdAndStrd()) {
+ locations->AddTemp(Location::RequiresRegister()); // Temp_lo.
+ locations->AddTemp(Location::RequiresRegister()); // Temp_hi.
+ }
+ } else if (type == Primitive::kPrimNot) {
+ // Temps for card-marking.
+ locations->AddTemp(Location::RequiresRegister()); // Temp.
+ locations->AddTemp(Location::RequiresRegister()); // Card.
+ }
+}
+
+void IntrinsicLocationsBuilderARM::VisitUnsafePut(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, false, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafePutOrdered(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, false, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafePutVolatile(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, true, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafePutObject(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, false, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, false, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, true, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafePutLong(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, false, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, false, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, true, invoke);
+}
+
+static void GenUnsafePut(LocationSummary* locations,
+ Primitive::Type type,
+ bool is_volatile,
+ bool is_ordered,
+ CodeGeneratorARM* codegen) {
+ ArmAssembler* assembler = codegen->GetAssembler();
+
+ Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
+ Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Long offset, lo part only.
+ Register value;
+
+ if (is_volatile || is_ordered) {
+ __ dmb(ISH);
+ }
+
+ if (type == Primitive::kPrimLong) {
+ Register value_lo = locations->InAt(3).AsRegisterPairLow<Register>();
+ value = value_lo;
+ if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
+ Register temp_lo = locations->GetTemp(0).AsRegister<Register>();
+ Register temp_hi = locations->GetTemp(1).AsRegister<Register>();
+ Register value_hi = locations->InAt(3).AsRegisterPairHigh<Register>();
+
+ __ add(IP, base, ShifterOperand(offset));
+ Label loop_head;
+ __ Bind(&loop_head);
+ __ ldrexd(temp_lo, temp_hi, IP);
+ __ strexd(temp_lo, value_lo, value_hi, IP);
+ __ cmp(temp_lo, ShifterOperand(0));
+ __ b(&loop_head, NE);
+ } else {
+ __ add(IP, base, ShifterOperand(offset));
+ __ strd(value_lo, Address(IP));
+ }
+ } else {
+ value = locations->InAt(3).AsRegister<Register>();
+ __ str(value, Address(base, offset));
+ }
+
+ if (is_volatile) {
+ __ dmb(ISH);
+ }
+
+ if (type == Primitive::kPrimNot) {
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+ Register card = locations->GetTemp(1).AsRegister<Register>();
+ codegen->MarkGCCard(temp, card, base, value);
+ }
+}
+
+void IntrinsicCodeGeneratorARM::VisitUnsafePut(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafePutOrdered(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafePutVolatile(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, true, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafePutObject(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, true, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafePutLong(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, false, codegen_);
+}
+
+static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* arena,
+ HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(3, Location::RequiresRegister());
+ locations->SetInAt(4, Location::RequiresRegister());
+
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+
+ locations->AddTemp(Location::RequiresRegister()); // Pointer.
+ locations->AddTemp(Location::RequiresRegister()); // Temp 1.
+ locations->AddTemp(Location::RequiresRegister()); // Temp 2.
+}
+
+static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM* codegen) {
+ DCHECK_NE(type, Primitive::kPrimLong);
+
+ ArmAssembler* assembler = codegen->GetAssembler();
+
+ Register out = locations->Out().AsRegister<Register>(); // Boolean result.
+
+ Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
+ Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Offset (discard high 4B).
+ Register expected_lo = locations->InAt(3).AsRegister<Register>(); // Expected.
+ Register value_lo = locations->InAt(4).AsRegister<Register>(); // Value.
+
+ Register tmp_ptr = locations->GetTemp(0).AsRegister<Register>(); // Pointer to actual memory.
+ Register tmp_lo = locations->GetTemp(1).AsRegister<Register>(); // Value in memory.
+
+ if (type == Primitive::kPrimNot) {
+ // Mark card for object assuming new value is stored. Worst case we will mark an unchanged
+ // object and scan the receiver at the next GC for nothing.
+ codegen->MarkGCCard(tmp_ptr, tmp_lo, base, value_lo);
+ }
+
+ // Prevent reordering with prior memory operations.
+ __ dmb(ISH);
+
+ __ add(tmp_ptr, base, ShifterOperand(offset));
+
+ // do {
+ // tmp = [r_ptr] - expected;
+ // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
+ // result = tmp != 0;
+
+ Label loop_head;
+ __ Bind(&loop_head);
+
+ __ ldrex(tmp_lo, tmp_ptr);
+
+ __ subs(tmp_lo, tmp_lo, ShifterOperand(expected_lo));
+
+ __ it(EQ, ItState::kItT);
+ __ strex(tmp_lo, value_lo, tmp_ptr, EQ);
+ __ cmp(tmp_lo, ShifterOperand(1), EQ);
+
+ __ b(&loop_head, EQ);
+
+ __ dmb(ISH);
+
+ __ rsbs(out, tmp_lo, ShifterOperand(1));
+ __ it(CC);
+ __ mov(out, ShifterOperand(0), CC);
+}
+
+void IntrinsicLocationsBuilderARM::VisitUnsafeCASInt(HInvoke* invoke ATTRIBUTE_UNUSED) {
+ CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke ATTRIBUTE_UNUSED) {
+ CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafeCASInt(HInvoke* invoke) {
+ GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) {
+ GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
+}
+
+void IntrinsicLocationsBuilderARM::VisitStringCharAt(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kCallOnSlowPath,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorARM::VisitStringCharAt(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ // Location of reference to data array
+ const MemberOffset value_offset = mirror::String::ValueOffset();
+ // Location of count
+ const MemberOffset count_offset = mirror::String::CountOffset();
+ // Starting offset within data array
+ const MemberOffset offset_offset = mirror::String::OffsetOffset();
+ // Start of char data with array_
+ const MemberOffset data_offset = mirror::Array::DataOffset(sizeof(uint16_t));
+
+ Register obj = locations->InAt(0).AsRegister<Register>(); // String object pointer.
+ Register idx = locations->InAt(1).AsRegister<Register>(); // Index of character.
+ Register out = locations->Out().AsRegister<Register>(); // Result character.
+
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+ Register array_temp = locations->GetTemp(1).AsRegister<Register>();
+
+ // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
+ // the cost.
+ // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
+ // we will not optimize the code for constants (which would save a register).
+
+ SlowPathCodeARM* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
+ codegen_->AddSlowPath(slow_path);
+
+ __ ldr(temp, Address(obj, count_offset.Int32Value())); // temp = str.length.
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
+ __ cmp(idx, ShifterOperand(temp));
+ __ b(slow_path->GetEntryLabel(), CS);
+
+ // Index computation.
+ __ ldr(temp, Address(obj, offset_offset.Int32Value())); // temp := str.offset.
+ __ ldr(array_temp, Address(obj, value_offset.Int32Value())); // array_temp := str.offset.
+ __ add(temp, temp, ShifterOperand(idx));
+ DCHECK_EQ(data_offset.Int32Value() % 2, 0); // We'll compensate by shifting.
+ __ add(temp, temp, ShifterOperand(data_offset.Int32Value() / 2));
+
+ // Load the value.
+ __ ldrh(out, Address(array_temp, temp, LSL, 1)); // out := array_temp[temp].
+
+ __ Bind(slow_path->GetExitLabel());
+}
+
+// Unimplemented intrinsics.
+
+#define UNIMPLEMENTED_INTRINSIC(Name) \
+void IntrinsicLocationsBuilderARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
+} \
+void IntrinsicCodeGeneratorARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
+}
+
+UNIMPLEMENTED_INTRINSIC(IntegerReverse)
+UNIMPLEMENTED_INTRINSIC(IntegerReverseBytes)
+UNIMPLEMENTED_INTRINSIC(LongReverse)
+UNIMPLEMENTED_INTRINSIC(LongReverseBytes)
+UNIMPLEMENTED_INTRINSIC(ShortReverseBytes)
+UNIMPLEMENTED_INTRINSIC(MathMinDoubleDouble)
+UNIMPLEMENTED_INTRINSIC(MathMinFloatFloat)
+UNIMPLEMENTED_INTRINSIC(MathMaxDoubleDouble)
+UNIMPLEMENTED_INTRINSIC(MathMaxFloatFloat)
+UNIMPLEMENTED_INTRINSIC(MathMinLongLong)
+UNIMPLEMENTED_INTRINSIC(MathMaxLongLong)
+UNIMPLEMENTED_INTRINSIC(MathCeil) // Could be done by changing rounding mode, maybe?
+UNIMPLEMENTED_INTRINSIC(MathFloor) // Could be done by changing rounding mode, maybe?
+UNIMPLEMENTED_INTRINSIC(MathRint)
+UNIMPLEMENTED_INTRINSIC(MathRoundDouble) // Could be done by changing rounding mode, maybe?
+UNIMPLEMENTED_INTRINSIC(MathRoundFloat) // Could be done by changing rounding mode, maybe?
+UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure.
+UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
+UNIMPLEMENTED_INTRINSIC(StringCompareTo)
+UNIMPLEMENTED_INTRINSIC(StringIsEmpty) // Might not want to do these two anyways, inlining should
+UNIMPLEMENTED_INTRINSIC(StringLength) // be good enough here.
+UNIMPLEMENTED_INTRINSIC(StringIndexOf)
+UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter)
+UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
+
+} // namespace arm
+} // namespace art
diff --git a/compiler/optimizing/intrinsics_arm.h b/compiler/optimizing/intrinsics_arm.h
new file mode 100644
index 0000000..8bfb7d4
--- /dev/null
+++ b/compiler/optimizing/intrinsics_arm.h
@@ -0,0 +1,88 @@
+/*
+ * 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_INTRINSICS_ARM_H_
+#define ART_COMPILER_OPTIMIZING_INTRINSICS_ARM_H_
+
+#include "intrinsics.h"
+
+namespace art {
+
+class ArenaAllocator;
+class ArmInstructionSetFeatures;
+class HInvokeStaticOrDirect;
+class HInvokeVirtual;
+
+namespace arm {
+
+class ArmAssembler;
+class CodeGeneratorARM;
+
+class IntrinsicLocationsBuilderARM FINAL : public IntrinsicVisitor {
+ public:
+ explicit IntrinsicLocationsBuilderARM(ArenaAllocator* arena,
+ const ArmInstructionSetFeatures& features)
+ : arena_(arena), features_(features) {}
+
+ // Define visitor methods.
+
+#define OPTIMIZING_INTRINSICS(Name, IsStatic) \
+ void Visit ## Name(HInvoke* invoke) OVERRIDE;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+ // Check whether an invoke is an intrinsic, and if so, create a location summary. Returns whether
+ // a corresponding LocationSummary with the intrinsified_ flag set was generated and attached to
+ // the invoke.
+ bool TryDispatch(HInvoke* invoke);
+
+ private:
+ ArenaAllocator* arena_;
+
+ const ArmInstructionSetFeatures& features_;
+
+ DISALLOW_COPY_AND_ASSIGN(IntrinsicLocationsBuilderARM);
+};
+
+class IntrinsicCodeGeneratorARM FINAL : public IntrinsicVisitor {
+ public:
+ explicit IntrinsicCodeGeneratorARM(CodeGeneratorARM* codegen) : codegen_(codegen) {}
+
+ // Define visitor methods.
+
+#define OPTIMIZING_INTRINSICS(Name, IsStatic) \
+ void Visit ## Name(HInvoke* invoke) OVERRIDE;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+ private:
+ ArmAssembler* GetAssembler();
+
+ ArenaAllocator* GetAllocator();
+
+ CodeGeneratorARM* codegen_;
+
+ DISALLOW_COPY_AND_ASSIGN(IntrinsicCodeGeneratorARM);
+};
+
+} // namespace arm
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_INTRINSICS_ARM_H_
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
new file mode 100644
index 0000000..8874edc
--- /dev/null
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -0,0 +1,1001 @@
+/*
+ * 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.
+ */
+
+#include "intrinsics_arm64.h"
+
+#include "code_generator_arm64.h"
+#include "common_arm64.h"
+#include "entrypoints/quick/quick_entrypoints.h"
+#include "intrinsics.h"
+#include "mirror/array-inl.h"
+#include "mirror/art_method.h"
+#include "mirror/string.h"
+#include "thread.h"
+#include "utils/arm64/assembler_arm64.h"
+#include "utils/arm64/constants_arm64.h"
+
+#include "a64/disasm-a64.h"
+#include "a64/macro-assembler-a64.h"
+
+using namespace vixl; // NOLINT(build/namespaces)
+
+namespace art {
+
+namespace arm64 {
+
+using helpers::DRegisterFrom;
+using helpers::FPRegisterFrom;
+using helpers::HeapOperand;
+using helpers::RegisterFrom;
+using helpers::SRegisterFrom;
+using helpers::WRegisterFrom;
+using helpers::XRegisterFrom;
+
+
+namespace {
+
+ALWAYS_INLINE inline MemOperand AbsoluteHeapOperandFrom(Location location, size_t offset = 0) {
+ return MemOperand(XRegisterFrom(location), offset);
+}
+
+} // namespace
+
+vixl::MacroAssembler* IntrinsicCodeGeneratorARM64::GetVIXLAssembler() {
+ return codegen_->GetAssembler()->vixl_masm_;
+}
+
+ArenaAllocator* IntrinsicCodeGeneratorARM64::GetAllocator() {
+ return codegen_->GetGraph()->GetArena();
+}
+
+#define __ codegen->GetAssembler()->vixl_masm_->
+
+static void MoveFromReturnRegister(Location trg,
+ Primitive::Type type,
+ CodeGeneratorARM64* codegen) {
+ if (!trg.IsValid()) {
+ DCHECK(type == Primitive::kPrimVoid);
+ return;
+ }
+
+ DCHECK_NE(type, Primitive::kPrimVoid);
+
+ if (Primitive::IsIntegralType(type)) {
+ Register trg_reg = RegisterFrom(trg, type);
+ Register res_reg = RegisterFrom(ARM64ReturnLocation(type), type);
+ __ Mov(trg_reg, res_reg, kDiscardForSameWReg);
+ } else {
+ FPRegister trg_reg = FPRegisterFrom(trg, type);
+ FPRegister res_reg = FPRegisterFrom(ARM64ReturnLocation(type), type);
+ __ Fmov(trg_reg, res_reg);
+ }
+}
+
+static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorARM64* codegen) {
+ if (invoke->InputCount() == 0) {
+ return;
+ }
+
+ LocationSummary* locations = invoke->GetLocations();
+ InvokeDexCallingConventionVisitor calling_convention_visitor;
+
+ // We're moving potentially two or more locations to locations that could overlap, so we need
+ // a parallel move resolver.
+ HParallelMove parallel_move(arena);
+
+ for (size_t i = 0; i < invoke->InputCount(); i++) {
+ HInstruction* input = invoke->InputAt(i);
+ Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType());
+ Location actual_loc = locations->InAt(i);
+
+ parallel_move.AddMove(actual_loc, cc_loc, nullptr);
+ }
+
+ codegen->GetMoveResolver()->EmitNativeCode(¶llel_move);
+}
+
+// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
+// call. This will copy the arguments into the positions for a regular call.
+//
+// Note: The actual parameters are required to be in the locations given by the invoke's location
+// summary. If an intrinsic modifies those locations before a slowpath call, they must be
+// restored!
+class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 {
+ public:
+ explicit IntrinsicSlowPathARM64(HInvoke* invoke) : invoke_(invoke) { }
+
+ void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
+ CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
+ __ Bind(GetEntryLabel());
+
+ codegen->SaveLiveRegisters(invoke_->GetLocations());
+
+ MoveArguments(invoke_, codegen->GetGraph()->GetArena(), codegen);
+
+ if (invoke_->IsInvokeStaticOrDirect()) {
+ codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), kArtMethodRegister);
+ } else {
+ UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
+ UNREACHABLE();
+ }
+
+ // Copy the result back to the expected output.
+ Location out = invoke_->GetLocations()->Out();
+ if (out.IsValid()) {
+ DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
+ DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
+ MoveFromReturnRegister(out, invoke_->GetType(), codegen);
+ }
+
+ codegen->RestoreLiveRegisters(invoke_->GetLocations());
+ __ B(GetExitLabel());
+ }
+
+ private:
+ // The instruction where this slow path is happening.
+ HInvoke* const invoke_;
+
+ DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM64);
+};
+
+#undef __
+
+bool IntrinsicLocationsBuilderARM64::TryDispatch(HInvoke* invoke) {
+ Dispatch(invoke);
+ LocationSummary* res = invoke->GetLocations();
+ return res != nullptr && res->Intrinsified();
+}
+
+#define __ masm->
+
+static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+}
+
+static void MoveFPToInt(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
+ Location input = locations->InAt(0);
+ Location output = locations->Out();
+ __ Fmov(is64bit ? XRegisterFrom(output) : WRegisterFrom(output),
+ is64bit ? DRegisterFrom(input) : SRegisterFrom(input));
+}
+
+static void MoveIntToFP(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
+ Location input = locations->InAt(0);
+ Location output = locations->Out();
+ __ Fmov(is64bit ? DRegisterFrom(output) : SRegisterFrom(output),
+ is64bit ? XRegisterFrom(input) : WRegisterFrom(input));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
+ CreateFPToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
+ CreateIntToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
+ MoveFPToInt(invoke->GetLocations(), true, GetVIXLAssembler());
+}
+void IntrinsicCodeGeneratorARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
+ MoveIntToFP(invoke->GetLocations(), true, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
+ CreateFPToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
+ CreateIntToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
+ MoveFPToInt(invoke->GetLocations(), false, GetVIXLAssembler());
+}
+void IntrinsicCodeGeneratorARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
+ MoveIntToFP(invoke->GetLocations(), false, GetVIXLAssembler());
+}
+
+static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+static void GenReverseBytes(LocationSummary* locations,
+ Primitive::Type type,
+ vixl::MacroAssembler* masm) {
+ Location in = locations->InAt(0);
+ Location out = locations->Out();
+
+ switch (type) {
+ case Primitive::kPrimShort:
+ __ Rev16(WRegisterFrom(out), WRegisterFrom(in));
+ __ Sxth(WRegisterFrom(out), WRegisterFrom(out));
+ break;
+ case Primitive::kPrimInt:
+ case Primitive::kPrimLong:
+ __ Rev(RegisterFrom(out, type), RegisterFrom(in, type));
+ break;
+ default:
+ LOG(FATAL) << "Unexpected size for reverse-bytes: " << type;
+ UNREACHABLE();
+ }
+}
+
+void IntrinsicLocationsBuilderARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
+ GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitLongReverseBytes(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitLongReverseBytes(HInvoke* invoke) {
+ GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitShortReverseBytes(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) {
+ GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler());
+}
+
+static void GenReverse(LocationSummary* locations,
+ Primitive::Type type,
+ vixl::MacroAssembler* masm) {
+ DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+
+ Location in = locations->InAt(0);
+ Location out = locations->Out();
+
+ __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitIntegerReverse(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitIntegerReverse(HInvoke* invoke) {
+ GenReverse(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitLongReverse(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) {
+ GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
+}
+
+static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+}
+
+static void MathAbsFP(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
+ Location in = locations->InAt(0);
+ Location out = locations->Out();
+
+ FPRegister in_reg = is64bit ? DRegisterFrom(in) : SRegisterFrom(in);
+ FPRegister out_reg = is64bit ? DRegisterFrom(out) : SRegisterFrom(out);
+
+ __ Fabs(out_reg, in_reg);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathAbsDouble(HInvoke* invoke) {
+ CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathAbsDouble(HInvoke* invoke) {
+ MathAbsFP(invoke->GetLocations(), true, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathAbsFloat(HInvoke* invoke) {
+ CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathAbsFloat(HInvoke* invoke) {
+ MathAbsFP(invoke->GetLocations(), false, GetVIXLAssembler());
+}
+
+static void CreateIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+static void GenAbsInteger(LocationSummary* locations,
+ bool is64bit,
+ vixl::MacroAssembler* masm) {
+ Location in = locations->InAt(0);
+ Location output = locations->Out();
+
+ Register in_reg = is64bit ? XRegisterFrom(in) : WRegisterFrom(in);
+ Register out_reg = is64bit ? XRegisterFrom(output) : WRegisterFrom(output);
+
+ __ Cmp(in_reg, Operand(0));
+ __ Cneg(out_reg, in_reg, lt);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) {
+ CreateIntToInt(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) {
+ GenAbsInteger(invoke->GetLocations(), false, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) {
+ CreateIntToInt(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) {
+ GenAbsInteger(invoke->GetLocations(), true, GetVIXLAssembler());
+}
+
+static void GenMinMaxFP(LocationSummary* locations,
+ bool is_min,
+ bool is_double,
+ vixl::MacroAssembler* masm) {
+ Location op1 = locations->InAt(0);
+ Location op2 = locations->InAt(1);
+ Location out = locations->Out();
+
+ FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1);
+ FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2);
+ FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out);
+ if (is_min) {
+ __ Fmin(out_reg, op1_reg, op2_reg);
+ } else {
+ __ Fmax(out_reg, op1_reg, op2_reg);
+ }
+}
+
+static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
+ CreateFPFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
+ GenMinMaxFP(invoke->GetLocations(), true, true, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
+ CreateFPFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
+ GenMinMaxFP(invoke->GetLocations(), true, false, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
+ CreateFPFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
+ GenMinMaxFP(invoke->GetLocations(), false, true, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
+ CreateFPFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
+ GenMinMaxFP(invoke->GetLocations(), false, false, GetVIXLAssembler());
+}
+
+static void GenMinMax(LocationSummary* locations,
+ bool is_min,
+ bool is_long,
+ vixl::MacroAssembler* masm) {
+ Location op1 = locations->InAt(0);
+ Location op2 = locations->InAt(1);
+ Location out = locations->Out();
+
+ Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
+ Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2);
+ Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out);
+
+ __ Cmp(op1_reg, op2_reg);
+ __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
+}
+
+static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) {
+ CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) {
+ GenMinMax(invoke->GetLocations(), true, false, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) {
+ CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) {
+ GenMinMax(invoke->GetLocations(), true, true, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) {
+ CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) {
+ GenMinMax(invoke->GetLocations(), false, false, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) {
+ CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) {
+ GenMinMax(invoke->GetLocations(), false, true, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) {
+ CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathSqrt(HInvoke* invoke) {
+ LocationSummary* locations = invoke->GetLocations();
+ vixl::MacroAssembler* masm = GetVIXLAssembler();
+ __ Fsqrt(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathCeil(HInvoke* invoke) {
+ CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathCeil(HInvoke* invoke) {
+ LocationSummary* locations = invoke->GetLocations();
+ vixl::MacroAssembler* masm = GetVIXLAssembler();
+ __ Frintp(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathFloor(HInvoke* invoke) {
+ CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathFloor(HInvoke* invoke) {
+ LocationSummary* locations = invoke->GetLocations();
+ vixl::MacroAssembler* masm = GetVIXLAssembler();
+ __ Frintm(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathRint(HInvoke* invoke) {
+ CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathRint(HInvoke* invoke) {
+ LocationSummary* locations = invoke->GetLocations();
+ vixl::MacroAssembler* masm = GetVIXLAssembler();
+ __ Frintn(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
+}
+
+static void CreateFPToIntPlusTempLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+static void GenMathRound(LocationSummary* locations,
+ bool is_double,
+ vixl::MacroAssembler* masm) {
+ FPRegister in_reg = is_double ?
+ DRegisterFrom(locations->InAt(0)) : SRegisterFrom(locations->InAt(0));
+ Register out_reg = is_double ?
+ XRegisterFrom(locations->Out()) : WRegisterFrom(locations->Out());
+ UseScratchRegisterScope temps(masm);
+ FPRegister temp1_reg = temps.AcquireSameSizeAs(in_reg);
+
+ // 0.5 can be encoded as an immediate, so use fmov.
+ if (is_double) {
+ __ Fmov(temp1_reg, static_cast<double>(0.5));
+ } else {
+ __ Fmov(temp1_reg, static_cast<float>(0.5));
+ }
+ __ Fadd(temp1_reg, in_reg, temp1_reg);
+ __ Fcvtms(out_reg, temp1_reg);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathRoundDouble(HInvoke* invoke) {
+ CreateFPToIntPlusTempLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathRoundDouble(HInvoke* invoke) {
+ GenMathRound(invoke->GetLocations(), true, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathRoundFloat(HInvoke* invoke) {
+ CreateFPToIntPlusTempLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathRoundFloat(HInvoke* invoke) {
+ GenMathRound(invoke->GetLocations(), false, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMemoryPeekByte(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMemoryPeekByte(HInvoke* invoke) {
+ vixl::MacroAssembler* masm = GetVIXLAssembler();
+ __ Ldrsb(WRegisterFrom(invoke->GetLocations()->Out()),
+ AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
+ vixl::MacroAssembler* masm = GetVIXLAssembler();
+ __ Ldr(WRegisterFrom(invoke->GetLocations()->Out()),
+ AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
+ vixl::MacroAssembler* masm = GetVIXLAssembler();
+ __ Ldr(XRegisterFrom(invoke->GetLocations()->Out()),
+ AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
+ vixl::MacroAssembler* masm = GetVIXLAssembler();
+ __ Ldrsh(WRegisterFrom(invoke->GetLocations()->Out()),
+ AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
+}
+
+static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMemoryPokeByte(HInvoke* invoke) {
+ CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMemoryPokeByte(HInvoke* invoke) {
+ vixl::MacroAssembler* masm = GetVIXLAssembler();
+ __ Strb(WRegisterFrom(invoke->GetLocations()->InAt(1)),
+ AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
+ CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
+ vixl::MacroAssembler* masm = GetVIXLAssembler();
+ __ Str(WRegisterFrom(invoke->GetLocations()->InAt(1)),
+ AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
+ CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
+ vixl::MacroAssembler* masm = GetVIXLAssembler();
+ __ Str(XRegisterFrom(invoke->GetLocations()->InAt(1)),
+ AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
+ CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
+ vixl::MacroAssembler* masm = GetVIXLAssembler();
+ __ Strh(WRegisterFrom(invoke->GetLocations()->InAt(1)),
+ AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitThreadCurrentThread(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorARM64::VisitThreadCurrentThread(HInvoke* invoke) {
+ codegen_->Load(Primitive::kPrimNot, WRegisterFrom(invoke->GetLocations()->Out()),
+ MemOperand(tr, Thread::PeerOffset<8>().Int32Value()));
+}
+
+static void GenUnsafeGet(HInvoke* invoke,
+ Primitive::Type type,
+ bool is_volatile,
+ CodeGeneratorARM64* codegen) {
+ LocationSummary* locations = invoke->GetLocations();
+ DCHECK((type == Primitive::kPrimInt) ||
+ (type == Primitive::kPrimLong) ||
+ (type == Primitive::kPrimNot));
+ vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
+ Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
+ Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
+ Register trg = RegisterFrom(locations->Out(), type);
+
+ MemOperand mem_op(base.X(), offset);
+ if (is_volatile) {
+ if (kUseAcquireRelease) {
+ codegen->LoadAcquire(invoke, trg, mem_op);
+ } else {
+ codegen->Load(type, trg, mem_op);
+ __ Dmb(InnerShareable, BarrierReads);
+ }
+ } else {
+ codegen->Load(type, trg, mem_op);
+ }
+}
+
+static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLong(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObject(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitUnsafeGet(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimInt, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimInt, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLong(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimLong, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimLong, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObject(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimNot, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimNot, true, codegen_);
+}
+
+static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(3, Location::RequiresRegister());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitUnsafePut(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafePutObject(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafePutLong(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
+ CreateIntIntIntIntToVoid(arena_, invoke);
+}
+
+static void GenUnsafePut(LocationSummary* locations,
+ Primitive::Type type,
+ bool is_volatile,
+ bool is_ordered,
+ CodeGeneratorARM64* codegen) {
+ vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
+
+ Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
+ Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
+ Register value = RegisterFrom(locations->InAt(3), type);
+
+ MemOperand mem_op(base.X(), offset);
+
+ if (is_volatile || is_ordered) {
+ if (kUseAcquireRelease) {
+ codegen->StoreRelease(type, value, mem_op);
+ } else {
+ __ Dmb(InnerShareable, BarrierAll);
+ codegen->Store(type, value, mem_op);
+ if (is_volatile) {
+ __ Dmb(InnerShareable, BarrierReads);
+ }
+ }
+ } else {
+ codegen->Store(type, value, mem_op);
+ }
+
+ if (type == Primitive::kPrimNot) {
+ codegen->MarkGCCard(base, value);
+ }
+}
+
+void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, true, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, true, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, false, codegen_);
+}
+
+static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(3, Location::RequiresRegister());
+ locations->SetInAt(4, Location::RequiresRegister());
+
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM64* codegen) {
+ // TODO: Currently we use acquire-release load-stores in the CAS loop. One could reasonably write
+ // a version relying on simple exclusive load-stores and barriers instead.
+ static_assert(kUseAcquireRelease, "Non-acquire-release inlined CAS not implemented, yet.");
+
+ vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
+
+ Register out = WRegisterFrom(locations->Out()); // Boolean result.
+
+ Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
+ Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
+ Register expected = RegisterFrom(locations->InAt(3), type); // Expected.
+ Register value = RegisterFrom(locations->InAt(4), type); // Value.
+
+ // This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
+ if (type == Primitive::kPrimNot) {
+ // Mark card for object assuming new value is stored.
+ codegen->MarkGCCard(base, value);
+ }
+
+ UseScratchRegisterScope temps(masm);
+ Register tmp_ptr = temps.AcquireX(); // Pointer to actual memory.
+ Register tmp_value = temps.AcquireSameSizeAs(value); // Value in memory.
+
+ Register tmp_32 = tmp_value.W();
+
+ __ Add(tmp_ptr, base.X(), Operand(offset));
+
+ // do {
+ // tmp_value = [tmp_ptr] - expected;
+ // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
+ // result = tmp_value != 0;
+
+ vixl::Label loop_head, exit_loop;
+ __ Bind(&loop_head);
+
+ __ Ldaxr(tmp_value, MemOperand(tmp_ptr));
+ __ Cmp(tmp_value, expected);
+ __ B(&exit_loop, ne);
+
+ __ Stlxr(tmp_32, value, MemOperand(tmp_ptr));
+ __ Cbnz(tmp_32, &loop_head);
+
+ __ Bind(&exit_loop);
+ __ Cset(out, eq);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
+ CreateIntIntIntIntIntToInt(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
+ CreateIntIntIntIntIntToInt(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
+ CreateIntIntIntIntIntToInt(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
+ GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) {
+ GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
+ GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitStringCharAt(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kCallOnSlowPath,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ // In case we need to go in the slow path, we can't have the output be the same
+ // as the input: the current liveness analysis considers the input to be live
+ // at the point of the call.
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitStringCharAt(HInvoke* invoke) {
+ vixl::MacroAssembler* masm = GetVIXLAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ // Location of reference to data array
+ const MemberOffset value_offset = mirror::String::ValueOffset();
+ // Location of count
+ const MemberOffset count_offset = mirror::String::CountOffset();
+ // Starting offset within data array
+ const MemberOffset offset_offset = mirror::String::OffsetOffset();
+ // Start of char data with array_
+ const MemberOffset data_offset = mirror::Array::DataOffset(sizeof(uint16_t));
+
+ Register obj = WRegisterFrom(locations->InAt(0)); // String object pointer.
+ Register idx = WRegisterFrom(locations->InAt(1)); // Index of character.
+ Register out = WRegisterFrom(locations->Out()); // Result character.
+
+ UseScratchRegisterScope temps(masm);
+ Register temp = temps.AcquireW();
+ Register array_temp = temps.AcquireW(); // We can trade this for worse scheduling.
+
+ // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
+ // the cost.
+ // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
+ // we will not optimize the code for constants (which would save a register).
+
+ SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
+ codegen_->AddSlowPath(slow_path);
+
+ __ Ldr(temp, HeapOperand(obj, count_offset)); // temp = str.length.
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
+ __ Cmp(idx, temp);
+ __ B(hs, slow_path->GetEntryLabel());
+
+ // Index computation.
+ __ Ldr(temp, HeapOperand(obj, offset_offset)); // temp := str.offset.
+ __ Ldr(array_temp, HeapOperand(obj, value_offset)); // array_temp := str.offset.
+ __ Add(temp, temp, idx);
+ DCHECK_EQ(data_offset.Int32Value() % 2, 0); // We'll compensate by shifting.
+ __ Add(temp, temp, Operand(data_offset.Int32Value() / 2));
+
+ // Load the value.
+ __ Ldrh(out, MemOperand(array_temp.X(), temp, UXTW, 1)); // out := array_temp[temp].
+
+ __ Bind(slow_path->GetExitLabel());
+}
+
+// Unimplemented intrinsics.
+
+#define UNIMPLEMENTED_INTRINSIC(Name) \
+void IntrinsicLocationsBuilderARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
+} \
+void IntrinsicCodeGeneratorARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
+}
+
+UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
+UNIMPLEMENTED_INTRINSIC(StringCompareTo)
+UNIMPLEMENTED_INTRINSIC(StringIsEmpty) // Might not want to do these two anyways, inlining should
+UNIMPLEMENTED_INTRINSIC(StringLength) // be good enough here.
+UNIMPLEMENTED_INTRINSIC(StringIndexOf)
+UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter)
+UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
+
+} // namespace arm64
+} // namespace art
diff --git a/compiler/optimizing/intrinsics_arm64.h b/compiler/optimizing/intrinsics_arm64.h
new file mode 100644
index 0000000..ba21889
--- /dev/null
+++ b/compiler/optimizing/intrinsics_arm64.h
@@ -0,0 +1,88 @@
+/*
+ * 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_INTRINSICS_ARM64_H_
+#define ART_COMPILER_OPTIMIZING_INTRINSICS_ARM64_H_
+
+#include "intrinsics.h"
+
+namespace vixl {
+
+class MacroAssembler;
+
+} // namespace vixl
+
+namespace art {
+
+class ArenaAllocator;
+class HInvokeStaticOrDirect;
+class HInvokeVirtual;
+
+namespace arm64 {
+
+class CodeGeneratorARM64;
+
+class IntrinsicLocationsBuilderARM64 FINAL : public IntrinsicVisitor {
+ public:
+ explicit IntrinsicLocationsBuilderARM64(ArenaAllocator* arena) : arena_(arena) {}
+
+ // Define visitor methods.
+
+#define OPTIMIZING_INTRINSICS(Name, IsStatic) \
+ void Visit ## Name(HInvoke* invoke) OVERRIDE;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+ // Check whether an invoke is an intrinsic, and if so, create a location summary. Returns whether
+ // a corresponding LocationSummary with the intrinsified_ flag set was generated and attached to
+ // the invoke.
+ bool TryDispatch(HInvoke* invoke);
+
+ private:
+ ArenaAllocator* arena_;
+
+ DISALLOW_COPY_AND_ASSIGN(IntrinsicLocationsBuilderARM64);
+};
+
+class IntrinsicCodeGeneratorARM64 FINAL : public IntrinsicVisitor {
+ public:
+ explicit IntrinsicCodeGeneratorARM64(CodeGeneratorARM64* codegen) : codegen_(codegen) {}
+
+ // Define visitor methods.
+
+#define OPTIMIZING_INTRINSICS(Name, IsStatic) \
+ void Visit ## Name(HInvoke* invoke) OVERRIDE;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+ private:
+ vixl::MacroAssembler* GetVIXLAssembler();
+
+ ArenaAllocator* GetAllocator();
+
+ CodeGeneratorARM64* codegen_;
+
+ DISALLOW_COPY_AND_ASSIGN(IntrinsicCodeGeneratorARM64);
+};
+
+} // namespace arm64
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_INTRINSICS_ARM64_H_
diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h
new file mode 100644
index 0000000..9cc77c6
--- /dev/null
+++ b/compiler/optimizing/intrinsics_list.h
@@ -0,0 +1,88 @@
+/*
+ * 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_INTRINSICS_LIST_H_
+#define ART_COMPILER_OPTIMIZING_INTRINSICS_LIST_H_
+
+// All intrinsics supported by the optimizing compiler. Format is name, then whether it is expected
+// to be a HInvokeStaticOrDirect node (compared to HInvokeVirtual).
+
+#define INTRINSICS_LIST(V) \
+ V(DoubleDoubleToRawLongBits, kStatic) \
+ V(DoubleLongBitsToDouble, kStatic) \
+ V(FloatFloatToRawIntBits, kStatic) \
+ V(FloatIntBitsToFloat, kStatic) \
+ V(IntegerReverse, kStatic) \
+ V(IntegerReverseBytes, kStatic) \
+ V(LongReverse, kStatic) \
+ V(LongReverseBytes, kStatic) \
+ V(ShortReverseBytes, kStatic) \
+ V(MathAbsDouble, kStatic) \
+ V(MathAbsFloat, kStatic) \
+ V(MathAbsLong, kStatic) \
+ V(MathAbsInt, kStatic) \
+ V(MathMinDoubleDouble, kStatic) \
+ V(MathMinFloatFloat, kStatic) \
+ V(MathMinLongLong, kStatic) \
+ V(MathMinIntInt, kStatic) \
+ V(MathMaxDoubleDouble, kStatic) \
+ V(MathMaxFloatFloat, kStatic) \
+ V(MathMaxLongLong, kStatic) \
+ V(MathMaxIntInt, kStatic) \
+ V(MathSqrt, kStatic) \
+ V(MathCeil, kStatic) \
+ V(MathFloor, kStatic) \
+ V(MathRint, kStatic) \
+ V(MathRoundDouble, kStatic) \
+ V(MathRoundFloat, kStatic) \
+ V(SystemArrayCopyChar, kStatic) \
+ V(ThreadCurrentThread, kStatic) \
+ V(MemoryPeekByte, kStatic) \
+ V(MemoryPeekIntNative, kStatic) \
+ V(MemoryPeekLongNative, kStatic) \
+ V(MemoryPeekShortNative, kStatic) \
+ V(MemoryPokeByte, kStatic) \
+ V(MemoryPokeIntNative, kStatic) \
+ V(MemoryPokeLongNative, kStatic) \
+ V(MemoryPokeShortNative, kStatic) \
+ V(StringCharAt, kDirect) \
+ V(StringCompareTo, kDirect) \
+ V(StringIsEmpty, kDirect) \
+ V(StringIndexOf, kDirect) \
+ V(StringIndexOfAfter, kDirect) \
+ V(StringLength, kDirect) \
+ V(UnsafeCASInt, kDirect) \
+ V(UnsafeCASLong, kDirect) \
+ V(UnsafeCASObject, kDirect) \
+ V(UnsafeGet, kDirect) \
+ V(UnsafeGetVolatile, kDirect) \
+ V(UnsafeGetObject, kDirect) \
+ V(UnsafeGetObjectVolatile, kDirect) \
+ V(UnsafeGetLong, kDirect) \
+ V(UnsafeGetLongVolatile, kDirect) \
+ V(UnsafePut, kDirect) \
+ V(UnsafePutOrdered, kDirect) \
+ V(UnsafePutVolatile, kDirect) \
+ V(UnsafePutObject, kDirect) \
+ V(UnsafePutObjectOrdered, kDirect) \
+ V(UnsafePutObjectVolatile, kDirect) \
+ V(UnsafePutLong, kDirect) \
+ V(UnsafePutLongOrdered, kDirect) \
+ V(UnsafePutLongVolatile, kDirect) \
+ V(ReferenceGetReferent, kDirect)
+
+#endif // ART_COMPILER_OPTIMIZING_INTRINSICS_LIST_H_
+#undef ART_COMPILER_OPTIMIZING_INTRINSICS_LIST_H_ // #define is only for lint.
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
new file mode 100644
index 0000000..c73f092
--- /dev/null
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -0,0 +1,1000 @@
+/*
+ * 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.
+ */
+
+#include "intrinsics_x86_64.h"
+
+#include "code_generator_x86_64.h"
+#include "entrypoints/quick/quick_entrypoints.h"
+#include "intrinsics.h"
+#include "mirror/array-inl.h"
+#include "mirror/art_method.h"
+#include "mirror/string.h"
+#include "thread.h"
+#include "utils/x86_64/assembler_x86_64.h"
+#include "utils/x86_64/constants_x86_64.h"
+
+namespace art {
+
+namespace x86_64 {
+
+X86_64Assembler* IntrinsicCodeGeneratorX86_64::GetAssembler() {
+ return reinterpret_cast<X86_64Assembler*>(codegen_->GetAssembler());
+}
+
+ArenaAllocator* IntrinsicCodeGeneratorX86_64::GetAllocator() {
+ return codegen_->GetGraph()->GetArena();
+}
+
+bool IntrinsicLocationsBuilderX86_64::TryDispatch(HInvoke* invoke) {
+ Dispatch(invoke);
+ const LocationSummary* res = invoke->GetLocations();
+ return res != nullptr && res->Intrinsified();
+}
+
+#define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())->
+
+// TODO: trg as memory.
+static void MoveFromReturnRegister(Location trg,
+ Primitive::Type type,
+ CodeGeneratorX86_64* codegen) {
+ if (!trg.IsValid()) {
+ DCHECK(type == Primitive::kPrimVoid);
+ return;
+ }
+
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ CpuRegister trg_reg = trg.AsRegister<CpuRegister>();
+ if (trg_reg.AsRegister() != RAX) {
+ __ movl(trg_reg, CpuRegister(RAX));
+ }
+ break;
+ }
+ case Primitive::kPrimLong: {
+ CpuRegister trg_reg = trg.AsRegister<CpuRegister>();
+ if (trg_reg.AsRegister() != RAX) {
+ __ movq(trg_reg, CpuRegister(RAX));
+ }
+ break;
+ }
+
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected void type for valid location " << trg;
+ UNREACHABLE();
+
+ case Primitive::kPrimDouble: {
+ XmmRegister trg_reg = trg.AsFpuRegister<XmmRegister>();
+ if (trg_reg.AsFloatRegister() != XMM0) {
+ __ movsd(trg_reg, XmmRegister(XMM0));
+ }
+ break;
+ }
+ case Primitive::kPrimFloat: {
+ XmmRegister trg_reg = trg.AsFpuRegister<XmmRegister>();
+ if (trg_reg.AsFloatRegister() != XMM0) {
+ __ movss(trg_reg, XmmRegister(XMM0));
+ }
+ break;
+ }
+ }
+}
+
+static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorX86_64* codegen) {
+ if (invoke->InputCount() == 0) {
+ return;
+ }
+
+ LocationSummary* locations = invoke->GetLocations();
+ InvokeDexCallingConventionVisitor calling_convention_visitor;
+
+ // We're moving potentially two or more locations to locations that could overlap, so we need
+ // a parallel move resolver.
+ HParallelMove parallel_move(arena);
+
+ for (size_t i = 0; i < invoke->InputCount(); i++) {
+ HInstruction* input = invoke->InputAt(i);
+ Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType());
+ Location actual_loc = locations->InAt(i);
+
+ parallel_move.AddMove(actual_loc, cc_loc, nullptr);
+ }
+
+ codegen->GetMoveResolver()->EmitNativeCode(¶llel_move);
+}
+
+// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
+// call. This will copy the arguments into the positions for a regular call.
+//
+// Note: The actual parameters are required to be in the locations given by the invoke's location
+// summary. If an intrinsic modifies those locations before a slowpath call, they must be
+// restored!
+class IntrinsicSlowPathX86_64 : public SlowPathCodeX86_64 {
+ public:
+ explicit IntrinsicSlowPathX86_64(HInvoke* invoke) : invoke_(invoke) { }
+
+ void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
+ CodeGeneratorX86_64* codegen = down_cast<CodeGeneratorX86_64*>(codegen_in);
+ __ Bind(GetEntryLabel());
+
+ codegen->SaveLiveRegisters(invoke_->GetLocations());
+
+ MoveArguments(invoke_, codegen->GetGraph()->GetArena(), codegen);
+
+ if (invoke_->IsInvokeStaticOrDirect()) {
+ codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), CpuRegister(RDI));
+ } else {
+ UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
+ UNREACHABLE();
+ }
+
+ // Copy the result back to the expected output.
+ Location out = invoke_->GetLocations()->Out();
+ if (out.IsValid()) {
+ DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
+ DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
+ MoveFromReturnRegister(out, invoke_->GetType(), codegen);
+ }
+
+ codegen->RestoreLiveRegisters(invoke_->GetLocations());
+ __ jmp(GetExitLabel());
+ }
+
+ private:
+ // The instruction where this slow path is happening.
+ HInvoke* const invoke_;
+
+ DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathX86_64);
+};
+
+#undef __
+#define __ assembler->
+
+static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+}
+
+static void MoveFPToInt(LocationSummary* locations, bool is64bit, X86_64Assembler* assembler) {
+ Location input = locations->InAt(0);
+ Location output = locations->Out();
+ __ movd(output.AsRegister<CpuRegister>(), input.AsFpuRegister<XmmRegister>(), is64bit);
+}
+
+static void MoveIntToFP(LocationSummary* locations, bool is64bit, X86_64Assembler* assembler) {
+ Location input = locations->InAt(0);
+ Location output = locations->Out();
+ __ movd(output.AsFpuRegister<XmmRegister>(), input.AsRegister<CpuRegister>(), is64bit);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
+ CreateFPToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
+ CreateIntToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
+ MoveFPToInt(invoke->GetLocations(), true, GetAssembler());
+}
+void IntrinsicCodeGeneratorX86_64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
+ MoveIntToFP(invoke->GetLocations(), true, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
+ CreateFPToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
+ CreateIntToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
+ MoveFPToInt(invoke->GetLocations(), false, GetAssembler());
+}
+void IntrinsicCodeGeneratorX86_64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
+ MoveIntToFP(invoke->GetLocations(), false, GetAssembler());
+}
+
+static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+}
+
+static void GenReverseBytes(LocationSummary* locations,
+ Primitive::Type size,
+ X86_64Assembler* assembler) {
+ CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+
+ switch (size) {
+ case Primitive::kPrimShort:
+ // TODO: Can be done with an xchg of 8b registers. This is straight from Quick.
+ __ bswapl(out);
+ __ sarl(out, Immediate(16));
+ break;
+ case Primitive::kPrimInt:
+ __ bswapl(out);
+ break;
+ case Primitive::kPrimLong:
+ __ bswapq(out);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected size for reverse-bytes: " << size;
+ UNREACHABLE();
+ }
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitIntegerReverseBytes(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitIntegerReverseBytes(HInvoke* invoke) {
+ GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitLongReverseBytes(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitLongReverseBytes(HInvoke* invoke) {
+ GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitShortReverseBytes(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitShortReverseBytes(HInvoke* invoke) {
+ GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler());
+}
+
+
+// TODO: Consider Quick's way of doing Double abs through integer operations, as the immediate we
+// need is 64b.
+
+static void CreateFloatToFloatPlusTemps(ArenaAllocator* arena, HInvoke* invoke) {
+ // TODO: Enable memory operations when the assembler supports them.
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ // TODO: Allow x86 to work with memory. This requires assembler support, see below.
+ // locations->SetInAt(0, Location::Any()); // X86 can work on memory directly.
+ locations->SetOut(Location::SameAsFirstInput());
+ locations->AddTemp(Location::RequiresRegister()); // Immediate constant.
+ locations->AddTemp(Location::RequiresFpuRegister()); // FP version of above.
+}
+
+static void MathAbsFP(LocationSummary* locations, bool is64bit, X86_64Assembler* assembler) {
+ Location output = locations->Out();
+ CpuRegister cpu_temp = locations->GetTemp(0).AsRegister<CpuRegister>();
+
+ if (output.IsFpuRegister()) {
+ // In-register
+ XmmRegister xmm_temp = locations->GetTemp(1).AsFpuRegister<XmmRegister>();
+
+ if (is64bit) {
+ __ movq(cpu_temp, Immediate(INT64_C(0x7FFFFFFFFFFFFFFF)));
+ __ movd(xmm_temp, cpu_temp);
+ __ andpd(output.AsFpuRegister<XmmRegister>(), xmm_temp);
+ } else {
+ __ movl(cpu_temp, Immediate(INT64_C(0x7FFFFFFF)));
+ __ movd(xmm_temp, cpu_temp);
+ __ andps(output.AsFpuRegister<XmmRegister>(), xmm_temp);
+ }
+ } else {
+ // TODO: update when assember support is available.
+ UNIMPLEMENTED(FATAL) << "Needs assembler support.";
+// Once assembler support is available, in-memory operations look like this:
+// if (is64bit) {
+// DCHECK(output.IsDoubleStackSlot());
+// // No 64b and with literal.
+// __ movq(cpu_temp, Immediate(INT64_C(0x7FFFFFFFFFFFFFFF)));
+// __ andq(Address(CpuRegister(RSP), output.GetStackIndex()), cpu_temp);
+// } else {
+// DCHECK(output.IsStackSlot());
+// // Can use and with a literal directly.
+// __ andl(Address(CpuRegister(RSP), output.GetStackIndex()), Immediate(INT64_C(0x7FFFFFFF)));
+// }
+ }
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathAbsDouble(HInvoke* invoke) {
+ CreateFloatToFloatPlusTemps(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathAbsDouble(HInvoke* invoke) {
+ MathAbsFP(invoke->GetLocations(), true, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathAbsFloat(HInvoke* invoke) {
+ CreateFloatToFloatPlusTemps(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathAbsFloat(HInvoke* invoke) {
+ MathAbsFP(invoke->GetLocations(), false, GetAssembler());
+}
+
+static void CreateIntToIntPlusTemp(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+static void GenAbsInteger(LocationSummary* locations, bool is64bit, X86_64Assembler* assembler) {
+ Location output = locations->Out();
+ CpuRegister out = output.AsRegister<CpuRegister>();
+ CpuRegister mask = locations->GetTemp(0).AsRegister<CpuRegister>();
+
+ if (is64bit) {
+ // Create mask.
+ __ movq(mask, out);
+ __ sarq(mask, Immediate(63));
+ // Add mask.
+ __ addq(out, mask);
+ __ xorq(out, mask);
+ } else {
+ // Create mask.
+ __ movl(mask, out);
+ __ sarl(mask, Immediate(31));
+ // Add mask.
+ __ addl(out, mask);
+ __ xorl(out, mask);
+ }
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathAbsInt(HInvoke* invoke) {
+ CreateIntToIntPlusTemp(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathAbsInt(HInvoke* invoke) {
+ GenAbsInteger(invoke->GetLocations(), false, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathAbsLong(HInvoke* invoke) {
+ CreateIntToIntPlusTemp(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathAbsLong(HInvoke* invoke) {
+ GenAbsInteger(invoke->GetLocations(), true, GetAssembler());
+}
+
+static void GenMinMaxFP(LocationSummary* locations, bool is_min, bool is_double,
+ X86_64Assembler* assembler) {
+ Location op1_loc = locations->InAt(0);
+ Location op2_loc = locations->InAt(1);
+ Location out_loc = locations->Out();
+ XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
+
+ // Shortcut for same input locations.
+ if (op1_loc.Equals(op2_loc)) {
+ DCHECK(out_loc.Equals(op1_loc));
+ return;
+ }
+
+ // (out := op1)
+ // out <=? op2
+ // if Nan jmp Nan_label
+ // if out is min jmp done
+ // if op2 is min jmp op2_label
+ // handle -0/+0
+ // jmp done
+ // Nan_label:
+ // out := NaN
+ // op2_label:
+ // out := op2
+ // done:
+ //
+ // This removes one jmp, but needs to copy one input (op1) to out.
+ //
+ // TODO: This is straight from Quick (except literal pool). Make NaN an out-of-line slowpath?
+
+ XmmRegister op2 = op2_loc.AsFpuRegister<XmmRegister>();
+
+ Label nan, done, op2_label;
+ if (is_double) {
+ __ ucomisd(out, op2);
+ } else {
+ __ ucomiss(out, op2);
+ }
+
+ __ j(Condition::kParityEven, &nan);
+
+ __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label);
+ __ j(is_min ? Condition::kBelow : Condition::kAbove, &done);
+
+ // Handle 0.0/-0.0.
+ if (is_min) {
+ if (is_double) {
+ __ orpd(out, op2);
+ } else {
+ __ orps(out, op2);
+ }
+ } else {
+ if (is_double) {
+ __ andpd(out, op2);
+ } else {
+ __ andps(out, op2);
+ }
+ }
+ __ jmp(&done);
+
+ // NaN handling.
+ __ Bind(&nan);
+ CpuRegister cpu_temp = locations->GetTemp(0).AsRegister<CpuRegister>();
+ // TODO: Literal pool. Trades 64b immediate in CPU reg for direct memory access.
+ if (is_double) {
+ __ movq(cpu_temp, Immediate(INT64_C(0x7FF8000000000000)));
+ } else {
+ __ movl(cpu_temp, Immediate(INT64_C(0x7FC00000)));
+ }
+ __ movd(out, cpu_temp, is_double);
+ __ jmp(&done);
+
+ // out := op2;
+ __ Bind(&op2_label);
+ if (is_double) {
+ __ movsd(out, op2);
+ } else {
+ __ movss(out, op2);
+ }
+
+ // Done.
+ __ Bind(&done);
+}
+
+static void CreateFPFPToFPPlusTempLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ // The following is sub-optimal, but all we can do for now. It would be fine to also accept
+ // the second input to be the output (we can simply swap inputs).
+ locations->SetOut(Location::SameAsFirstInput());
+ locations->AddTemp(Location::RequiresRegister()); // Immediate constant.
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathMinDoubleDouble(HInvoke* invoke) {
+ CreateFPFPToFPPlusTempLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathMinDoubleDouble(HInvoke* invoke) {
+ GenMinMaxFP(invoke->GetLocations(), true, true, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathMinFloatFloat(HInvoke* invoke) {
+ CreateFPFPToFPPlusTempLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathMinFloatFloat(HInvoke* invoke) {
+ GenMinMaxFP(invoke->GetLocations(), true, false, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
+ CreateFPFPToFPPlusTempLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
+ GenMinMaxFP(invoke->GetLocations(), false, true, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathMaxFloatFloat(HInvoke* invoke) {
+ CreateFPFPToFPPlusTempLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathMaxFloatFloat(HInvoke* invoke) {
+ GenMinMaxFP(invoke->GetLocations(), false, false, GetAssembler());
+}
+
+static void GenMinMax(LocationSummary* locations, bool is_min, bool is_long,
+ X86_64Assembler* assembler) {
+ Location op1_loc = locations->InAt(0);
+ Location op2_loc = locations->InAt(1);
+
+ // Shortcut for same input locations.
+ if (op1_loc.Equals(op2_loc)) {
+ // Can return immediately, as op1_loc == out_loc.
+ // Note: if we ever support separate registers, e.g., output into memory, we need to check for
+ // a copy here.
+ DCHECK(locations->Out().Equals(op1_loc));
+ return;
+ }
+
+ CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+ CpuRegister op2 = op2_loc.AsRegister<CpuRegister>();
+
+ // (out := op1)
+ // out <=? op2
+ // if out is min jmp done
+ // out := op2
+ // done:
+
+ if (is_long) {
+ __ cmpq(out, op2);
+ } else {
+ __ cmpl(out, op2);
+ }
+
+ __ cmov(is_min ? Condition::kGreater : Condition::kLess, out, op2, is_long);
+}
+
+static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathMinIntInt(HInvoke* invoke) {
+ CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathMinIntInt(HInvoke* invoke) {
+ GenMinMax(invoke->GetLocations(), true, false, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathMinLongLong(HInvoke* invoke) {
+ CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathMinLongLong(HInvoke* invoke) {
+ GenMinMax(invoke->GetLocations(), true, true, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathMaxIntInt(HInvoke* invoke) {
+ CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathMaxIntInt(HInvoke* invoke) {
+ GenMinMax(invoke->GetLocations(), false, false, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathMaxLongLong(HInvoke* invoke) {
+ CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathMaxLongLong(HInvoke* invoke) {
+ GenMinMax(invoke->GetLocations(), false, true, GetAssembler());
+}
+
+static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathSqrt(HInvoke* invoke) {
+ CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathSqrt(HInvoke* invoke) {
+ LocationSummary* locations = invoke->GetLocations();
+ XmmRegister in = locations->InAt(0).AsFpuRegister<XmmRegister>();
+ XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
+
+ GetAssembler()->sqrtsd(out, in);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitStringCharAt(HInvoke* invoke) {
+ // The inputs plus one temp.
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kCallOnSlowPath,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitStringCharAt(HInvoke* invoke) {
+ LocationSummary* locations = invoke->GetLocations();
+
+ // Location of reference to data array
+ const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
+ // Location of count
+ const int32_t count_offset = mirror::String::CountOffset().Int32Value();
+ // Starting offset within data array
+ const int32_t offset_offset = mirror::String::OffsetOffset().Int32Value();
+ // Start of char data with array_
+ const int32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value();
+
+ CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>();
+ CpuRegister idx = locations->InAt(1).AsRegister<CpuRegister>();
+ CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+ Location temp_loc = locations->GetTemp(0);
+ CpuRegister temp = temp_loc.AsRegister<CpuRegister>();
+
+ // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
+ // the cost.
+ // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
+ // we will not optimize the code for constants (which would save a register).
+
+ SlowPathCodeX86_64* slow_path = new (GetAllocator()) IntrinsicSlowPathX86_64(invoke);
+ codegen_->AddSlowPath(slow_path);
+
+ X86_64Assembler* assembler = GetAssembler();
+
+ __ cmpl(idx, Address(obj, count_offset));
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
+ __ j(kAboveEqual, slow_path->GetEntryLabel());
+
+ // Get the actual element.
+ __ movl(temp, idx); // temp := idx.
+ __ addl(temp, Address(obj, offset_offset)); // temp := offset + idx.
+ __ movl(out, Address(obj, value_offset)); // obj := obj.array.
+ // out = out[2*temp].
+ __ movzxw(out, Address(out, temp, ScaleFactor::TIMES_2, data_offset));
+
+ __ Bind(slow_path->GetExitLabel());
+}
+
+static void GenPeek(LocationSummary* locations, Primitive::Type size, X86_64Assembler* assembler) {
+ CpuRegister address = locations->InAt(0).AsRegister<CpuRegister>();
+ CpuRegister out = locations->Out().AsRegister<CpuRegister>(); // == address, here for clarity.
+ // x86 allows unaligned access. We do not have to check the input or use specific instructions
+ // to avoid a SIGBUS.
+ switch (size) {
+ case Primitive::kPrimByte:
+ __ movsxb(out, Address(address, 0));
+ break;
+ case Primitive::kPrimShort:
+ __ movsxw(out, Address(address, 0));
+ break;
+ case Primitive::kPrimInt:
+ __ movl(out, Address(address, 0));
+ break;
+ case Primitive::kPrimLong:
+ __ movq(out, Address(address, 0));
+ break;
+ default:
+ LOG(FATAL) << "Type not recognized for peek: " << size;
+ UNREACHABLE();
+ }
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMemoryPeekByte(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMemoryPeekByte(HInvoke* invoke) {
+ GenPeek(invoke->GetLocations(), Primitive::kPrimByte, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMemoryPeekIntNative(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMemoryPeekIntNative(HInvoke* invoke) {
+ GenPeek(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMemoryPeekLongNative(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMemoryPeekLongNative(HInvoke* invoke) {
+ GenPeek(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMemoryPeekShortNative(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMemoryPeekShortNative(HInvoke* invoke) {
+ GenPeek(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler());
+}
+
+static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+}
+
+static void GenPoke(LocationSummary* locations, Primitive::Type size, X86_64Assembler* assembler) {
+ CpuRegister address = locations->InAt(0).AsRegister<CpuRegister>();
+ CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
+ // x86 allows unaligned access. We do not have to check the input or use specific instructions
+ // to avoid a SIGBUS.
+ switch (size) {
+ case Primitive::kPrimByte:
+ __ movb(Address(address, 0), value);
+ break;
+ case Primitive::kPrimShort:
+ __ movw(Address(address, 0), value);
+ break;
+ case Primitive::kPrimInt:
+ __ movl(Address(address, 0), value);
+ break;
+ case Primitive::kPrimLong:
+ __ movq(Address(address, 0), value);
+ break;
+ default:
+ LOG(FATAL) << "Type not recognized for poke: " << size;
+ UNREACHABLE();
+ }
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMemoryPokeByte(HInvoke* invoke) {
+ CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMemoryPokeByte(HInvoke* invoke) {
+ GenPoke(invoke->GetLocations(), Primitive::kPrimByte, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMemoryPokeIntNative(HInvoke* invoke) {
+ CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMemoryPokeIntNative(HInvoke* invoke) {
+ GenPoke(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMemoryPokeLongNative(HInvoke* invoke) {
+ CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMemoryPokeLongNative(HInvoke* invoke) {
+ GenPoke(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMemoryPokeShortNative(HInvoke* invoke) {
+ CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMemoryPokeShortNative(HInvoke* invoke) {
+ GenPoke(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitThreadCurrentThread(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitThreadCurrentThread(HInvoke* invoke) {
+ CpuRegister out = invoke->GetLocations()->Out().AsRegister<CpuRegister>();
+ GetAssembler()->gs()->movl(out, Address::Absolute(Thread::PeerOffset<kX86_64WordSize>(), true));
+}
+
+static void GenUnsafeGet(LocationSummary* locations, Primitive::Type type,
+ bool is_volatile ATTRIBUTE_UNUSED, X86_64Assembler* assembler) {
+ CpuRegister base = locations->InAt(1).AsRegister<CpuRegister>();
+ CpuRegister offset = locations->InAt(2).AsRegister<CpuRegister>();
+ CpuRegister trg = locations->Out().AsRegister<CpuRegister>();
+
+ switch (type) {
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot:
+ __ movl(trg, Address(base, offset, ScaleFactor::TIMES_1, 0));
+ break;
+
+ case Primitive::kPrimLong:
+ __ movq(trg, Address(base, offset, ScaleFactor::TIMES_1, 0));
+ break;
+
+ default:
+ LOG(FATAL) << "Unsupported op size " << type;
+ UNREACHABLE();
+ }
+}
+
+static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitUnsafeGet(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetVolatile(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetLong(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetObject(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+
+
+void IntrinsicCodeGeneratorX86_64::VisitUnsafeGet(HInvoke* invoke) {
+ GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimInt, false, GetAssembler());
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafeGetVolatile(HInvoke* invoke) {
+ GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimInt, true, GetAssembler());
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafeGetLong(HInvoke* invoke) {
+ GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimLong, false, GetAssembler());
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
+ GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimLong, true, GetAssembler());
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafeGetObject(HInvoke* invoke) {
+ GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimNot, false, GetAssembler());
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
+ GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimNot, true, GetAssembler());
+}
+
+
+static void CreateIntIntIntIntToVoidPlusTempsLocations(ArenaAllocator* arena,
+ Primitive::Type type,
+ HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(3, Location::RequiresRegister());
+ if (type == Primitive::kPrimNot) {
+ // Need temp registers for card-marking.
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ }
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePut(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimInt, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePutOrdered(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimInt, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePutVolatile(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimInt, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePutObject(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimNot, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimNot, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimNot, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePutLong(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimLong, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimLong, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimLong, invoke);
+}
+
+// We don't care for ordered: it requires an AnyStore barrier, which is already given by the x86
+// memory model.
+static void GenUnsafePut(LocationSummary* locations, Primitive::Type type, bool is_volatile,
+ CodeGeneratorX86_64* codegen) {
+ X86_64Assembler* assembler = reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler());
+ CpuRegister base = locations->InAt(1).AsRegister<CpuRegister>();
+ CpuRegister offset = locations->InAt(2).AsRegister<CpuRegister>();
+ CpuRegister value = locations->InAt(3).AsRegister<CpuRegister>();
+
+ if (type == Primitive::kPrimLong) {
+ __ movq(Address(base, offset, ScaleFactor::TIMES_1, 0), value);
+ } else {
+ __ movl(Address(base, offset, ScaleFactor::TIMES_1, 0), value);
+ }
+
+ if (is_volatile) {
+ __ mfence();
+ }
+
+ if (type == Primitive::kPrimNot) {
+ codegen->MarkGCCard(locations->GetTemp(0).AsRegister<CpuRegister>(),
+ locations->GetTemp(1).AsRegister<CpuRegister>(),
+ base,
+ value);
+ }
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePut(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, codegen_);
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePutOrdered(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, codegen_);
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePutVolatile(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, true, codegen_);
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePutObject(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, codegen_);
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, codegen_);
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, true, codegen_);
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePutLong(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, codegen_);
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, codegen_);
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, codegen_);
+}
+
+// Unimplemented intrinsics.
+
+#define UNIMPLEMENTED_INTRINSIC(Name) \
+void IntrinsicLocationsBuilderX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
+} \
+void IntrinsicCodeGeneratorX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
+}
+
+UNIMPLEMENTED_INTRINSIC(IntegerReverse)
+UNIMPLEMENTED_INTRINSIC(LongReverse)
+UNIMPLEMENTED_INTRINSIC(MathFloor)
+UNIMPLEMENTED_INTRINSIC(MathCeil)
+UNIMPLEMENTED_INTRINSIC(MathRint)
+UNIMPLEMENTED_INTRINSIC(MathRoundDouble)
+UNIMPLEMENTED_INTRINSIC(MathRoundFloat)
+UNIMPLEMENTED_INTRINSIC(StringIsEmpty) // Might not want to do these two anyways, inlining should
+UNIMPLEMENTED_INTRINSIC(StringLength) // be good enough here.
+UNIMPLEMENTED_INTRINSIC(StringCompareTo)
+UNIMPLEMENTED_INTRINSIC(StringIndexOf)
+UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter)
+UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
+UNIMPLEMENTED_INTRINSIC(UnsafeCASInt)
+UNIMPLEMENTED_INTRINSIC(UnsafeCASLong)
+UNIMPLEMENTED_INTRINSIC(UnsafeCASObject)
+UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
+
+} // namespace x86_64
+} // namespace art
diff --git a/compiler/optimizing/intrinsics_x86_64.h b/compiler/optimizing/intrinsics_x86_64.h
new file mode 100644
index 0000000..dfae7fa
--- /dev/null
+++ b/compiler/optimizing/intrinsics_x86_64.h
@@ -0,0 +1,83 @@
+/*
+ * 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_INTRINSICS_X86_64_H_
+#define ART_COMPILER_OPTIMIZING_INTRINSICS_X86_64_H_
+
+#include "intrinsics.h"
+
+namespace art {
+
+class ArenaAllocator;
+class HInvokeStaticOrDirect;
+class HInvokeVirtual;
+
+namespace x86_64 {
+
+class CodeGeneratorX86_64;
+class X86_64Assembler;
+
+class IntrinsicLocationsBuilderX86_64 FINAL : public IntrinsicVisitor {
+ public:
+ explicit IntrinsicLocationsBuilderX86_64(ArenaAllocator* arena) : arena_(arena) {}
+
+ // Define visitor methods.
+
+#define OPTIMIZING_INTRINSICS(Name, IsStatic) \
+ void Visit ## Name(HInvoke* invoke) OVERRIDE;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+ // Check whether an invoke is an intrinsic, and if so, create a location summary. Returns whether
+ // a corresponding LocationSummary with the intrinsified_ flag set was generated and attached to
+ // the invoke.
+ bool TryDispatch(HInvoke* invoke);
+
+ private:
+ ArenaAllocator* arena_;
+
+ DISALLOW_COPY_AND_ASSIGN(IntrinsicLocationsBuilderX86_64);
+};
+
+class IntrinsicCodeGeneratorX86_64 FINAL : public IntrinsicVisitor {
+ public:
+ explicit IntrinsicCodeGeneratorX86_64(CodeGeneratorX86_64* codegen) : codegen_(codegen) {}
+
+ // Define visitor methods.
+
+#define OPTIMIZING_INTRINSICS(Name, IsStatic) \
+ void Visit ## Name(HInvoke* invoke) OVERRIDE;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+ private:
+ X86_64Assembler* GetAssembler();
+
+ ArenaAllocator* GetAllocator();
+
+ CodeGeneratorX86_64* codegen_;
+
+ DISALLOW_COPY_AND_ASSIGN(IntrinsicCodeGeneratorX86_64);
+};
+
+} // namespace x86_64
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_INTRINSICS_X86_64_H_
diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc
new file mode 100644
index 0000000..10f24d8
--- /dev/null
+++ b/compiler/optimizing/licm.cc
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+
+#include "licm.h"
+#include "side_effects_analysis.h"
+
+namespace art {
+
+static bool IsPhiOf(HInstruction* instruction, HBasicBlock* block) {
+ return instruction->IsPhi() && instruction->GetBlock() == block;
+}
+
+/**
+ * Returns whether `instruction` has all its inputs and environment defined
+ * before the loop it is in.
+ */
+static bool InputsAreDefinedBeforeLoop(HInstruction* instruction) {
+ DCHECK(instruction->IsInLoop());
+ HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
+ for (HInputIterator it(instruction); !it.Done(); it.Advance()) {
+ HLoopInformation* input_loop = it.Current()->GetBlock()->GetLoopInformation();
+ // We only need to check whether the input is defined in the loop. If it is not
+ // it is defined before the loop.
+ if (input_loop != nullptr && input_loop->IsIn(*info)) {
+ return false;
+ }
+ }
+
+ if (instruction->HasEnvironment()) {
+ HEnvironment* environment = instruction->GetEnvironment();
+ for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+ HInstruction* input = environment->GetInstructionAt(i);
+ if (input != nullptr) {
+ HLoopInformation* input_loop = input->GetBlock()->GetLoopInformation();
+ if (input_loop != nullptr && input_loop->IsIn(*info)) {
+ // We can move an instruction that takes a loop header phi in the environment:
+ // we will just replace that phi with its first input later in `UpdateLoopPhisIn`.
+ bool is_loop_header_phi = IsPhiOf(input, info->GetHeader());
+ if (!is_loop_header_phi) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
+
+/**
+ * If `environment` has a loop header phi, we replace it with its first input.
+ */
+static void UpdateLoopPhisIn(HEnvironment* environment, HLoopInformation* info) {
+ for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+ HInstruction* input = environment->GetInstructionAt(i);
+ if (input != nullptr && IsPhiOf(input, info->GetHeader())) {
+ HUseListNode<HEnvironment*>* env_use = environment->GetInstructionEnvUseAt(i);
+ input->RemoveEnvironmentUser(env_use);
+ HInstruction* incoming = input->InputAt(0);
+ environment->SetRawEnvAt(i, incoming);
+ incoming->AddEnvUseAt(environment, i);
+ }
+ }
+}
+
+void LICM::Run() {
+ DCHECK(side_effects_.HasRun());
+ // Only used during debug.
+ ArenaBitVector visited(graph_->GetArena(), graph_->GetBlocks().Size(), false);
+
+ // Post order visit to visit inner loops before outer loops.
+ for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ if (!block->IsLoopHeader()) {
+ // Only visit the loop when we reach the header.
+ continue;
+ }
+
+ HLoopInformation* loop_info = block->GetLoopInformation();
+ SideEffects loop_effects = side_effects_.GetLoopEffects(block);
+ HBasicBlock* pre_header = loop_info->GetPreHeader();
+
+ for (HBlocksInLoopIterator it_loop(*loop_info); !it_loop.Done(); it_loop.Advance()) {
+ HBasicBlock* inner = it_loop.Current();
+ DCHECK(inner->IsInLoop());
+ if (inner->GetLoopInformation() != loop_info) {
+ // Thanks to post order visit, inner loops were already visited.
+ DCHECK(visited.IsBitSet(inner->GetBlockId()));
+ continue;
+ }
+ visited.SetBit(inner->GetBlockId());
+
+ // We can move an instruction that can throw only if it is the first
+ // throwing instruction in the loop. Note that the first potentially
+ // throwing instruction encountered that is not hoisted stops this
+ // optimization. Non-throwing instruction can still be hoisted.
+ bool found_first_non_hoisted_throwing_instruction_in_loop = !inner->IsLoopHeader();
+ for (HInstructionIterator inst_it(inner->GetInstructions());
+ !inst_it.Done();
+ inst_it.Advance()) {
+ HInstruction* instruction = inst_it.Current();
+ if (instruction->CanBeMoved()
+ && (!instruction->CanThrow() || !found_first_non_hoisted_throwing_instruction_in_loop)
+ && !instruction->GetSideEffects().DependsOn(loop_effects)
+ && InputsAreDefinedBeforeLoop(instruction)) {
+ // We need to update the environment if the instruction has a loop header
+ // phi in it.
+ if (instruction->NeedsEnvironment()) {
+ UpdateLoopPhisIn(instruction->GetEnvironment(), loop_info);
+ }
+ instruction->MoveBefore(pre_header->GetLastInstruction());
+ } else if (instruction->CanThrow()) {
+ // If `instruction` can throw, we cannot move further instructions
+ // that can throw as well.
+ found_first_non_hoisted_throwing_instruction_in_loop = true;
+ }
+ }
+ }
+ }
+}
+
+} // namespace art
diff --git a/compiler/optimizing/licm.h b/compiler/optimizing/licm.h
new file mode 100644
index 0000000..4812394
--- /dev/null
+++ b/compiler/optimizing/licm.h
@@ -0,0 +1,42 @@
+/*
+ * 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_LICM_H_
+#define ART_COMPILER_OPTIMIZING_LICM_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+class SideEffectsAnalysis;
+
+class LICM : public HOptimization {
+ public:
+ LICM(HGraph* graph, const SideEffectsAnalysis& side_effects)
+ : HOptimization(graph, true, kLoopInvariantCodeMotionPassName), side_effects_(side_effects) {}
+
+ void Run() OVERRIDE;
+
+ private:
+ const SideEffectsAnalysis& side_effects_;
+
+ DISALLOW_COPY_AND_ASSIGN(LICM);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_LICM_H_
diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc
index 28ca5e8..eb27965 100644
--- a/compiler/optimizing/linearize_test.cc
+++ b/compiler/optimizing/linearize_test.cc
@@ -22,6 +22,7 @@
#include "code_generator_x86.h"
#include "dex_file.h"
#include "dex_instruction.h"
+#include "driver/compiler_options.h"
#include "graph_visualizer.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
@@ -37,16 +38,15 @@
static void TestCode(const uint16_t* data, const int* expected_order, size_t number_of_blocks) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraphBuilder builder(&allocator);
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+ HGraphBuilder builder(graph);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- HGraph* graph = builder.BuildGraph(*item);
- ASSERT_NE(graph, nullptr);
+ bool graph_built = builder.BuildGraph(*item);
+ ASSERT_TRUE(graph_built);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
- graph->AnalyzeNaturalLoops();
+ graph->TryBuildingSsa();
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
diff --git a/compiler/optimizing/live_interval_test.cc b/compiler/optimizing/live_interval_test.cc
index 3e4b83b..ac8759c 100644
--- a/compiler/optimizing/live_interval_test.cc
+++ b/compiler/optimizing/live_interval_test.cc
@@ -278,4 +278,55 @@
}
}
+TEST(LiveInterval, AddLoopRange) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ {
+ // Test when only used in a loop.
+ static constexpr size_t ranges[][2] = {{0, 4}};
+ LiveInterval* interval = BuildInterval(ranges, arraysize(ranges), &allocator);
+ interval->AddLoopRange(0, 8);
+ LiveRange* range = interval->GetFirstRange();
+ ASSERT_TRUE(range->GetNext() == nullptr);
+ ASSERT_EQ(range->GetStart(), 0u);
+ ASSERT_EQ(range->GetEnd(), 8u);
+ }
+
+ {
+ // Test when only used in a loop.
+ static constexpr size_t ranges[][2] = {{2, 4}};
+ LiveInterval* interval = BuildInterval(ranges, arraysize(ranges), &allocator);
+ interval->AddLoopRange(0, 8);
+ LiveRange* range = interval->GetFirstRange();
+ ASSERT_TRUE(range->GetNext() == nullptr);
+ ASSERT_EQ(range->GetStart(), 0u);
+ ASSERT_EQ(range->GetEnd(), 8u);
+ }
+
+ {
+ // Test when used just after the loop.
+ static constexpr size_t ranges[][2] = {{2, 4}, {8, 10}};
+ LiveInterval* interval = BuildInterval(ranges, arraysize(ranges), &allocator);
+ interval->AddLoopRange(0, 8);
+ LiveRange* range = interval->GetFirstRange();
+ ASSERT_TRUE(range->GetNext() == nullptr);
+ ASSERT_EQ(range->GetStart(), 0u);
+ ASSERT_EQ(range->GetEnd(), 10u);
+ }
+
+ {
+ // Test when use after the loop is after a lifetime hole.
+ static constexpr size_t ranges[][2] = {{2, 4}, {10, 12}};
+ LiveInterval* interval = BuildInterval(ranges, arraysize(ranges), &allocator);
+ interval->AddLoopRange(0, 8);
+ LiveRange* range = interval->GetFirstRange();
+ ASSERT_EQ(range->GetStart(), 0u);
+ ASSERT_EQ(range->GetEnd(), 8u);
+ range = range->GetNext();
+ ASSERT_EQ(range->GetStart(), 10u);
+ ASSERT_EQ(range->GetEnd(), 12u);
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index 5c7e6f0..0558b85 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -19,6 +19,7 @@
#include "code_generator_x86.h"
#include "dex_file.h"
#include "dex_instruction.h"
+#include "driver/compiler_options.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "prepare_for_register_allocation.h"
@@ -30,15 +31,14 @@
namespace art {
static HGraph* BuildGraph(const uint16_t* data, ArenaAllocator* allocator) {
- HGraphBuilder builder(allocator);
+ HGraph* graph = new (allocator) HGraph(allocator);
+ HGraphBuilder builder(graph);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- HGraph* graph = builder.BuildGraph(*item);
+ builder.BuildGraph(*item);
// Suspend checks implementation may change in the future, and this test relies
// on how instructions are ordered.
RemoveSuspendChecks(graph);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
- graph->AnalyzeNaturalLoops();
+ graph->TryBuildingSsa();
// `Inline` conditions into ifs.
PrepareForRegisterAllocation(graph).Run();
return graph;
@@ -65,7 +65,7 @@
ArenaAllocator allocator(&pool);
HGraph* graph = BuildGraph(data, &allocator);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -111,7 +111,7 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = BuildGraph(data, &allocator);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -160,7 +160,7 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = BuildGraph(data, &allocator);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -237,7 +237,7 @@
ArenaAllocator allocator(&pool);
HGraph* graph = BuildGraph(data, &allocator);
RemoveSuspendChecks(graph);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -315,7 +315,7 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = BuildGraph(data, &allocator);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -391,7 +391,7 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = BuildGraph(data, &allocator);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -433,7 +433,7 @@
ASSERT_TRUE(range->GetNext() == nullptr);
HPhi* phi = liveness.GetInstructionFromSsaIndex(4)->AsPhi();
- ASSERT_EQ(phi->NumberOfUses(), 1u);
+ ASSERT_TRUE(phi->GetUses().HasOnlyOneUse());
interval = phi->GetLiveInterval();
range = interval->GetFirstRange();
ASSERT_EQ(26u, range->GetStart());
diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc
index 4b69e57..c9be570 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -19,6 +19,7 @@
#include "code_generator_x86.h"
#include "dex_file.h"
#include "dex_instruction.h"
+#include "driver/compiler_options.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "prepare_for_register_allocation.h"
@@ -44,16 +45,15 @@
static void TestCode(const uint16_t* data, const char* expected) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraphBuilder builder(&allocator);
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+ HGraphBuilder builder(graph);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- HGraph* graph = builder.BuildGraph(*item);
- ASSERT_NE(graph, nullptr);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
- graph->AnalyzeNaturalLoops();
+ bool graph_built = builder.BuildGraph(*item);
+ ASSERT_TRUE(graph_built);
+ graph->TryBuildingSsa();
// `Inline` conditions into ifs.
PrepareForRegisterAllocation(graph).Run();
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc
index ed5e260..4ac1fe8 100644
--- a/compiler/optimizing/locations.cc
+++ b/compiler/optimizing/locations.cc
@@ -20,16 +20,19 @@
namespace art {
-LocationSummary::LocationSummary(HInstruction* instruction, CallKind call_kind)
+LocationSummary::LocationSummary(HInstruction* instruction,
+ CallKind call_kind,
+ bool intrinsified)
: inputs_(instruction->GetBlock()->GetGraph()->GetArena(), instruction->InputCount()),
temps_(instruction->GetBlock()->GetGraph()->GetArena(), 0),
environment_(instruction->GetBlock()->GetGraph()->GetArena(),
instruction->EnvironmentSize()),
- output_overlaps_(true),
+ output_overlaps_(Location::kOutputOverlap),
call_kind_(call_kind),
stack_mask_(nullptr),
register_mask_(0),
- live_registers_() {
+ live_registers_(),
+ intrinsified_(intrinsified) {
inputs_.SetSize(instruction->InputCount());
for (size_t i = 0; i < instruction->InputCount(); ++i) {
inputs_.Put(i, Location());
@@ -61,6 +64,13 @@
std::ostream& operator<<(std::ostream& os, const Location& location) {
os << location.DebugString();
+ if (location.IsRegister() || location.IsFpuRegister()) {
+ os << location.reg();
+ } else if (location.IsPair()) {
+ os << location.low() << ":" << location.high();
+ } else if (location.IsStackSlot() || location.IsDoubleStackSlot()) {
+ os << location.GetStackIndex();
+ }
return os;
}
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index 1ff26d9..9ce8d35 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -37,7 +37,10 @@
*/
class Location : public ValueObject {
public:
- static constexpr bool kNoOutputOverlap = false;
+ enum OutputOverlap {
+ kOutputOverlap,
+ kNoOutputOverlap
+ };
enum Kind {
kInvalid = 0,
@@ -59,17 +62,11 @@
// We do not use the value 9 because it conflicts with kLocationConstantMask.
kDoNotUse9 = 9,
- // On 32bits architectures, quick can pass a long where the
- // low bits are in the last parameter register, and the high
- // bits are in a stack slot. The kQuickParameter kind is for
- // handling this special case.
- kQuickParameter = 10,
-
// Unallocated location represents a location that is not fixed and can be
// allocated by a register allocator. Each unallocated location has
// a policy that specifies what kind of location is suitable. Payload
// contains register allocation policy.
- kUnallocated = 11,
+ kUnallocated = 10,
};
Location() : value_(kInvalid) {
@@ -79,7 +76,6 @@
static_assert((kStackSlot & kLocationConstantMask) != kConstant, "TagError");
static_assert((kDoubleStackSlot & kLocationConstantMask) != kConstant, "TagError");
static_assert((kRegister & kLocationConstantMask) != kConstant, "TagError");
- static_assert((kQuickParameter & kLocationConstantMask) != kConstant, "TagError");
static_assert((kFpuRegister & kLocationConstantMask) != kConstant, "TagError");
static_assert((kRegisterPair & kLocationConstantMask) != kConstant, "TagError");
static_assert((kFpuRegisterPair & kLocationConstantMask) != kConstant, "TagError");
@@ -160,6 +156,16 @@
return GetPayload();
}
+ int low() const {
+ DCHECK(IsPair());
+ return GetPayload() >> 16;
+ }
+
+ int high() const {
+ DCHECK(IsPair());
+ return GetPayload() & 0xFFFF;
+ }
+
template <typename T>
T AsRegister() const {
DCHECK(IsRegister());
@@ -175,25 +181,41 @@
template <typename T>
T AsRegisterPairLow() const {
DCHECK(IsRegisterPair());
- return static_cast<T>(GetPayload() >> 16);
+ return static_cast<T>(low());
}
template <typename T>
T AsRegisterPairHigh() const {
DCHECK(IsRegisterPair());
- return static_cast<T>(GetPayload() & 0xFFFF);
+ return static_cast<T>(high());
}
template <typename T>
T AsFpuRegisterPairLow() const {
DCHECK(IsFpuRegisterPair());
- return static_cast<T>(GetPayload() >> 16);
+ return static_cast<T>(low());
}
template <typename T>
T AsFpuRegisterPairHigh() const {
DCHECK(IsFpuRegisterPair());
- return static_cast<T>(GetPayload() & 0xFFFF);
+ return static_cast<T>(high());
+ }
+
+ bool IsPair() const {
+ return IsRegisterPair() || IsFpuRegisterPair();
+ }
+
+ Location ToLow() const {
+ return IsRegisterPair()
+ ? Location::RegisterLocation(low())
+ : Location::FpuRegisterLocation(low());
+ }
+
+ Location ToHigh() const {
+ return IsRegisterPair()
+ ? Location::RegisterLocation(high())
+ : Location::FpuRegisterLocation(high());
}
static uintptr_t EncodeStackIndex(intptr_t stack_index) {
@@ -238,24 +260,6 @@
return GetPayload() - kStackIndexBias + word_size;
}
- static Location QuickParameter(uint16_t register_index, uint16_t stack_index) {
- return Location(kQuickParameter, register_index << 16 | stack_index);
- }
-
- uint32_t GetQuickParameterRegisterIndex() const {
- DCHECK(IsQuickParameter());
- return GetPayload() >> 16;
- }
-
- uint32_t GetQuickParameterStackIndex() const {
- DCHECK(IsQuickParameter());
- return GetPayload() & 0xFFFF;
- }
-
- bool IsQuickParameter() const {
- return GetKind() == kQuickParameter;
- }
-
Kind GetKind() const {
return IsConstant() ? kConstant : KindField::Decode(value_);
}
@@ -264,13 +268,26 @@
return value_ == other.value_;
}
+ bool Contains(Location other) const {
+ if (Equals(other)) {
+ return true;
+ } else if (IsFpuRegisterPair() && other.IsFpuRegister()) {
+ return other.reg() == low() || other.reg() == high();
+ } else if (IsRegisterPair() && other.IsRegister()) {
+ return other.reg() == low() || other.reg() == high();
+ } else if (IsDoubleStackSlot() && other.IsStackSlot()) {
+ return (GetStackIndex() == other.GetStackIndex())
+ || (GetStackIndex() + 4 == other.GetStackIndex());
+ }
+ return false;
+ }
+
const char* DebugString() const {
switch (GetKind()) {
case kInvalid: return "I";
case kRegister: return "R";
case kStackSlot: return "S";
case kDoubleStackSlot: return "DS";
- case kQuickParameter: return "Q";
case kUnallocated: return "U";
case kConstant: return "C";
case kFpuRegister: return "F";
@@ -402,6 +419,14 @@
return __builtin_popcount(core_registers_) + __builtin_popcount(floating_point_registers_);
}
+ uint32_t GetCoreRegisters() const {
+ return core_registers_;
+ }
+
+ uint32_t GetFloatingPointRegisters() const {
+ return floating_point_registers_;
+ }
+
private:
uint32_t core_registers_;
uint32_t floating_point_registers_;
@@ -409,6 +434,8 @@
DISALLOW_COPY_AND_ASSIGN(RegisterSet);
};
+static constexpr bool kIntrinsified = true;
+
/**
* The code generator computes LocationSummary for each instruction so that
* the instruction itself knows what code to generate: where to find the inputs
@@ -425,7 +452,9 @@
kCall
};
- LocationSummary(HInstruction* instruction, CallKind call_kind = kNoCall);
+ LocationSummary(HInstruction* instruction,
+ CallKind call_kind = kNoCall,
+ bool intrinsified = false);
void SetInAt(uint32_t at, Location location) {
DCHECK(inputs_.Get(at).IsUnallocated() || inputs_.Get(at).IsInvalid());
@@ -440,17 +469,18 @@
return inputs_.Size();
}
- void SetOut(Location location, bool overlaps = true) {
- DCHECK(output_.IsUnallocated() || output_.IsInvalid());
+ void SetOut(Location location, Location::OutputOverlap overlaps = Location::kOutputOverlap) {
+ DCHECK(output_.IsInvalid());
output_overlaps_ = overlaps;
output_ = location;
}
void UpdateOut(Location location) {
- // The only reason for updating an output is for parameters where
- // we only know the exact stack slot after doing full register
- // allocation.
- DCHECK(output_.IsStackSlot() || output_.IsDoubleStackSlot());
+ // There are two reasons for updating an output:
+ // 1) Parameters, where we only know the exact stack slot after
+ // doing full register allocation.
+ // 2) Unallocated location.
+ DCHECK(output_.IsStackSlot() || output_.IsDoubleStackSlot() || output_.IsUnallocated());
output_ = location;
}
@@ -498,6 +528,10 @@
register_mask_ |= (1 << reg_id);
}
+ uint32_t GetRegisterMask() const {
+ return register_mask_;
+ }
+
bool RegisterContainsObject(uint32_t reg_id) {
return RegisterSet::Contains(register_mask_, reg_id);
}
@@ -518,21 +552,27 @@
return live_registers_.GetNumberOfRegisters();
}
- bool InputOverlapsWithOutputOrTemp(uint32_t input_index, bool is_environment) const {
- if (is_environment) return true;
- if ((input_index == 0)
+ bool OutputUsesSameAs(uint32_t input_index) const {
+ return (input_index == 0)
&& output_.IsUnallocated()
- && (output_.GetPolicy() == Location::kSameAsFirstInput)) {
- return false;
- }
- if (inputs_.Get(input_index).IsRegister() || inputs_.Get(input_index).IsFpuRegister()) {
- return false;
- }
- return true;
+ && (output_.GetPolicy() == Location::kSameAsFirstInput);
}
- bool OutputOverlapsWithInputs() const {
- return output_overlaps_;
+ bool IsFixedInput(uint32_t input_index) const {
+ Location input = inputs_.Get(input_index);
+ return input.IsRegister()
+ || input.IsFpuRegister()
+ || input.IsPair()
+ || input.IsStackSlot()
+ || input.IsDoubleStackSlot();
+ }
+
+ bool OutputCanOverlapWithInputs() const {
+ return output_overlaps_ == Location::kOutputOverlap;
+ }
+
+ bool Intrinsified() const {
+ return intrinsified_;
}
private:
@@ -541,7 +581,7 @@
GrowableArray<Location> environment_;
// Whether the output overlaps with any of the inputs. If it overlaps, then it cannot
// share the same register as the inputs.
- bool output_overlaps_;
+ Location::OutputOverlap output_overlaps_;
Location output_;
const CallKind call_kind_;
@@ -554,6 +594,9 @@
// Registers that are in use at this position.
RegisterSet live_registers_;
+ // Whether these are locations for an intrinsified call.
+ const bool intrinsified_;
+
ART_FRIEND_TEST(RegisterAllocatorTest, ExpectedInRegisterHint);
ART_FRIEND_TEST(RegisterAllocatorTest, SameAsFirstInputHint);
DISALLOW_COPY_AND_ASSIGN(LocationSummary);
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index ba4dccf..cd36598 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -15,6 +15,7 @@
*/
#include "nodes.h"
+
#include "ssa_builder.h"
#include "utils/growable_array.h"
@@ -38,9 +39,11 @@
HEnvironment* environment = instruction->GetEnvironment();
if (environment != nullptr) {
for (size_t i = 0, e = environment->Size(); i < e; ++i) {
- HInstruction* vreg = environment->GetInstructionAt(i);
- if (vreg != nullptr) {
- vreg->RemoveEnvironmentUser(environment, i);
+ HUseListNode<HEnvironment*>* vreg_env_use = environment->GetInstructionEnvUseAt(i);
+ if (vreg_env_use != nullptr) {
+ HInstruction* vreg = environment->GetInstructionAt(i);
+ DCHECK(vreg != nullptr);
+ vreg->RemoveEnvironmentUser(vreg_env_use);
}
}
}
@@ -60,19 +63,22 @@
}
}
+void HGraph::RemoveBlock(HBasicBlock* block) const {
+ for (size_t j = 0; j < block->GetSuccessors().Size(); ++j) {
+ block->GetSuccessors().Get(j)->RemovePredecessor(block);
+ }
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ block->RemovePhi(it.Current()->AsPhi());
+ }
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ block->RemoveInstruction(it.Current());
+ }
+}
+
void HGraph::RemoveDeadBlocks(const ArenaBitVector& visited) const {
for (size_t i = 0; i < blocks_.Size(); ++i) {
if (!visited.IsBitSet(i)) {
- HBasicBlock* block = blocks_.Get(i);
- for (size_t j = 0; j < block->GetSuccessors().Size(); ++j) {
- block->GetSuccessors().Get(j)->RemovePredecessor(block);
- }
- for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
- block->RemovePhi(it.Current()->AsPhi());
- }
- for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
- block->RemoveInstruction(it.Current());
- }
+ RemoveBlock(blocks_.Get(i));
}
}
}
@@ -167,7 +173,7 @@
}
}
-void HGraph::TransformToSSA() {
+void HGraph::TransformToSsa() {
DCHECK(!reverse_post_order_.IsEmpty());
SsaBuilder ssa_builder(this);
ssa_builder.BuildSsa();
@@ -286,6 +292,10 @@
return true;
}
+void HLoopInformation::Add(HBasicBlock* block) {
+ blocks_.SetBit(block->GetBlockId());
+}
+
void HLoopInformation::PopulateRecursive(HBasicBlock* block) {
if (blocks_.IsBitSet(block->GetBlockId())) {
return;
@@ -421,8 +431,8 @@
HBasicBlock* block,
HInstruction* instruction) {
DCHECK_EQ(block, instruction->GetBlock());
- DCHECK(instruction->GetUses() == nullptr);
- DCHECK(instruction->GetEnvUses() == nullptr);
+ DCHECK(instruction->GetUses().IsEmpty());
+ DCHECK(instruction->GetEnvUses().IsEmpty());
instruction->SetBlock(nullptr);
instruction_list->RemoveInstruction(instruction);
@@ -437,31 +447,49 @@
Remove(&phis_, this, phi);
}
-template <typename T>
-static void RemoveFromUseList(T* user,
- size_t input_index,
- HUseListNode<T>** list) {
- HUseListNode<T>* previous = nullptr;
- HUseListNode<T>* current = *list;
- while (current != nullptr) {
- if (current->GetUser() == user && current->GetIndex() == input_index) {
- if (previous == NULL) {
- *list = current->GetTail();
- } else {
- previous->SetTail(current->GetTail());
- }
+void HEnvironment::CopyFrom(HEnvironment* env) {
+ for (size_t i = 0; i < env->Size(); i++) {
+ HInstruction* instruction = env->GetInstructionAt(i);
+ SetRawEnvAt(i, instruction);
+ if (instruction != nullptr) {
+ instruction->AddEnvUseAt(this, i);
}
- previous = current;
- current = current->GetTail();
}
}
+template <typename T>
+static void RemoveFromUseList(T user, size_t input_index, HUseList<T>* list) {
+ HUseListNode<T>* current;
+ for (HUseIterator<HInstruction*> use_it(*list); !use_it.Done(); use_it.Advance()) {
+ current = use_it.Current();
+ if (current->GetUser() == user && current->GetIndex() == input_index) {
+ list->Remove(current);
+ }
+ }
+}
+
+HInstruction* HInstruction::GetNextDisregardingMoves() const {
+ HInstruction* next = GetNext();
+ while (next != nullptr && next->IsParallelMove()) {
+ next = next->GetNext();
+ }
+ return next;
+}
+
+HInstruction* HInstruction::GetPreviousDisregardingMoves() const {
+ HInstruction* previous = GetPrevious();
+ while (previous != nullptr && previous->IsParallelMove()) {
+ previous = previous->GetPrevious();
+ }
+ return previous;
+}
+
void HInstruction::RemoveUser(HInstruction* user, size_t input_index) {
RemoveFromUseList(user, input_index, &uses_);
}
-void HInstruction::RemoveEnvironmentUser(HEnvironment* user, size_t input_index) {
- RemoveFromUseList(user, input_index, &env_uses_);
+void HInstruction::RemoveEnvironmentUser(HUseListNode<HEnvironment*>* use) {
+ env_uses_.Remove(use);
}
void HInstructionList::AddInstruction(HInstruction* instruction) {
@@ -553,24 +581,24 @@
void HInstruction::ReplaceWith(HInstruction* other) {
DCHECK(other != nullptr);
- for (HUseIterator<HInstruction> it(GetUses()); !it.Done(); it.Advance()) {
- HUseListNode<HInstruction>* current = it.Current();
+ for (HUseIterator<HInstruction*> it(GetUses()); !it.Done(); it.Advance()) {
+ HUseListNode<HInstruction*>* current = it.Current();
HInstruction* user = current->GetUser();
size_t input_index = current->GetIndex();
user->SetRawInputAt(input_index, other);
other->AddUseAt(user, input_index);
}
- for (HUseIterator<HEnvironment> it(GetEnvUses()); !it.Done(); it.Advance()) {
- HUseListNode<HEnvironment>* current = it.Current();
+ for (HUseIterator<HEnvironment*> it(GetEnvUses()); !it.Done(); it.Advance()) {
+ HUseListNode<HEnvironment*>* current = it.Current();
HEnvironment* user = current->GetUser();
size_t input_index = current->GetIndex();
user->SetRawEnvAt(input_index, other);
other->AddEnvUseAt(user, input_index);
}
- uses_ = nullptr;
- env_uses_ = nullptr;
+ uses_.Clear();
+ env_uses_.Clear();
}
void HInstruction::ReplaceInput(HInstruction* replacement, size_t index) {
@@ -643,17 +671,18 @@
} else if (GetLeft()->IsLongConstant() && GetRight()->IsLongConstant()) {
int64_t value = Evaluate(GetLeft()->AsLongConstant()->GetValue(),
GetRight()->AsLongConstant()->GetValue());
- return new(GetBlock()->GetGraph()->GetArena()) HLongConstant(value);
+ if (GetResultType() == Primitive::kPrimLong) {
+ return new(GetBlock()->GetGraph()->GetArena()) HLongConstant(value);
+ } else {
+ DCHECK_EQ(GetResultType(), Primitive::kPrimInt);
+ return new(GetBlock()->GetGraph()->GetArena()) HIntConstant(value);
+ }
}
return nullptr;
}
bool HCondition::IsBeforeWhenDisregardMoves(HIf* if_) const {
- HInstruction* previous = if_->GetPrevious();
- while (previous != nullptr && previous->IsParallelMove()) {
- previous = previous->GetPrevious();
- }
- return previous == this;
+ return this == if_->GetPreviousDisregardingMoves();
}
bool HInstruction::Equals(HInstruction* other) const {
@@ -682,4 +711,283 @@
return os;
}
+void HInstruction::MoveBefore(HInstruction* cursor) {
+ next_->previous_ = previous_;
+ if (previous_ != nullptr) {
+ previous_->next_ = next_;
+ }
+ if (block_->instructions_.first_instruction_ == this) {
+ block_->instructions_.first_instruction_ = next_;
+ }
+ DCHECK_NE(block_->instructions_.last_instruction_, this);
+
+ previous_ = cursor->previous_;
+ if (previous_ != nullptr) {
+ previous_->next_ = this;
+ }
+ next_ = cursor;
+ cursor->previous_ = this;
+ block_ = cursor->block_;
+
+ if (block_->instructions_.first_instruction_ == cursor) {
+ block_->instructions_.first_instruction_ = this;
+ }
+}
+
+HBasicBlock* HBasicBlock::SplitAfter(HInstruction* cursor) {
+ DCHECK(!cursor->IsControlFlow());
+ DCHECK_NE(instructions_.last_instruction_, cursor);
+ DCHECK_EQ(cursor->GetBlock(), this);
+
+ HBasicBlock* new_block = new (GetGraph()->GetArena()) HBasicBlock(GetGraph(), GetDexPc());
+ new_block->instructions_.first_instruction_ = cursor->GetNext();
+ new_block->instructions_.last_instruction_ = instructions_.last_instruction_;
+ cursor->next_->previous_ = nullptr;
+ cursor->next_ = nullptr;
+ instructions_.last_instruction_ = cursor;
+
+ new_block->instructions_.SetBlockOfInstructions(new_block);
+ for (size_t i = 0, e = GetSuccessors().Size(); i < e; ++i) {
+ HBasicBlock* successor = GetSuccessors().Get(i);
+ new_block->successors_.Add(successor);
+ successor->predecessors_.Put(successor->GetPredecessorIndexOf(this), new_block);
+ }
+ successors_.Reset();
+
+ for (size_t i = 0, e = GetDominatedBlocks().Size(); i < e; ++i) {
+ HBasicBlock* dominated = GetDominatedBlocks().Get(i);
+ dominated->dominator_ = new_block;
+ new_block->dominated_blocks_.Add(dominated);
+ }
+ dominated_blocks_.Reset();
+ return new_block;
+}
+
+void HInstructionList::SetBlockOfInstructions(HBasicBlock* block) const {
+ for (HInstruction* current = first_instruction_;
+ current != nullptr;
+ current = current->GetNext()) {
+ current->SetBlock(block);
+ }
+}
+
+void HInstructionList::AddAfter(HInstruction* cursor, const HInstructionList& instruction_list) {
+ DCHECK(Contains(cursor));
+ if (!instruction_list.IsEmpty()) {
+ if (cursor == last_instruction_) {
+ last_instruction_ = instruction_list.last_instruction_;
+ } else {
+ cursor->next_->previous_ = instruction_list.last_instruction_;
+ }
+ instruction_list.last_instruction_->next_ = cursor->next_;
+ cursor->next_ = instruction_list.first_instruction_;
+ instruction_list.first_instruction_->previous_ = cursor;
+ }
+}
+
+void HInstructionList::Add(const HInstructionList& instruction_list) {
+ DCHECK(!IsEmpty());
+ AddAfter(last_instruction_, instruction_list);
+}
+
+void HBasicBlock::MergeWith(HBasicBlock* other) {
+ DCHECK(successors_.IsEmpty()) << "Unimplemented block merge scenario";
+ DCHECK(dominated_blocks_.IsEmpty()) << "Unimplemented block merge scenario";
+ DCHECK(other->GetDominator()->IsEntryBlock() && other->GetGraph() != graph_)
+ << "Unimplemented block merge scenario";
+ DCHECK(other->GetPhis().IsEmpty());
+
+ successors_.Reset();
+ dominated_blocks_.Reset();
+ instructions_.Add(other->GetInstructions());
+ other->GetInstructions().SetBlockOfInstructions(this);
+
+ while (!other->GetSuccessors().IsEmpty()) {
+ HBasicBlock* successor = other->GetSuccessors().Get(0);
+ successor->ReplacePredecessor(other, this);
+ }
+
+ for (size_t i = 0, e = other->GetDominatedBlocks().Size(); i < e; ++i) {
+ HBasicBlock* dominated = other->GetDominatedBlocks().Get(i);
+ dominated_blocks_.Add(dominated);
+ dominated->SetDominator(this);
+ }
+ other->dominated_blocks_.Reset();
+ other->dominator_ = nullptr;
+ other->graph_ = nullptr;
+}
+
+void HBasicBlock::ReplaceWith(HBasicBlock* other) {
+ while (!GetPredecessors().IsEmpty()) {
+ HBasicBlock* predecessor = GetPredecessors().Get(0);
+ predecessor->ReplaceSuccessor(this, other);
+ }
+ while (!GetSuccessors().IsEmpty()) {
+ HBasicBlock* successor = GetSuccessors().Get(0);
+ successor->ReplacePredecessor(this, other);
+ }
+ for (size_t i = 0; i < dominated_blocks_.Size(); ++i) {
+ other->AddDominatedBlock(dominated_blocks_.Get(i));
+ }
+ GetDominator()->ReplaceDominatedBlock(this, other);
+ other->SetDominator(GetDominator());
+ dominator_ = nullptr;
+ graph_ = nullptr;
+}
+
+// Create space in `blocks` for adding `number_of_new_blocks` entries
+// starting at location `at`. Blocks after `at` are moved accordingly.
+static void MakeRoomFor(GrowableArray<HBasicBlock*>* blocks,
+ size_t number_of_new_blocks,
+ size_t at) {
+ size_t old_size = blocks->Size();
+ size_t new_size = old_size + number_of_new_blocks;
+ blocks->SetSize(new_size);
+ for (size_t i = old_size - 1, j = new_size - 1; i > at; --i, --j) {
+ blocks->Put(j, blocks->Get(i));
+ }
+}
+
+void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
+ // Walk over the entry block and:
+ // - Move constants from the entry block to the outer_graph's entry block,
+ // - Replace HParameterValue instructions with their real value.
+ // - Remove suspend checks, that hold an environment.
+ int parameter_index = 0;
+ for (HInstructionIterator it(entry_block_->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* current = it.Current();
+ if (current->IsConstant()) {
+ current->MoveBefore(outer_graph->GetEntryBlock()->GetLastInstruction());
+ } else if (current->IsParameterValue()) {
+ current->ReplaceWith(invoke->InputAt(parameter_index++));
+ } else {
+ DCHECK(current->IsGoto() || current->IsSuspendCheck());
+ entry_block_->RemoveInstruction(current);
+ }
+ }
+
+ if (GetBlocks().Size() == 3) {
+ // Simple case of an entry block, a body block, and an exit block.
+ // Put the body block's instruction into `invoke`'s block.
+ HBasicBlock* body = GetBlocks().Get(1);
+ DCHECK(GetBlocks().Get(0)->IsEntryBlock());
+ DCHECK(GetBlocks().Get(2)->IsExitBlock());
+ DCHECK(!body->IsExitBlock());
+ HInstruction* last = body->GetLastInstruction();
+
+ invoke->GetBlock()->instructions_.AddAfter(invoke, body->GetInstructions());
+ body->GetInstructions().SetBlockOfInstructions(invoke->GetBlock());
+
+ // Replace the invoke with the return value of the inlined graph.
+ if (last->IsReturn()) {
+ invoke->ReplaceWith(last->InputAt(0));
+ } else {
+ DCHECK(last->IsReturnVoid());
+ }
+
+ invoke->GetBlock()->RemoveInstruction(last);
+ } else {
+ // Need to inline multiple blocks. We split `invoke`'s block
+ // into two blocks, merge the first block of the inlined graph into
+ // the first half, and replace the exit block of the inlined graph
+ // with the second half.
+ ArenaAllocator* allocator = outer_graph->GetArena();
+ HBasicBlock* at = invoke->GetBlock();
+ HBasicBlock* to = at->SplitAfter(invoke);
+
+ HBasicBlock* first = entry_block_->GetSuccessors().Get(0);
+ DCHECK(!first->IsInLoop());
+ at->MergeWith(first);
+ exit_block_->ReplaceWith(to);
+
+ // Update all predecessors of the exit block (now the `to` block)
+ // to not `HReturn` but `HGoto` instead. Also collect the return
+ // values if any, and potentially make it a phi if there are multiple
+ // predecessors.
+ HInstruction* return_value = nullptr;
+ for (size_t i = 0, e = to->GetPredecessors().Size(); i < e; ++i) {
+ HBasicBlock* predecessor = to->GetPredecessors().Get(i);
+ HInstruction* last = predecessor->GetLastInstruction();
+ if (!last->IsReturnVoid()) {
+ if (return_value != nullptr) {
+ if (!return_value->IsPhi()) {
+ HPhi* phi = new (allocator) HPhi(allocator, kNoRegNumber, 0, invoke->GetType());
+ to->AddPhi(phi);
+ phi->AddInput(return_value);
+ return_value = phi;
+ }
+ return_value->AsPhi()->AddInput(last->InputAt(0));
+ } else {
+ return_value = last->InputAt(0);
+ }
+ }
+ predecessor->AddInstruction(new (allocator) HGoto());
+ predecessor->RemoveInstruction(last);
+ }
+
+ if (return_value != nullptr) {
+ invoke->ReplaceWith(return_value);
+ }
+
+ // Update the meta information surrounding blocks:
+ // (1) the graph they are now in,
+ // (2) the reverse post order of that graph,
+ // (3) the potential loop information they are now in.
+
+ // We don't add the entry block, the exit block, and the first block, which
+ // has been merged with `at`.
+ static constexpr int kNumberOfSkippedBlocksInCallee = 3;
+
+ // We add the `to` block.
+ static constexpr int kNumberOfNewBlocksInCaller = 1;
+ size_t blocks_added = (reverse_post_order_.Size() - kNumberOfSkippedBlocksInCallee)
+ + kNumberOfNewBlocksInCaller;
+
+ // Find the location of `at` in the outer graph's reverse post order. The new
+ // blocks will be added after it.
+ size_t index_of_at = 0;
+ while (outer_graph->reverse_post_order_.Get(index_of_at) != at) {
+ index_of_at++;
+ }
+ MakeRoomFor(&outer_graph->reverse_post_order_, blocks_added, index_of_at);
+
+ // Do a reverse post order of the blocks in the callee and do (1), (2),
+ // and (3) to the blocks that apply.
+ HLoopInformation* info = at->GetLoopInformation();
+ for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
+ HBasicBlock* current = it.Current();
+ if (current != exit_block_ && current != entry_block_ && current != first) {
+ DCHECK(!current->IsInLoop());
+ DCHECK(current->GetGraph() == this);
+ current->SetGraph(outer_graph);
+ outer_graph->AddBlock(current);
+ outer_graph->reverse_post_order_.Put(++index_of_at, current);
+ if (info != nullptr) {
+ info->Add(current);
+ current->SetLoopInformation(info);
+ }
+ }
+ }
+
+ // Do (1), (2), and (3) to `to`.
+ to->SetGraph(outer_graph);
+ outer_graph->AddBlock(to);
+ outer_graph->reverse_post_order_.Put(++index_of_at, to);
+ if (info != nullptr) {
+ info->Add(to);
+ to->SetLoopInformation(info);
+ if (info->IsBackEdge(at)) {
+ // Only `at` can become a back edge, as the inlined blocks
+ // are predecessors of `at`.
+ DCHECK_EQ(1u, info->NumberOfBackEdges());
+ info->ClearBackEdges();
+ info->AddBackEdge(to);
+ }
+ }
+ }
+
+ // Finally remove the invoke from the caller.
+ invoke->GetBlock()->RemoveInstruction(invoke);
+}
+
} // namespace art
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 3908a61..fd88e42 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -17,6 +17,8 @@
#ifndef ART_COMPILER_OPTIMIZING_NODES_H_
#define ART_COMPILER_OPTIMIZING_NODES_H_
+#include "entrypoints/quick/quick_entrypoints_enum.h"
+#include "invoke_type.h"
#include "locations.h"
#include "offsets.h"
#include "primitive.h"
@@ -30,6 +32,7 @@
class HEnvironment;
class HInstruction;
class HIntConstant;
+class HInvoke;
class HGraphVisitor;
class HPhi;
class HSuspendCheck;
@@ -70,11 +73,22 @@
bool FoundBefore(const HInstruction* instruction1,
const HInstruction* instruction2) const;
+ bool IsEmpty() const { return first_instruction_ == nullptr; }
+ void Clear() { first_instruction_ = last_instruction_ = nullptr; }
+
+ // Update the block of all instructions to be `block`.
+ void SetBlockOfInstructions(HBasicBlock* block) const;
+
+ void AddAfter(HInstruction* cursor, const HInstructionList& instruction_list);
+ void Add(const HInstructionList& instruction_list);
+
private:
HInstruction* first_instruction_;
HInstruction* last_instruction_;
friend class HBasicBlock;
+ friend class HGraph;
+ friend class HInstruction;
friend class HInstructionIterator;
friend class HBackwardInstructionIterator;
@@ -84,7 +98,7 @@
// Control-flow graph of a method. Contains a list of basic blocks.
class HGraph : public ArenaObject<kArenaAllocMisc> {
public:
- explicit HGraph(ArenaAllocator* arena)
+ HGraph(ArenaAllocator* arena, int start_instruction_id = 0)
: arena_(arena),
blocks_(arena, kDefaultNumberOfBlocks),
reverse_post_order_(arena, kDefaultNumberOfBlocks),
@@ -94,7 +108,7 @@
number_of_vregs_(0),
number_of_in_vregs_(0),
temporaries_vreg_slots_(0),
- current_instruction_id_(0) {}
+ current_instruction_id_(start_instruction_id) {}
ArenaAllocator* GetArena() const { return arena_; }
const GrowableArray<HBasicBlock*>& GetBlocks() const { return blocks_; }
@@ -108,8 +122,16 @@
void AddBlock(HBasicBlock* block);
+ // Try building the SSA form of this graph, with dominance computation and loop
+ // recognition. Returns whether it was successful in doing all these steps.
+ bool TryBuildingSsa() {
+ BuildDominatorTree();
+ TransformToSsa();
+ return AnalyzeNaturalLoops();
+ }
+
void BuildDominatorTree();
- void TransformToSSA();
+ void TransformToSsa();
void SimplifyCFG();
// Analyze all natural loops in this graph. Returns false if one
@@ -117,19 +139,31 @@
// back edge.
bool AnalyzeNaturalLoops() const;
+ // Inline this graph in `outer_graph`, replacing the given `invoke` instruction.
+ void InlineInto(HGraph* outer_graph, HInvoke* invoke);
+
void SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor);
void SimplifyLoop(HBasicBlock* header);
- int GetNextInstructionId() {
+ int32_t GetNextInstructionId() {
+ DCHECK_NE(current_instruction_id_, INT32_MAX);
return current_instruction_id_++;
}
+ int32_t GetCurrentInstructionId() const {
+ return current_instruction_id_;
+ }
+
+ void SetCurrentInstructionId(int32_t id) {
+ current_instruction_id_ = id;
+ }
+
uint16_t GetMaximumNumberOfOutVRegs() const {
return maximum_number_of_out_vregs_;
}
- void UpdateMaximumNumberOfOutVRegs(uint16_t new_value) {
- maximum_number_of_out_vregs_ = std::max(new_value, maximum_number_of_out_vregs_);
+ void SetMaximumNumberOfOutVRegs(uint16_t new_value) {
+ maximum_number_of_out_vregs_ = new_value;
}
void UpdateTemporariesVRegSlots(size_t slots) {
@@ -152,10 +186,6 @@
number_of_in_vregs_ = value;
}
- uint16_t GetNumberOfInVRegs() const {
- return number_of_in_vregs_;
- }
-
uint16_t GetNumberOfLocalVRegs() const {
return number_of_vregs_ - number_of_in_vregs_;
}
@@ -175,6 +205,7 @@
ArenaBitVector* visiting);
void RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const;
void RemoveDeadBlocks(const ArenaBitVector& visited) const;
+ void RemoveBlock(HBasicBlock* block) const;
ArenaAllocator* const arena_;
@@ -200,8 +231,9 @@
size_t temporaries_vreg_slots_;
// The current id to assign to a newly added instruction. See HInstruction.id_.
- int current_instruction_id_;
+ int32_t current_instruction_id_;
+ ART_FRIEND_TEST(GraphTest, IfSuccessorSimpleJoinBlock1);
DISALLOW_COPY_AND_ASSIGN(HGraph);
};
@@ -218,6 +250,10 @@
return header_;
}
+ void SetHeader(HBasicBlock* block) {
+ header_ = block;
+ }
+
HSuspendCheck* GetSuspendCheck() const { return suspend_check_; }
void SetSuspendCheck(HSuspendCheck* check) { suspend_check_ = check; }
bool HasSuspendCheck() const { return suspend_check_ != nullptr; }
@@ -265,6 +301,8 @@
const ArenaBitVector& GetBlocks() const { return blocks_; }
+ void Add(HBasicBlock* block);
+
private:
// Internal recursive implementation of `Populate`.
void PopulateRecursive(HBasicBlock* block);
@@ -328,6 +366,7 @@
}
HGraph* GetGraph() const { return graph_; }
+ void SetGraph(HGraph* graph) { graph_ = graph; }
int GetBlockId() const { return block_id_; }
void SetBlockId(int id) { block_id_ = id; }
@@ -335,6 +374,16 @@
HBasicBlock* GetDominator() const { return dominator_; }
void SetDominator(HBasicBlock* dominator) { dominator_ = dominator; }
void AddDominatedBlock(HBasicBlock* block) { dominated_blocks_.Add(block); }
+ void ReplaceDominatedBlock(HBasicBlock* existing, HBasicBlock* new_block) {
+ for (size_t i = 0, e = dominated_blocks_.Size(); i < e; ++i) {
+ if (dominated_blocks_.Get(i) == existing) {
+ dominated_blocks_.Put(i, new_block);
+ return;
+ }
+ }
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+ }
int NumberOfBackEdges() const {
return loop_information_ == nullptr
@@ -361,10 +410,22 @@
successors_.Put(successor_index, new_block);
}
+ void ReplacePredecessor(HBasicBlock* existing, HBasicBlock* new_block) {
+ size_t predecessor_index = GetPredecessorIndexOf(existing);
+ DCHECK_NE(predecessor_index, static_cast<size_t>(-1));
+ existing->RemoveSuccessor(this);
+ new_block->successors_.Add(this);
+ predecessors_.Put(predecessor_index, new_block);
+ }
+
void RemovePredecessor(HBasicBlock* block) {
predecessors_.Delete(block);
}
+ void RemoveSuccessor(HBasicBlock* block) {
+ successors_.Delete(block);
+ }
+
void ClearAllPredecessors() {
predecessors_.Reset();
}
@@ -399,6 +460,26 @@
return -1;
}
+ // Split the block into two blocks just after `cursor`. Returns the newly
+ // created block. Note that this method just updates raw block information,
+ // like predecessors, successors, dominators, and instruction list. It does not
+ // update the graph, reverse post order, loop information, nor make sure the
+ // blocks are consistent (for example ending with a control flow instruction).
+ HBasicBlock* SplitAfter(HInstruction* cursor);
+
+ // Merge `other` at the end of `this`. Successors and dominated blocks of
+ // `other` are changed to be successors and dominated blocks of `this`. Note
+ // that this method does not update the graph, reverse post order, loop
+ // information, nor make sure the blocks are consistent (for example ending
+ // with a control flow instruction).
+ void MergeWith(HBasicBlock* other);
+
+ // Replace `this` with `other`. Predecessors, successors, and dominated blocks
+ // of `this` are moved to `other`.
+ // Note that this method does not update the graph, reverse post order, loop
+ // information, nor make sure the blocks are consistent (for example ending
+ void ReplaceWith(HBasicBlock* other);
+
void AddInstruction(HInstruction* instruction);
void RemoveInstruction(HInstruction* instruction);
void InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor);
@@ -423,8 +504,9 @@
return loop_information_;
}
- // Set the loop_information_ on this block. This method overrides the current
+ // Set the loop_information_ on this block. Overrides the current
// loop_information if it is an outer loop of the passed loop information.
+ // Note that this method is called while creating the loop information.
void SetInLoop(HLoopInformation* info) {
if (IsLoopHeader()) {
// Nothing to do. This just means `info` is an outer loop.
@@ -442,6 +524,11 @@
}
}
+ // Raw update of the loop information.
+ void SetLoopInformation(HLoopInformation* info) {
+ loop_information_ = info;
+ }
+
bool IsInLoop() const { return loop_information_ != nullptr; }
// Returns wheter this block dominates the blocked passed as parameter.
@@ -459,7 +546,7 @@
void SetIsCatchBlock() { is_catch_block_ = true; }
private:
- HGraph* const graph_;
+ HGraph* graph_;
GrowableArray<HBasicBlock*> predecessors_;
GrowableArray<HBasicBlock*> successors_;
HInstructionList instructions_;
@@ -474,6 +561,9 @@
size_t lifetime_end_;
bool is_catch_block_;
+ friend class HGraph;
+ friend class HInstruction;
+
DISALLOW_COPY_AND_ASSIGN(HBasicBlock);
};
@@ -503,7 +593,7 @@
M(InstanceOf, Instruction) \
M(IntConstant, Constant) \
M(InvokeInterface, Invoke) \
- M(InvokeStatic, Invoke) \
+ M(InvokeStaticOrDirect, Invoke) \
M(InvokeVirtual, Invoke) \
M(LessThan, Condition) \
M(LessThanOrEqual, Condition) \
@@ -562,26 +652,104 @@
} \
virtual void Accept(HGraphVisitor* visitor)
+template <typename T> class HUseList;
+
template <typename T>
class HUseListNode : public ArenaObject<kArenaAllocMisc> {
public:
- HUseListNode(T* user, size_t index, HUseListNode* tail)
- : user_(user), index_(index), tail_(tail) {}
-
- HUseListNode* GetTail() const { return tail_; }
- T* GetUser() const { return user_; }
+ HUseListNode* GetPrevious() const { return prev_; }
+ HUseListNode* GetNext() const { return next_; }
+ T GetUser() const { return user_; }
size_t GetIndex() const { return index_; }
- void SetTail(HUseListNode<T>* node) { tail_ = node; }
-
private:
- T* const user_;
+ HUseListNode(T user, size_t index)
+ : user_(user), index_(index), prev_(nullptr), next_(nullptr) {}
+
+ T const user_;
const size_t index_;
- HUseListNode<T>* tail_;
+ HUseListNode<T>* prev_;
+ HUseListNode<T>* next_;
+
+ friend class HUseList<T>;
DISALLOW_COPY_AND_ASSIGN(HUseListNode);
};
+template <typename T>
+class HUseList : public ValueObject {
+ public:
+ HUseList() : first_(nullptr) {}
+
+ void Clear() {
+ first_ = nullptr;
+ }
+
+ // Adds a new entry at the beginning of the use list and returns
+ // the newly created node.
+ HUseListNode<T>* AddUse(T user, size_t index, ArenaAllocator* arena) {
+ HUseListNode<T>* new_node = new (arena) HUseListNode<T>(user, index);
+ if (IsEmpty()) {
+ first_ = new_node;
+ } else {
+ first_->prev_ = new_node;
+ new_node->next_ = first_;
+ first_ = new_node;
+ }
+ return new_node;
+ }
+
+ HUseListNode<T>* GetFirst() const {
+ return first_;
+ }
+
+ void Remove(HUseListNode<T>* node) {
+ if (node->prev_ != nullptr) {
+ node->prev_->next_ = node->next_;
+ }
+ if (node->next_ != nullptr) {
+ node->next_->prev_ = node->prev_;
+ }
+ if (node == first_) {
+ first_ = node->next_;
+ }
+ }
+
+ bool IsEmpty() const {
+ return first_ == nullptr;
+ }
+
+ bool HasOnlyOneUse() const {
+ return first_ != nullptr && first_->next_ == nullptr;
+ }
+
+ private:
+ HUseListNode<T>* first_;
+};
+
+template<typename T>
+class HUseIterator : public ValueObject {
+ public:
+ explicit HUseIterator(const HUseList<T>& uses) : current_(uses.GetFirst()) {}
+
+ bool Done() const { return current_ == nullptr; }
+
+ void Advance() {
+ DCHECK(!Done());
+ current_ = current_->GetNext();
+ }
+
+ HUseListNode<T>* Current() const {
+ DCHECK(!Done());
+ return current_;
+ }
+
+ private:
+ HUseListNode<T>* current_;
+
+ friend class HValue;
+};
+
// Represents the side effects an instruction may have.
class SideEffects : public ValueObject {
public:
@@ -645,6 +813,57 @@
size_t flags_;
};
+// A HEnvironment object contains the values of virtual registers at a given location.
+class HEnvironment : public ArenaObject<kArenaAllocMisc> {
+ public:
+ HEnvironment(ArenaAllocator* arena, size_t number_of_vregs)
+ : vregs_(arena, number_of_vregs) {
+ vregs_.SetSize(number_of_vregs);
+ for (size_t i = 0; i < number_of_vregs; i++) {
+ vregs_.Put(i, VRegInfo(nullptr, nullptr));
+ }
+ }
+
+ void CopyFrom(HEnvironment* env);
+
+ void SetRawEnvAt(size_t index, HInstruction* instruction) {
+ vregs_.Put(index, VRegInfo(instruction, nullptr));
+ }
+
+ // Record instructions' use entries of this environment for constant-time removal.
+ void RecordEnvUse(HUseListNode<HEnvironment*>* env_use) {
+ DCHECK(env_use->GetUser() == this);
+ size_t index = env_use->GetIndex();
+ VRegInfo info = vregs_.Get(index);
+ DCHECK(info.vreg_ != nullptr);
+ DCHECK(info.node_ == nullptr);
+ vregs_.Put(index, VRegInfo(info.vreg_, env_use));
+ }
+
+ HInstruction* GetInstructionAt(size_t index) const {
+ return vregs_.Get(index).vreg_;
+ }
+
+ HUseListNode<HEnvironment*>* GetInstructionEnvUseAt(size_t index) const {
+ return vregs_.Get(index).node_;
+ }
+
+ size_t Size() const { return vregs_.Size(); }
+
+ private:
+ struct VRegInfo {
+ HInstruction* vreg_;
+ HUseListNode<HEnvironment*>* node_;
+
+ VRegInfo(HInstruction* instruction, HUseListNode<HEnvironment*>* env_use)
+ : vreg_(instruction), node_(env_use) {}
+ };
+
+ GrowableArray<VRegInfo> vregs_;
+
+ DISALLOW_COPY_AND_ASSIGN(HEnvironment);
+};
+
class HInstruction : public ArenaObject<kArenaAllocMisc> {
public:
explicit HInstruction(SideEffects side_effects)
@@ -653,8 +872,6 @@
block_(nullptr),
id_(-1),
ssa_index_(-1),
- uses_(nullptr),
- env_uses_(nullptr),
environment_(nullptr),
locations_(nullptr),
live_interval_(nullptr),
@@ -672,6 +889,9 @@
HInstruction* GetNext() const { return next_; }
HInstruction* GetPrevious() const { return previous_; }
+ HInstruction* GetNextDisregardingMoves() const;
+ HInstruction* GetPreviousDisregardingMoves() const;
+
HBasicBlock* GetBlock() const { return block_; }
void SetBlock(HBasicBlock* block) { block_ = block; }
bool IsInBlock() const { return block_ != nullptr; }
@@ -692,35 +912,31 @@
virtual bool CanThrow() const { return false; }
bool HasSideEffects() const { return side_effects_.HasSideEffects(); }
+ // Does not apply for all instructions, but having this at top level greatly
+ // simplifies the null check elimination.
+ virtual bool CanBeNull() const { return true; }
+
+ virtual bool CanDoImplicitNullCheck() const { return false; }
+
void AddUseAt(HInstruction* user, size_t index) {
- uses_ = new (block_->GetGraph()->GetArena()) HUseListNode<HInstruction>(user, index, uses_);
+ uses_.AddUse(user, index, GetBlock()->GetGraph()->GetArena());
}
void AddEnvUseAt(HEnvironment* user, size_t index) {
DCHECK(user != nullptr);
- env_uses_ = new (block_->GetGraph()->GetArena()) HUseListNode<HEnvironment>(
- user, index, env_uses_);
+ HUseListNode<HEnvironment*>* env_use =
+ env_uses_.AddUse(user, index, GetBlock()->GetGraph()->GetArena());
+ user->RecordEnvUse(env_use);
}
void RemoveUser(HInstruction* user, size_t index);
- void RemoveEnvironmentUser(HEnvironment* user, size_t index);
+ void RemoveEnvironmentUser(HUseListNode<HEnvironment*>* use);
- HUseListNode<HInstruction>* GetUses() const { return uses_; }
- HUseListNode<HEnvironment>* GetEnvUses() const { return env_uses_; }
+ const HUseList<HInstruction*>& GetUses() { return uses_; }
+ const HUseList<HEnvironment*>& GetEnvUses() { return env_uses_; }
- bool HasUses() const { return uses_ != nullptr || env_uses_ != nullptr; }
- bool HasEnvironmentUses() const { return env_uses_ != nullptr; }
-
- size_t NumberOfUses() const {
- // TODO: Optimize this method if it is used outside of the HGraphVisualizer.
- size_t result = 0;
- HUseListNode<HInstruction>* current = uses_;
- while (current != nullptr) {
- current = current->GetTail();
- ++result;
- }
- return result;
- }
+ bool HasUses() const { return !uses_.IsEmpty() || !env_uses_.IsEmpty(); }
+ bool HasEnvironmentUses() const { return !env_uses_.IsEmpty(); }
// Does this instruction strictly dominate `other_instruction`?
// Returns false if this instruction and `other_instruction` are the same.
@@ -748,9 +964,8 @@
void ReplaceWith(HInstruction* instruction);
void ReplaceInput(HInstruction* replacement, size_t index);
- bool HasOnlyOneUse() const {
- return uses_ != nullptr && uses_->GetTail() == nullptr;
- }
+ // Move `this` instruction before `cursor`.
+ void MoveBefore(HInstruction* cursor);
#define INSTRUCTION_TYPE_CHECK(type, super) \
bool Is##type() const { return (As##type() != nullptr); } \
@@ -800,6 +1015,18 @@
void SetLiveInterval(LiveInterval* interval) { live_interval_ = interval; }
bool HasLiveInterval() const { return live_interval_ != nullptr; }
+ bool IsSuspendCheckEntry() const { return IsSuspendCheck() && GetBlock()->IsEntryBlock(); }
+
+ // Returns whether the code generation of the instruction will require to have access
+ // to the current method. Such instructions are:
+ // (1): Instructions that require an environment, as calling the runtime requires
+ // to walk the stack and have the current method stored at a specific stack address.
+ // (2): Object literals like classes and strings, that are loaded from the dex cache
+ // fields of the current method.
+ bool NeedsCurrentMethod() const {
+ return NeedsEnvironment() || IsLoadClass() || IsLoadString();
+ }
+
private:
HInstruction* previous_;
HInstruction* next_;
@@ -814,10 +1041,10 @@
int ssa_index_;
// List of instructions that have this instruction as input.
- HUseListNode<HInstruction>* uses_;
+ HUseList<HInstruction*> uses_;
// List of environments that contain this instruction.
- HUseListNode<HEnvironment>* env_uses_;
+ HUseList<HEnvironment*> env_uses_;
// The environment associated with this instruction. Not null if the instruction
// might jump out of the method.
@@ -836,75 +1063,13 @@
const SideEffects side_effects_;
friend class HBasicBlock;
+ friend class HGraph;
friend class HInstructionList;
DISALLOW_COPY_AND_ASSIGN(HInstruction);
};
std::ostream& operator<<(std::ostream& os, const HInstruction::InstructionKind& rhs);
-template<typename T>
-class HUseIterator : public ValueObject {
- public:
- explicit HUseIterator(HUseListNode<T>* uses) : current_(uses) {}
-
- bool Done() const { return current_ == nullptr; }
-
- void Advance() {
- DCHECK(!Done());
- current_ = current_->GetTail();
- }
-
- HUseListNode<T>* Current() const {
- DCHECK(!Done());
- return current_;
- }
-
- private:
- HUseListNode<T>* current_;
-
- friend class HValue;
-};
-
-// A HEnvironment object contains the values of virtual registers at a given location.
-class HEnvironment : public ArenaObject<kArenaAllocMisc> {
- public:
- HEnvironment(ArenaAllocator* arena, size_t number_of_vregs) : vregs_(arena, number_of_vregs) {
- vregs_.SetSize(number_of_vregs);
- for (size_t i = 0; i < number_of_vregs; i++) {
- vregs_.Put(i, nullptr);
- }
- }
-
- void Populate(const GrowableArray<HInstruction*>& env) {
- for (size_t i = 0; i < env.Size(); i++) {
- HInstruction* instruction = env.Get(i);
- vregs_.Put(i, instruction);
- if (instruction != nullptr) {
- instruction->AddEnvUseAt(this, i);
- }
- }
- }
-
- void SetRawEnvAt(size_t index, HInstruction* instruction) {
- vregs_.Put(index, instruction);
- }
-
- HInstruction* GetInstructionAt(size_t index) const {
- return vregs_.Get(index);
- }
-
- GrowableArray<HInstruction*>* GetVRegs() {
- return &vregs_;
- }
-
- size_t Size() const { return vregs_.Size(); }
-
- private:
- GrowableArray<HInstruction*> vregs_;
-
- DISALLOW_COPY_AND_ASSIGN(HEnvironment);
-};
-
class HInputIterator : public ValueObject {
public:
explicit HInputIterator(HInstruction* instruction) : instruction_(instruction), index_(0) {}
@@ -1552,25 +1717,24 @@
DISALLOW_COPY_AND_ASSIGN(HLongConstant);
};
+enum class Intrinsics {
+#define OPTIMIZING_INTRINSICS(Name, IsStatic) k ## Name,
+#include "intrinsics_list.h"
+ kNone,
+ INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+};
+std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic);
+
class HInvoke : public HInstruction {
public:
- HInvoke(ArenaAllocator* arena,
- uint32_t number_of_arguments,
- Primitive::Type return_type,
- uint32_t dex_pc)
- : HInstruction(SideEffects::All()),
- inputs_(arena, number_of_arguments),
- return_type_(return_type),
- dex_pc_(dex_pc) {
- inputs_.SetSize(number_of_arguments);
- }
-
virtual size_t InputCount() const { return inputs_.Size(); }
virtual HInstruction* InputAt(size_t i) const { return inputs_.Get(i); }
// Runtime needs to walk the stack, so Dex -> Dex calls need to
// know their environment.
- virtual bool NeedsEnvironment() const { return true; }
+ bool NeedsEnvironment() const OVERRIDE { return true; }
void SetArgumentAt(size_t index, HInstruction* argument) {
SetRawInputAt(index, argument);
@@ -1584,35 +1748,72 @@
uint32_t GetDexPc() const { return dex_pc_; }
+ uint32_t GetDexMethodIndex() const { return dex_method_index_; }
+
+ Intrinsics GetIntrinsic() {
+ return intrinsic_;
+ }
+
+ void SetIntrinsic(Intrinsics intrinsic) {
+ intrinsic_ = intrinsic;
+ }
+
DECLARE_INSTRUCTION(Invoke);
protected:
+ HInvoke(ArenaAllocator* arena,
+ uint32_t number_of_arguments,
+ Primitive::Type return_type,
+ uint32_t dex_pc,
+ uint32_t dex_method_index)
+ : HInstruction(SideEffects::All()),
+ inputs_(arena, number_of_arguments),
+ return_type_(return_type),
+ dex_pc_(dex_pc),
+ dex_method_index_(dex_method_index),
+ intrinsic_(Intrinsics::kNone) {
+ inputs_.SetSize(number_of_arguments);
+ }
+
GrowableArray<HInstruction*> inputs_;
const Primitive::Type return_type_;
const uint32_t dex_pc_;
+ const uint32_t dex_method_index_;
+ Intrinsics intrinsic_;
private:
DISALLOW_COPY_AND_ASSIGN(HInvoke);
};
-class HInvokeStatic : public HInvoke {
+class HInvokeStaticOrDirect : public HInvoke {
public:
- HInvokeStatic(ArenaAllocator* arena,
- uint32_t number_of_arguments,
- Primitive::Type return_type,
- uint32_t dex_pc,
- uint32_t index_in_dex_cache)
- : HInvoke(arena, number_of_arguments, return_type, dex_pc),
- index_in_dex_cache_(index_in_dex_cache) {}
+ HInvokeStaticOrDirect(ArenaAllocator* arena,
+ uint32_t number_of_arguments,
+ Primitive::Type return_type,
+ uint32_t dex_pc,
+ uint32_t dex_method_index,
+ bool is_recursive,
+ InvokeType invoke_type)
+ : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index),
+ invoke_type_(invoke_type),
+ is_recursive_(is_recursive) {}
- uint32_t GetIndexInDexCache() const { return index_in_dex_cache_; }
+ bool CanDoImplicitNullCheck() const OVERRIDE {
+ // We access the method via the dex cache so we can't do an implicit null check.
+ // TODO: for intrinsics we can generate implicit null checks.
+ return false;
+ }
- DECLARE_INSTRUCTION(InvokeStatic);
+ InvokeType GetInvokeType() const { return invoke_type_; }
+ bool IsRecursive() const { return is_recursive_; }
+
+ DECLARE_INSTRUCTION(InvokeStaticOrDirect);
private:
- const uint32_t index_in_dex_cache_;
+ const InvokeType invoke_type_;
+ const bool is_recursive_;
- DISALLOW_COPY_AND_ASSIGN(HInvokeStatic);
+ DISALLOW_COPY_AND_ASSIGN(HInvokeStaticOrDirect);
};
class HInvokeVirtual : public HInvoke {
@@ -1621,10 +1822,16 @@
uint32_t number_of_arguments,
Primitive::Type return_type,
uint32_t dex_pc,
+ uint32_t dex_method_index,
uint32_t vtable_index)
- : HInvoke(arena, number_of_arguments, return_type, dex_pc),
+ : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index),
vtable_index_(vtable_index) {}
+ bool CanDoImplicitNullCheck() const OVERRIDE {
+ // TODO: Add implicit null checks in intrinsics.
+ return !GetLocations()->Intrinsified();
+ }
+
uint32_t GetVTableIndex() const { return vtable_index_; }
DECLARE_INSTRUCTION(InvokeVirtual);
@@ -1643,17 +1850,20 @@
uint32_t dex_pc,
uint32_t dex_method_index,
uint32_t imt_index)
- : HInvoke(arena, number_of_arguments, return_type, dex_pc),
- dex_method_index_(dex_method_index),
+ : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index),
imt_index_(imt_index) {}
+ bool CanDoImplicitNullCheck() const OVERRIDE {
+ // TODO: Add implicit null checks in intrinsics.
+ return !GetLocations()->Intrinsified();
+ }
+
uint32_t GetImtIndex() const { return imt_index_; }
uint32_t GetDexMethodIndex() const { return dex_method_index_; }
DECLARE_INSTRUCTION(InvokeInterface);
private:
- const uint32_t dex_method_index_;
const uint32_t imt_index_;
DISALLOW_COPY_AND_ASSIGN(HInvokeInterface);
@@ -1661,10 +1871,11 @@
class HNewInstance : public HExpression<0> {
public:
- HNewInstance(uint32_t dex_pc, uint16_t type_index)
+ HNewInstance(uint32_t dex_pc, uint16_t type_index, QuickEntrypointEnum entrypoint)
: HExpression(Primitive::kPrimNot, SideEffects::None()),
dex_pc_(dex_pc),
- type_index_(type_index) {}
+ type_index_(type_index),
+ entrypoint_(entrypoint) {}
uint32_t GetDexPc() const { return dex_pc_; }
uint16_t GetTypeIndex() const { return type_index_; }
@@ -1677,11 +1888,16 @@
// TODO: optimize when possible.
bool CanThrow() const OVERRIDE { return true; }
+ bool CanBeNull() const OVERRIDE { return false; }
+
+ QuickEntrypointEnum GetEntrypoint() const { return entrypoint_; }
+
DECLARE_INSTRUCTION(NewInstance);
private:
const uint32_t dex_pc_;
const uint16_t type_index_;
+ const QuickEntrypointEnum entrypoint_;
DISALLOW_COPY_AND_ASSIGN(HNewInstance);
};
@@ -1702,10 +1918,14 @@
class HNewArray : public HExpression<1> {
public:
- HNewArray(HInstruction* length, uint32_t dex_pc, uint16_t type_index)
+ HNewArray(HInstruction* length,
+ uint32_t dex_pc,
+ uint16_t type_index,
+ QuickEntrypointEnum entrypoint)
: HExpression(Primitive::kPrimNot, SideEffects::None()),
dex_pc_(dex_pc),
- type_index_(type_index) {
+ type_index_(type_index),
+ entrypoint_(entrypoint) {
SetRawInputAt(0, length);
}
@@ -1713,13 +1933,18 @@
uint16_t GetTypeIndex() const { return type_index_; }
// Calls runtime so needs an environment.
- virtual bool NeedsEnvironment() const { return true; }
+ bool NeedsEnvironment() const OVERRIDE { return true; }
+
+ bool CanBeNull() const OVERRIDE { return false; }
+
+ QuickEntrypointEnum GetEntrypoint() const { return entrypoint_; }
DECLARE_INSTRUCTION(NewArray);
private:
const uint32_t dex_pc_;
const uint16_t type_index_;
+ const QuickEntrypointEnum entrypoint_;
DISALLOW_COPY_AND_ASSIGN(HNewArray);
};
@@ -1963,18 +2188,23 @@
// the calling convention.
class HParameterValue : public HExpression<0> {
public:
- HParameterValue(uint8_t index, Primitive::Type parameter_type)
- : HExpression(parameter_type, SideEffects::None()), index_(index) {}
+ HParameterValue(uint8_t index, Primitive::Type parameter_type, bool is_this = false)
+ : HExpression(parameter_type, SideEffects::None()), index_(index), is_this_(is_this) {}
uint8_t GetIndex() const { return index_; }
+ bool CanBeNull() const OVERRIDE { return !is_this_; }
+
DECLARE_INSTRUCTION(ParameterValue);
private:
// The index of this parameter in the parameters list. Must be less
- // than HGraph::number_of_in_vregs_;
+ // than HGraph::number_of_in_vregs_.
const uint8_t index_;
+ // Whether or not the parameter value corresponds to 'this' argument.
+ const bool is_this_;
+
DISALLOW_COPY_AND_ASSIGN(HParameterValue);
};
@@ -2001,8 +2231,8 @@
class HTypeConversion : public HExpression<1> {
public:
// Instantiate a type conversion of `input` to `result_type`.
- HTypeConversion(Primitive::Type result_type, HInstruction* input)
- : HExpression(result_type, SideEffects::None()) {
+ HTypeConversion(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc)
+ : HExpression(result_type, SideEffects::None()), dex_pc_(dex_pc) {
SetRawInputAt(0, input);
DCHECK_NE(input->GetType(), result_type);
}
@@ -2011,15 +2241,23 @@
Primitive::Type GetInputType() const { return GetInput()->GetType(); }
Primitive::Type GetResultType() const { return GetType(); }
+ // Required by the x86 and ARM code generators when producing calls
+ // to the runtime.
+ uint32_t GetDexPc() const { return dex_pc_; }
+
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; }
DECLARE_INSTRUCTION(TypeConversion);
private:
+ const uint32_t dex_pc_;
+
DISALLOW_COPY_AND_ASSIGN(HTypeConversion);
};
+static constexpr uint32_t kNoRegNumber = -1;
+
class HPhi : public HInstruction {
public:
HPhi(ArenaAllocator* arena, uint32_t reg_number, size_t number_of_inputs, Primitive::Type type)
@@ -2027,22 +2265,26 @@
inputs_(arena, number_of_inputs),
reg_number_(reg_number),
type_(type),
- is_live_(false) {
+ is_live_(false),
+ can_be_null_(true) {
inputs_.SetSize(number_of_inputs);
}
- virtual size_t InputCount() const { return inputs_.Size(); }
- virtual HInstruction* InputAt(size_t i) const { return inputs_.Get(i); }
+ size_t InputCount() const OVERRIDE { return inputs_.Size(); }
+ HInstruction* InputAt(size_t i) const OVERRIDE { return inputs_.Get(i); }
- virtual void SetRawInputAt(size_t index, HInstruction* input) {
+ void SetRawInputAt(size_t index, HInstruction* input) OVERRIDE {
inputs_.Put(index, input);
}
void AddInput(HInstruction* input);
- virtual Primitive::Type GetType() const { return type_; }
+ Primitive::Type GetType() const OVERRIDE { return type_; }
void SetType(Primitive::Type type) { type_ = type; }
+ bool CanBeNull() const OVERRIDE { return can_be_null_; }
+ void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; }
+
uint32_t GetRegNumber() const { return reg_number_; }
void SetDead() { is_live_ = false; }
@@ -2057,6 +2299,7 @@
const uint32_t reg_number_;
Primitive::Type type_;
bool is_live_;
+ bool can_be_null_;
DISALLOW_COPY_AND_ASSIGN(HPhi);
};
@@ -2068,15 +2311,17 @@
SetRawInputAt(0, value);
}
- virtual bool CanBeMoved() const { return true; }
- virtual bool InstructionDataEquals(HInstruction* other) const {
+ bool CanBeMoved() const OVERRIDE { return true; }
+ bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
UNUSED(other);
return true;
}
- virtual bool NeedsEnvironment() const { return true; }
+ bool NeedsEnvironment() const OVERRIDE { return true; }
- virtual bool CanThrow() const { return true; }
+ bool CanThrow() const OVERRIDE { return true; }
+
+ bool CanBeNull() const OVERRIDE { return false; }
uint32_t GetDexPc() const { return dex_pc_; }
@@ -2090,39 +2335,49 @@
class FieldInfo : public ValueObject {
public:
- FieldInfo(MemberOffset field_offset, Primitive::Type field_type)
- : field_offset_(field_offset), field_type_(field_type) {}
+ FieldInfo(MemberOffset field_offset, Primitive::Type field_type, bool is_volatile)
+ : field_offset_(field_offset), field_type_(field_type), is_volatile_(is_volatile) {}
MemberOffset GetFieldOffset() const { return field_offset_; }
Primitive::Type GetFieldType() const { return field_type_; }
+ bool IsVolatile() const { return is_volatile_; }
private:
const MemberOffset field_offset_;
const Primitive::Type field_type_;
+ const bool is_volatile_;
};
class HInstanceFieldGet : public HExpression<1> {
public:
HInstanceFieldGet(HInstruction* value,
Primitive::Type field_type,
- MemberOffset field_offset)
+ MemberOffset field_offset,
+ bool is_volatile)
: HExpression(field_type, SideEffects::DependsOnSomething()),
- field_info_(field_offset, field_type) {
+ field_info_(field_offset, field_type, is_volatile) {
SetRawInputAt(0, value);
}
- virtual bool CanBeMoved() const { return true; }
- virtual bool InstructionDataEquals(HInstruction* other) const {
- size_t other_offset = other->AsInstanceFieldGet()->GetFieldOffset().SizeValue();
- return other_offset == GetFieldOffset().SizeValue();
+ bool CanBeMoved() const OVERRIDE { return !IsVolatile(); }
+
+ bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
+ HInstanceFieldGet* other_get = other->AsInstanceFieldGet();
+ return GetFieldOffset().SizeValue() == other_get->GetFieldOffset().SizeValue();
}
- virtual size_t ComputeHashCode() const {
+ bool CanDoImplicitNullCheck() const OVERRIDE {
+ return GetFieldOffset().Uint32Value() < kPageSize;
+ }
+
+ size_t ComputeHashCode() const OVERRIDE {
return (HInstruction::ComputeHashCode() << 7) | GetFieldOffset().SizeValue();
}
+ const FieldInfo& GetFieldInfo() const { return field_info_; }
MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
+ bool IsVolatile() const { return field_info_.IsVolatile(); }
DECLARE_INSTRUCTION(InstanceFieldGet);
@@ -2137,16 +2392,22 @@
HInstanceFieldSet(HInstruction* object,
HInstruction* value,
Primitive::Type field_type,
- MemberOffset field_offset)
+ MemberOffset field_offset,
+ bool is_volatile)
: HTemplateInstruction(SideEffects::ChangesSomething()),
- field_info_(field_offset, field_type) {
+ field_info_(field_offset, field_type, is_volatile) {
SetRawInputAt(0, object);
SetRawInputAt(1, value);
}
+ bool CanDoImplicitNullCheck() const OVERRIDE {
+ return GetFieldOffset().Uint32Value() < kPageSize;
+ }
+
+ const FieldInfo& GetFieldInfo() const { return field_info_; }
MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
-
+ bool IsVolatile() const { return field_info_.IsVolatile(); }
HInstruction* GetValue() const { return InputAt(1); }
DECLARE_INSTRUCTION(InstanceFieldSet);
@@ -2170,6 +2431,15 @@
UNUSED(other);
return true;
}
+ bool CanDoImplicitNullCheck() const OVERRIDE {
+ // TODO: We can be smarter here.
+ // Currently, the array access is always preceded by an ArrayLength or a NullCheck
+ // which generates the implicit null check. There are cases when these can be removed
+ // to produce better code. If we ever add optimizations to do so we should allow an
+ // implicit check here (as long as the address falls in the first page).
+ return false;
+ }
+
void SetType(Primitive::Type type) { type_ = type; }
HInstruction* GetArray() const { return InputAt(0); }
@@ -2197,12 +2467,17 @@
SetRawInputAt(2, value);
}
- bool NeedsEnvironment() const {
+ bool NeedsEnvironment() const OVERRIDE {
// We currently always call a runtime method to catch array store
// exceptions.
return needs_type_check_;
}
+ bool CanDoImplicitNullCheck() const OVERRIDE {
+ // TODO: Same as for ArrayGet.
+ return false;
+ }
+
void ClearNeedsTypeCheck() {
needs_type_check_ = false;
}
@@ -2245,11 +2520,12 @@
SetRawInputAt(0, array);
}
- virtual bool CanBeMoved() const { return true; }
- virtual bool InstructionDataEquals(HInstruction* other) const {
+ bool CanBeMoved() const OVERRIDE { return true; }
+ bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
UNUSED(other);
return true;
}
+ bool CanDoImplicitNullCheck() const OVERRIDE { return true; }
DECLARE_INSTRUCTION(ArrayLength);
@@ -2376,6 +2652,12 @@
return MustGenerateClinitCheck() || !is_referrers_class_;
}
+ bool CanThrow() const OVERRIDE {
+ // May call runtime and and therefore can throw.
+ // TODO: finer grain decision.
+ return !is_referrers_class_;
+ }
+
DECLARE_INSTRUCTION(LoadClass);
private:
@@ -2419,7 +2701,7 @@
DISALLOW_COPY_AND_ASSIGN(HLoadString);
};
-// TODO: Pass this check to HInvokeStatic nodes.
+// TODO: Pass this check to HInvokeStaticOrDirect nodes.
/**
* Performs an initialization check on its Class object input.
*/
@@ -2458,24 +2740,29 @@
public:
HStaticFieldGet(HInstruction* cls,
Primitive::Type field_type,
- MemberOffset field_offset)
+ MemberOffset field_offset,
+ bool is_volatile)
: HExpression(field_type, SideEffects::DependsOnSomething()),
- field_info_(field_offset, field_type) {
+ field_info_(field_offset, field_type, is_volatile) {
SetRawInputAt(0, cls);
}
- bool CanBeMoved() const OVERRIDE { return true; }
+
+ bool CanBeMoved() const OVERRIDE { return !IsVolatile(); }
+
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
- size_t other_offset = other->AsStaticFieldGet()->GetFieldOffset().SizeValue();
- return other_offset == GetFieldOffset().SizeValue();
+ HStaticFieldGet* other_get = other->AsStaticFieldGet();
+ return GetFieldOffset().SizeValue() == other_get->GetFieldOffset().SizeValue();
}
size_t ComputeHashCode() const OVERRIDE {
return (HInstruction::ComputeHashCode() << 7) | GetFieldOffset().SizeValue();
}
+ const FieldInfo& GetFieldInfo() const { return field_info_; }
MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
+ bool IsVolatile() const { return field_info_.IsVolatile(); }
DECLARE_INSTRUCTION(StaticFieldGet);
@@ -2490,15 +2777,18 @@
HStaticFieldSet(HInstruction* cls,
HInstruction* value,
Primitive::Type field_type,
- MemberOffset field_offset)
+ MemberOffset field_offset,
+ bool is_volatile)
: HTemplateInstruction(SideEffects::ChangesSomething()),
- field_info_(field_offset, field_type) {
+ field_info_(field_offset, field_type, is_volatile) {
SetRawInputAt(0, cls);
SetRawInputAt(1, value);
}
+ const FieldInfo& GetFieldInfo() const { return field_info_; }
MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
+ bool IsVolatile() const { return field_info_.IsVolatile(); }
HInstruction* GetValue() const { return InputAt(1); }
@@ -2532,12 +2822,14 @@
bool NeedsEnvironment() const OVERRIDE { return true; }
+ bool CanThrow() const OVERRIDE { return true; }
+
uint32_t GetDexPc() const { return dex_pc_; }
DECLARE_INSTRUCTION(Throw);
private:
- uint32_t dex_pc_;
+ const uint32_t dex_pc_;
DISALLOW_COPY_AND_ASSIGN(HThrow);
};
@@ -2639,7 +2931,7 @@
DECLARE_INSTRUCTION(MonitorOperation);
- protected:
+ private:
const OperationKind kind_;
const uint32_t dex_pc_;
@@ -2647,7 +2939,6 @@
DISALLOW_COPY_AND_ASSIGN(HMonitorOperation);
};
-
class MoveOperands : public ArenaObject<kArenaAllocMisc> {
public:
MoveOperands(Location source, Location destination, HInstruction* instruction)
@@ -2680,7 +2971,7 @@
// True if this blocks a move from the given location.
bool Blocks(Location loc) const {
- return !IsEliminated() && source_.Equals(loc);
+ return !IsEliminated() && (source_.Contains(loc) || loc.Contains(source_));
}
// A move is redundant if it's been eliminated, if its source and
@@ -2709,8 +3000,6 @@
// This is only used in debug mode, to ensure we do not connect interval siblings
// in the same parallel move.
HInstruction* instruction_;
-
- DISALLOW_COPY_AND_ASSIGN(MoveOperands);
};
static constexpr size_t kDefaultNumberOfMoves = 4;
@@ -2720,18 +3009,26 @@
explicit HParallelMove(ArenaAllocator* arena)
: HTemplateInstruction(SideEffects::None()), moves_(arena, kDefaultNumberOfMoves) {}
- void AddMove(MoveOperands* move) {
- if (kIsDebugBuild && move->GetInstruction() != nullptr) {
+ void AddMove(Location source, Location destination, HInstruction* instruction) {
+ DCHECK(source.IsValid());
+ DCHECK(destination.IsValid());
+ if (kIsDebugBuild) {
+ if (instruction != nullptr) {
+ for (size_t i = 0, e = moves_.Size(); i < e; ++i) {
+ DCHECK_NE(moves_.Get(i).GetInstruction(), instruction)
+ << "Doing parallel moves for the same instruction.";
+ }
+ }
for (size_t i = 0, e = moves_.Size(); i < e; ++i) {
- DCHECK_NE(moves_.Get(i)->GetInstruction(), move->GetInstruction())
- << "Doing parallel moves for the same instruction.";
+ DCHECK(!destination.Equals(moves_.Get(i).GetDestination()))
+ << "Same destination for two moves in a parallel move.";
}
}
- moves_.Add(move);
+ moves_.Add(MoveOperands(source, destination, instruction));
}
MoveOperands* MoveOperandsAt(size_t index) const {
- return moves_.Get(index);
+ return moves_.GetRawStorage() + index;
}
size_t NumMoves() const { return moves_.Size(); }
@@ -2739,7 +3036,7 @@
DECLARE_INSTRUCTION(ParallelMove);
private:
- GrowableArray<MoveOperands*> moves_;
+ GrowableArray<MoveOperands> moves_;
DISALLOW_COPY_AND_ASSIGN(HParallelMove);
};
@@ -2837,6 +3134,39 @@
DISALLOW_COPY_AND_ASSIGN(HPostOrderIterator);
};
+// Iterator over the blocks that art part of the loop. Includes blocks part
+// of an inner loop. The order in which the blocks are iterated is on their
+// block id.
+class HBlocksInLoopIterator : public ValueObject {
+ public:
+ explicit HBlocksInLoopIterator(const HLoopInformation& info)
+ : blocks_in_loop_(info.GetBlocks()),
+ blocks_(info.GetHeader()->GetGraph()->GetBlocks()),
+ index_(0) {
+ if (!blocks_in_loop_.IsBitSet(index_)) {
+ Advance();
+ }
+ }
+
+ bool Done() const { return index_ == blocks_.Size(); }
+ HBasicBlock* Current() const { return blocks_.Get(index_); }
+ void Advance() {
+ ++index_;
+ for (size_t e = blocks_.Size(); index_ < e; ++index_) {
+ if (blocks_in_loop_.IsBitSet(index_)) {
+ break;
+ }
+ }
+ }
+
+ private:
+ const BitVector& blocks_in_loop_;
+ const GrowableArray<HBasicBlock*>& blocks_;
+ size_t index_;
+
+ DISALLOW_COPY_AND_ASSIGN(HBlocksInLoopIterator);
+};
+
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_NODES_H_
diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc
index 70dd8d7..5dbdc74 100644
--- a/compiler/optimizing/nodes_test.cc
+++ b/compiler/optimizing/nodes_test.cc
@@ -81,13 +81,12 @@
entry->AddInstruction(new (&allocator) HExit());
ASSERT_FALSE(parameter1->HasUses());
- ASSERT_EQ(parameter1->NumberOfUses(), 0u);
HInstruction* to_insert = new (&allocator) HNullCheck(parameter1, 0);
entry->InsertInstructionBefore(to_insert, parameter2);
ASSERT_TRUE(parameter1->HasUses());
- ASSERT_EQ(parameter1->NumberOfUses(), 1u);
+ ASSERT_TRUE(parameter1->GetUses().HasOnlyOneUse());
}
/**
@@ -105,13 +104,12 @@
entry->AddInstruction(parameter);
ASSERT_FALSE(parameter->HasUses());
- ASSERT_EQ(parameter->NumberOfUses(), 0u);
HInstruction* to_add = new (&allocator) HNullCheck(parameter, 0);
entry->AddInstruction(to_add);
ASSERT_TRUE(parameter->HasUses());
- ASSERT_EQ(parameter->NumberOfUses(), 1u);
+ ASSERT_TRUE(parameter->GetUses().HasOnlyOneUse());
}
} // namespace art
diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h
index e36ef19..d9e082a 100644
--- a/compiler/optimizing/optimization.h
+++ b/compiler/optimizing/optimization.h
@@ -21,6 +21,12 @@
namespace art {
+static const char* kBuilderPassName = "builder";
+static const char* kSsaBuilderPassName = "ssa_builder";
+static const char* kLivenessPassName = "liveness";
+static const char* kRegisterAllocatorPassName = "register";
+static const char* kLoopInvariantCodeMotionPassName = "licm";
+
/**
* Abstraction to implement an optimization pass.
*/
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 100a6bc..c518f33 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -19,25 +19,34 @@
#include <fstream>
#include <stdint.h>
+#include "base/dumpable.h"
+#include "base/timing_logger.h"
+#include "bounds_check_elimination.h"
#include "builder.h"
#include "code_generator.h"
#include "compiler.h"
#include "constant_folding.h"
#include "dead_code_elimination.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "driver/compiler_driver.h"
#include "driver/dex_compilation_unit.h"
#include "elf_writer_quick.h"
#include "graph_visualizer.h"
#include "gvn.h"
+#include "inliner.h"
#include "instruction_simplifier.h"
+#include "intrinsics.h"
+#include "licm.h"
#include "jni/quick/jni_compiler.h"
#include "mirror/art_method-inl.h"
#include "nodes.h"
#include "prepare_for_register_allocation.h"
#include "register_allocator.h"
+#include "side_effects_analysis.h"
#include "ssa_builder.h"
#include "ssa_phi_elimination.h"
#include "ssa_liveness_analysis.h"
+#include "reference_type_propagation.h"
#include "utils/arena_allocator.h"
namespace art {
@@ -66,16 +75,88 @@
};
/**
- * If set to true, generates a file suitable for the c1visualizer tool and IRHydra.
- */
-static bool kIsVisualizerEnabled = false;
-
-/**
* Filter to apply to the visualizer. Methods whose name contain that filter will
- * be in the file.
+ * be dumped.
*/
static const char* kStringFilter = "";
+class PassInfo;
+
+class PassInfoPrinter : public ValueObject {
+ public:
+ PassInfoPrinter(HGraph* graph,
+ const char* method_name,
+ const CodeGenerator& codegen,
+ std::ostream* visualizer_output,
+ CompilerDriver* compiler_driver)
+ : method_name_(method_name),
+ timing_logger_enabled_(compiler_driver->GetDumpPasses()),
+ timing_logger_(method_name, true, true),
+ visualizer_enabled_(!compiler_driver->GetDumpCfgFileName().empty()),
+ visualizer_(visualizer_output, graph, codegen, method_name_) {
+ if (strstr(method_name, kStringFilter) == nullptr) {
+ timing_logger_enabled_ = visualizer_enabled_ = false;
+ }
+ }
+
+ ~PassInfoPrinter() {
+ if (timing_logger_enabled_) {
+ LOG(INFO) << "TIMINGS " << method_name_;
+ LOG(INFO) << Dumpable<TimingLogger>(timing_logger_);
+ }
+ }
+
+ private:
+ void StartPass(const char* pass_name) {
+ // Dump graph first, then start timer.
+ if (visualizer_enabled_) {
+ visualizer_.DumpGraph(pass_name, /* is_after_pass */ false);
+ }
+ if (timing_logger_enabled_) {
+ timing_logger_.StartTiming(pass_name);
+ }
+ }
+
+ void EndPass(const char* pass_name) {
+ // Pause timer first, then dump graph.
+ if (timing_logger_enabled_) {
+ timing_logger_.EndTiming();
+ }
+ if (visualizer_enabled_) {
+ visualizer_.DumpGraph(pass_name, /* is_after_pass */ true);
+ }
+ }
+
+ const char* method_name_;
+
+ bool timing_logger_enabled_;
+ TimingLogger timing_logger_;
+
+ bool visualizer_enabled_;
+ HGraphVisualizer visualizer_;
+
+ friend PassInfo;
+
+ DISALLOW_COPY_AND_ASSIGN(PassInfoPrinter);
+};
+
+class PassInfo : public ValueObject {
+ public:
+ PassInfo(const char *pass_name, PassInfoPrinter* pass_info_printer)
+ : pass_name_(pass_name),
+ pass_info_printer_(pass_info_printer) {
+ pass_info_printer_->StartPass(pass_name_);
+ }
+
+ ~PassInfo() {
+ pass_info_printer_->EndPass(pass_name_);
+ }
+
+ private:
+ const char* const pass_name_;
+ PassInfoPrinter* const pass_info_printer_;
+};
+
class OptimizingCompiler FINAL : public Compiler {
public:
explicit OptimizingCompiler(CompilerDriver* driver);
@@ -105,14 +186,9 @@
const std::string& android_root,
bool is_host) const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- Backend* GetCodeGenerator(CompilationUnit* cu ATTRIBUTE_UNUSED,
- void* compilation_unit ATTRIBUTE_UNUSED) const OVERRIDE {
- return nullptr;
- }
-
void InitCompilationUnit(CompilationUnit& cu ATTRIBUTE_UNUSED) const OVERRIDE {}
- void Init() const OVERRIDE {}
+ void Init() OVERRIDE;
void UnInit() const OVERRIDE {}
@@ -120,9 +196,20 @@
// Whether we should run any optimization or register allocation. If false, will
// just run the code generation after the graph was built.
const bool run_optimizations_;
- mutable AtomicInteger total_compiled_methods_;
- mutable AtomicInteger unoptimized_compiled_methods_;
- mutable AtomicInteger optimized_compiled_methods_;
+
+ // Optimize and compile `graph`.
+ CompiledMethod* CompileOptimized(HGraph* graph,
+ CodeGenerator* codegen,
+ CompilerDriver* driver,
+ const DexCompilationUnit& dex_compilation_unit,
+ PassInfoPrinter* pass_info) const;
+
+ // Just compile without doing optimizations.
+ CompiledMethod* CompileBaseline(CodeGenerator* codegen,
+ CompilerDriver* driver,
+ const DexCompilationUnit& dex_compilation_unit) const;
+
+ mutable OptimizingCompilerStats compilation_stats_;
std::unique_ptr<std::ostream> visualizer_output_;
@@ -135,24 +222,23 @@
: Compiler(driver, kMaximumCompilationTimeBeforeWarning),
run_optimizations_(
driver->GetCompilerOptions().GetCompilerFilter() != CompilerOptions::kTime),
- total_compiled_methods_(0),
- unoptimized_compiled_methods_(0),
- optimized_compiled_methods_(0) {
- if (kIsVisualizerEnabled) {
- visualizer_output_.reset(new std::ofstream("art.cfg"));
+ compilation_stats_() {}
+
+void OptimizingCompiler::Init() {
+ // Enable C1visualizer output. Must be done in Init() because the compiler
+ // driver is not fully initialized when passed to the compiler's constructor.
+ CompilerDriver* driver = GetCompilerDriver();
+ const std::string cfg_file_name = driver->GetDumpCfgFileName();
+ if (!cfg_file_name.empty()) {
+ CHECK_EQ(driver->GetThreadCount(), 1U)
+ << "Graph visualizer requires the compiler to run single-threaded. "
+ << "Invoke the compiler with '-j1'.";
+ visualizer_output_.reset(new std::ofstream(cfg_file_name));
}
}
OptimizingCompiler::~OptimizingCompiler() {
- if (total_compiled_methods_ == 0) {
- LOG(INFO) << "Did not compile any method.";
- } else {
- size_t unoptimized_percent = (unoptimized_compiled_methods_ * 100 / total_compiled_methods_);
- size_t optimized_percent = (optimized_compiled_methods_ * 100 / total_compiled_methods_);
- LOG(INFO) << "Compiled " << total_compiled_methods_ << " methods: "
- << unoptimized_percent << "% (" << unoptimized_compiled_methods_ << ") unoptimized, "
- << optimized_percent << "% (" << optimized_compiled_methods_ << ") optimized.";
- }
+ compilation_stats_.Log();
}
bool OptimizingCompiler::CanCompileMethod(uint32_t method_idx ATTRIBUTE_UNUSED,
@@ -191,48 +277,140 @@
return code_item.tries_size_ == 0;
}
-static void RunOptimizations(HGraph* graph, const HGraphVisualizer& visualizer) {
- HDeadCodeElimination opt1(graph);
- HConstantFolding opt2(graph);
- SsaRedundantPhiElimination opt3(graph);
- SsaDeadPhiElimination opt4(graph);
- InstructionSimplifier opt5(graph);
- GVNOptimization opt6(graph);
- InstructionSimplifier opt7(graph);
-
- HOptimization* optimizations[] = {
- &opt1,
- &opt2,
- &opt3,
- &opt4,
- &opt5,
- &opt6,
- &opt7
- };
-
- for (size_t i = 0; i < arraysize(optimizations); ++i) {
+static void RunOptimizations(HOptimization* optimizations[],
+ size_t length,
+ PassInfoPrinter* pass_info_printer) {
+ for (size_t i = 0; i < length; ++i) {
HOptimization* optimization = optimizations[i];
- optimization->Run();
- visualizer.DumpGraph(optimization->GetPassName());
+ {
+ PassInfo pass_info(optimization->GetPassName(), pass_info_printer);
+ optimization->Run();
+ }
optimization->Check();
}
}
-static bool TryBuildingSsa(HGraph* graph,
- const DexCompilationUnit& dex_compilation_unit,
- const HGraphVisualizer& visualizer) {
- graph->BuildDominatorTree();
- graph->TransformToSSA();
+static void RunOptimizations(HGraph* graph,
+ CompilerDriver* driver,
+ OptimizingCompilerStats* stats,
+ const DexCompilationUnit& dex_compilation_unit,
+ PassInfoPrinter* pass_info_printer) {
+ SsaRedundantPhiElimination redundant_phi(graph);
+ SsaDeadPhiElimination dead_phi(graph);
+ HDeadCodeElimination dce(graph);
+ HConstantFolding fold1(graph);
+ InstructionSimplifier simplify1(graph);
- if (!graph->AnalyzeNaturalLoops()) {
- LOG(INFO) << "Skipping compilation of "
- << PrettyMethod(dex_compilation_unit.GetDexMethodIndex(),
- *dex_compilation_unit.GetDexFile())
- << ": it contains a non natural loop";
- return false;
+ HInliner inliner(graph, dex_compilation_unit, driver, stats);
+
+ HConstantFolding fold2(graph);
+ SideEffectsAnalysis side_effects(graph);
+ GVNOptimization gvn(graph, side_effects);
+ LICM licm(graph, side_effects);
+ BoundsCheckElimination bce(graph);
+ ReferenceTypePropagation type_propagation(graph);
+ InstructionSimplifier simplify2(graph, "instruction_simplifier_after_types");
+
+ IntrinsicsRecognizer intrinsics(graph, dex_compilation_unit.GetDexFile(), driver);
+
+ HOptimization* optimizations[] = {
+ &redundant_phi,
+ &dead_phi,
+ &intrinsics,
+ &dce,
+ &fold1,
+ &simplify1,
+ &inliner,
+ &fold2,
+ &side_effects,
+ &gvn,
+ &licm,
+ &bce,
+ &type_propagation,
+ &simplify2
+ };
+
+ RunOptimizations(optimizations, arraysize(optimizations), pass_info_printer);
+}
+
+// The stack map we generate must be 4-byte aligned on ARM. Since existing
+// maps are generated alongside these stack maps, we must also align them.
+static ArrayRef<const uint8_t> AlignVectorSize(std::vector<uint8_t>& vector) {
+ size_t size = vector.size();
+ size_t aligned_size = RoundUp(size, 4);
+ for (; size < aligned_size; ++size) {
+ vector.push_back(0);
}
- visualizer.DumpGraph("ssa transform");
- return true;
+ return ArrayRef<const uint8_t>(vector);
+}
+
+
+CompiledMethod* OptimizingCompiler::CompileOptimized(HGraph* graph,
+ CodeGenerator* codegen,
+ CompilerDriver* compiler_driver,
+ const DexCompilationUnit& dex_compilation_unit,
+ PassInfoPrinter* pass_info_printer) const {
+ RunOptimizations(
+ graph, compiler_driver, &compilation_stats_, dex_compilation_unit, pass_info_printer);
+
+ PrepareForRegisterAllocation(graph).Run();
+ SsaLivenessAnalysis liveness(*graph, codegen);
+ {
+ PassInfo pass_info(kLivenessPassName, pass_info_printer);
+ liveness.Analyze();
+ }
+ {
+ PassInfo pass_info(kRegisterAllocatorPassName, pass_info_printer);
+ RegisterAllocator(graph->GetArena(), codegen, liveness).AllocateRegisters();
+ }
+
+ CodeVectorAllocator allocator;
+ codegen->CompileOptimized(&allocator);
+
+ std::vector<uint8_t> stack_map;
+ codegen->BuildStackMaps(&stack_map);
+
+ compilation_stats_.RecordStat(MethodCompilationStat::kCompiledOptimized);
+
+ return CompiledMethod::SwapAllocCompiledMethodStackMap(
+ compiler_driver,
+ codegen->GetInstructionSet(),
+ ArrayRef<const uint8_t>(allocator.GetMemory()),
+ codegen->GetFrameSize(),
+ codegen->GetCoreSpillMask(),
+ codegen->GetFpuSpillMask(),
+ ArrayRef<const uint8_t>(stack_map));
+}
+
+
+CompiledMethod* OptimizingCompiler::CompileBaseline(
+ CodeGenerator* codegen,
+ CompilerDriver* compiler_driver,
+ const DexCompilationUnit& dex_compilation_unit) const {
+ CodeVectorAllocator allocator;
+ codegen->CompileBaseline(&allocator);
+
+ std::vector<uint8_t> mapping_table;
+ DefaultSrcMap src_mapping_table;
+ bool include_debug_symbol = compiler_driver->GetCompilerOptions().GetIncludeDebugSymbols();
+ codegen->BuildMappingTable(&mapping_table, include_debug_symbol ? &src_mapping_table : nullptr);
+ std::vector<uint8_t> vmap_table;
+ codegen->BuildVMapTable(&vmap_table);
+ std::vector<uint8_t> gc_map;
+ codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
+
+ compilation_stats_.RecordStat(MethodCompilationStat::kCompiledBaseline);
+ return CompiledMethod::SwapAllocCompiledMethod(compiler_driver,
+ codegen->GetInstructionSet(),
+ ArrayRef<const uint8_t>(allocator.GetMemory()),
+ codegen->GetFrameSize(),
+ codegen->GetCoreSpillMask(),
+ codegen->GetFpuSpillMask(),
+ &src_mapping_table,
+ AlignVectorSize(mapping_table),
+ AlignVectorSize(vmap_table),
+ AlignVectorSize(gc_map),
+ ArrayRef<const uint8_t>());
}
CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
@@ -243,8 +421,10 @@
jobject class_loader,
const DexFile& dex_file) const {
UNUSED(invoke_type);
- total_compiled_methods_++;
- InstructionSet instruction_set = GetCompilerDriver()->GetInstructionSet();
+ std::string method_name = PrettyMethod(method_idx, dex_file);
+ compilation_stats_.RecordStat(MethodCompilationStat::kAttemptCompilation);
+ CompilerDriver* compiler_driver = GetCompilerDriver();
+ InstructionSet instruction_set = compiler_driver->GetInstructionSet();
// Always use the thumb2 assembler: some runtime functionality (like implicit stack
// overflow checks) assume thumb2.
if (instruction_set == kArm) {
@@ -253,114 +433,98 @@
// Do not attempt to compile on architectures we do not support.
if (!IsInstructionSetSupported(instruction_set)) {
+ compilation_stats_.RecordStat(MethodCompilationStat::kNotCompiledUnsupportedIsa);
return nullptr;
}
if (Compiler::IsPathologicalCase(*code_item, method_idx, dex_file)) {
+ compilation_stats_.RecordStat(MethodCompilationStat::kNotCompiledPathological);
return nullptr;
}
DexCompilationUnit dex_compilation_unit(
nullptr, class_loader, art::Runtime::Current()->GetClassLinker(), dex_file, code_item,
class_def_idx, method_idx, access_flags,
- GetCompilerDriver()->GetVerifiedMethod(&dex_file, method_idx));
-
- // For testing purposes, we put a special marker on method names that should be compiled
- // with this compiler. This makes sure we're not regressing.
- bool shouldCompile = dex_compilation_unit.GetSymbol().find("00024opt_00024") != std::string::npos;
- bool shouldOptimize =
- dex_compilation_unit.GetSymbol().find("00024reg_00024") != std::string::npos;
+ compiler_driver->GetVerifiedMethod(&dex_file, method_idx));
ArenaPool pool;
ArenaAllocator arena(&pool);
- HGraphBuilder builder(&arena, &dex_compilation_unit, &dex_file, GetCompilerDriver());
+ HGraph* graph = new (&arena) HGraph(&arena);
- HGraph* graph = builder.BuildGraph(*code_item);
- if (graph == nullptr) {
- CHECK(!shouldCompile) << "Could not build graph in optimizing compiler";
- return nullptr;
- }
+ // For testing purposes, we put a special marker on method names that should be compiled
+ // with this compiler. This makes sure we're not regressing.
+ bool shouldCompile = method_name.find("$opt$") != std::string::npos;
+ bool shouldOptimize = method_name.find("$opt$reg$") != std::string::npos;
- CodeGenerator* codegen = CodeGenerator::Create(&arena, graph, instruction_set);
- if (codegen == nullptr) {
+ std::unique_ptr<CodeGenerator> codegen(
+ CodeGenerator::Create(graph,
+ instruction_set,
+ *compiler_driver->GetInstructionSetFeatures(),
+ compiler_driver->GetCompilerOptions()));
+ if (codegen.get() == nullptr) {
CHECK(!shouldCompile) << "Could not find code generator for optimizing compiler";
+ compilation_stats_.RecordStat(MethodCompilationStat::kNotCompiledNoCodegen);
return nullptr;
}
- HGraphVisualizer visualizer(
- visualizer_output_.get(), graph, kStringFilter, *codegen, dex_compilation_unit);
- visualizer.DumpGraph("builder");
+ PassInfoPrinter pass_info_printer(graph,
+ method_name.c_str(),
+ *codegen.get(),
+ visualizer_output_.get(),
+ compiler_driver);
- CodeVectorAllocator allocator;
+ HGraphBuilder builder(graph,
+ &dex_compilation_unit,
+ &dex_compilation_unit,
+ &dex_file,
+ compiler_driver,
+ &compilation_stats_);
- if (run_optimizations_
- && CanOptimize(*code_item)
- && RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set)) {
- VLOG(compiler) << "Optimizing " << PrettyMethod(method_idx, dex_file);
- optimized_compiled_methods_++;
- if (!TryBuildingSsa(graph, dex_compilation_unit, visualizer)) {
- // We could not transform the graph to SSA, bailout.
+ VLOG(compiler) << "Building " << method_name;
+
+ {
+ PassInfo pass_info(kBuilderPassName, &pass_info_printer);
+ if (!builder.BuildGraph(*code_item)) {
+ CHECK(!shouldCompile) << "Could not build graph in optimizing compiler";
return nullptr;
}
- RunOptimizations(graph, visualizer);
+ }
- PrepareForRegisterAllocation(graph).Run();
- SsaLivenessAnalysis liveness(*graph, codegen);
- liveness.Analyze();
- visualizer.DumpGraph(kLivenessPassName);
+ bool can_optimize = CanOptimize(*code_item);
+ bool can_allocate_registers = RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set);
+ if (run_optimizations_ && can_optimize && can_allocate_registers) {
+ VLOG(compiler) << "Optimizing " << method_name;
- RegisterAllocator register_allocator(graph->GetArena(), codegen, liveness);
- register_allocator.AllocateRegisters();
+ {
+ PassInfo pass_info(kSsaBuilderPassName, &pass_info_printer);
+ if (!graph->TryBuildingSsa()) {
+ // We could not transform the graph to SSA, bailout.
+ LOG(INFO) << "Skipping compilation of " << method_name << ": it contains a non natural loop";
+ compilation_stats_.RecordStat(MethodCompilationStat::kNotCompiledCannotBuildSSA);
+ return nullptr;
+ }
+ }
- visualizer.DumpGraph(kRegisterAllocatorPassName);
- codegen->CompileOptimized(&allocator);
-
- std::vector<uint8_t> mapping_table;
- SrcMap src_mapping_table;
- codegen->BuildMappingTable(&mapping_table,
- GetCompilerDriver()->GetCompilerOptions().GetIncludeDebugSymbols() ?
- &src_mapping_table : nullptr);
-
- std::vector<uint8_t> stack_map;
- codegen->BuildStackMaps(&stack_map);
-
- return new CompiledMethod(GetCompilerDriver(),
- instruction_set,
- allocator.GetMemory(),
- codegen->GetFrameSize(),
- codegen->GetCoreSpillMask(),
- 0, /* FPR spill mask, unused */
- mapping_table,
- stack_map);
+ return CompileOptimized(graph,
+ codegen.get(),
+ compiler_driver,
+ dex_compilation_unit,
+ &pass_info_printer);
} else if (shouldOptimize && RegisterAllocator::Supports(instruction_set)) {
LOG(FATAL) << "Could not allocate registers in optimizing compiler";
UNREACHABLE();
} else {
- VLOG(compiler) << "Compile baseline " << PrettyMethod(method_idx, dex_file);
- unoptimized_compiled_methods_++;
- codegen->CompileBaseline(&allocator);
+ VLOG(compiler) << "Compile baseline " << method_name;
- std::vector<uint8_t> mapping_table;
- SrcMap src_mapping_table;
- codegen->BuildMappingTable(&mapping_table,
- GetCompilerDriver()->GetCompilerOptions().GetIncludeDebugSymbols() ?
- &src_mapping_table : nullptr);
- std::vector<uint8_t> vmap_table;
- codegen->BuildVMapTable(&vmap_table);
- std::vector<uint8_t> gc_map;
- codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
+ if (!run_optimizations_) {
+ compilation_stats_.RecordStat(MethodCompilationStat::kNotOptimizedDisabled);
+ } else if (!can_optimize) {
+ compilation_stats_.RecordStat(MethodCompilationStat::kNotOptimizedTryCatch);
+ } else if (!can_allocate_registers) {
+ compilation_stats_.RecordStat(MethodCompilationStat::kNotOptimizedRegisterAllocator);
+ }
- return new CompiledMethod(GetCompilerDriver(),
- instruction_set,
- allocator.GetMemory(),
- codegen->GetFrameSize(),
- codegen->GetCoreSpillMask(),
- 0, /* FPR spill mask, unused */
- &src_mapping_table,
- mapping_table,
- vmap_table,
- gc_map,
- nullptr);
+ return CompileBaseline(codegen.get(), compiler_driver, dex_compilation_unit);
}
}
diff --git a/compiler/optimizing/optimizing_compiler.h b/compiler/optimizing/optimizing_compiler.h
index a415eca..d076fb5 100644
--- a/compiler/optimizing/optimizing_compiler.h
+++ b/compiler/optimizing/optimizing_compiler.h
@@ -24,6 +24,6 @@
Compiler* CreateOptimizingCompiler(CompilerDriver* driver);
-}
+} // namespace art
#endif // ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
new file mode 100644
index 0000000..cc2723d
--- /dev/null
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_STATS_H_
+#define ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_STATS_H_
+
+#include <sstream>
+#include <string>
+
+#include "atomic.h"
+
+namespace art {
+
+enum MethodCompilationStat {
+ kAttemptCompilation = 0,
+ kCompiledBaseline,
+ kCompiledOptimized,
+ kInlinedInvoke,
+ kNotCompiledUnsupportedIsa,
+ kNotCompiledPathological,
+ kNotCompiledHugeMethod,
+ kNotCompiledLargeMethodNoBranches,
+ kNotCompiledCannotBuildSSA,
+ kNotCompiledNoCodegen,
+ kNotCompiledUnresolvedMethod,
+ kNotCompiledUnresolvedField,
+ kNotCompiledNonSequentialRegPair,
+ kNotOptimizedTryCatch,
+ kNotOptimizedDisabled,
+ kNotCompiledCantAccesType,
+ kNotOptimizedRegisterAllocator,
+ kNotCompiledUnhandledInstruction,
+ kLastStat
+};
+
+class OptimizingCompilerStats {
+ public:
+ OptimizingCompilerStats() {}
+
+ void RecordStat(MethodCompilationStat stat) {
+ compile_stats_[stat]++;
+ }
+
+ void Log() const {
+ if (compile_stats_[kAttemptCompilation] == 0) {
+ LOG(INFO) << "Did not compile any method.";
+ } else {
+ size_t unoptimized_percent =
+ compile_stats_[kCompiledBaseline] * 100 / compile_stats_[kAttemptCompilation];
+ size_t optimized_percent =
+ compile_stats_[kCompiledOptimized] * 100 / compile_stats_[kAttemptCompilation];
+ std::ostringstream oss;
+ oss << "Attempted compilation of " << compile_stats_[kAttemptCompilation] << " methods: "
+ << unoptimized_percent << "% (" << compile_stats_[kCompiledBaseline] << ") unoptimized, "
+ << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized.";
+ for (int i = 0; i < kLastStat; i++) {
+ if (compile_stats_[i] != 0) {
+ oss << "\n" << PrintMethodCompilationStat(i) << ": " << compile_stats_[i];
+ }
+ }
+ LOG(INFO) << oss.str();
+ }
+ }
+
+ private:
+ std::string PrintMethodCompilationStat(int stat) const {
+ switch (stat) {
+ case kAttemptCompilation : return "kAttemptCompilation";
+ case kCompiledBaseline : return "kCompiledBaseline";
+ case kCompiledOptimized : return "kCompiledOptimized";
+ case kInlinedInvoke : return "kInlinedInvoke";
+ case kNotCompiledUnsupportedIsa : return "kNotCompiledUnsupportedIsa";
+ case kNotCompiledPathological : return "kNotCompiledPathological";
+ case kNotCompiledHugeMethod : return "kNotCompiledHugeMethod";
+ case kNotCompiledLargeMethodNoBranches : return "kNotCompiledLargeMethodNoBranches";
+ case kNotCompiledCannotBuildSSA : return "kNotCompiledCannotBuildSSA";
+ case kNotCompiledNoCodegen : return "kNotCompiledNoCodegen";
+ case kNotCompiledUnresolvedMethod : return "kNotCompiledUnresolvedMethod";
+ case kNotCompiledUnresolvedField : return "kNotCompiledUnresolvedField";
+ case kNotCompiledNonSequentialRegPair : return "kNotCompiledNonSequentialRegPair";
+ case kNotOptimizedDisabled : return "kNotOptimizedDisabled";
+ case kNotOptimizedTryCatch : return "kNotOptimizedTryCatch";
+ case kNotCompiledCantAccesType : return "kNotCompiledCantAccesType";
+ case kNotOptimizedRegisterAllocator : return "kNotOptimizedRegisterAllocator";
+ case kNotCompiledUnhandledInstruction : return "kNotCompiledUnhandledInstruction";
+ default: LOG(FATAL) << "invalid stat";
+ }
+ return "";
+ }
+
+ AtomicInteger compile_stats_[kLastStat];
+
+ DISALLOW_COPY_AND_ASSIGN(OptimizingCompilerStats);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_STATS_H_
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index c4106b7..6b23692 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -19,6 +19,7 @@
#include "nodes.h"
#include "builder.h"
+#include "compiler/dex/pass_manager.h"
#include "dex_file.h"
#include "dex_instruction.h"
#include "ssa_liveness_analysis.h"
@@ -45,8 +46,12 @@
LiveInterval* BuildInterval(const size_t ranges[][2],
size_t number_of_ranges,
ArenaAllocator* allocator,
- int reg = -1) {
- LiveInterval* interval = LiveInterval::MakeInterval(allocator, Primitive::kPrimInt);
+ int reg = -1,
+ HInstruction* defined_by = nullptr) {
+ LiveInterval* interval = LiveInterval::MakeInterval(allocator, Primitive::kPrimInt, defined_by);
+ if (defined_by != nullptr) {
+ defined_by->SetLiveInterval(interval);
+ }
for (size_t i = number_of_ranges; i > 0; --i) {
interval->AddRange(ranges[i - 1][0], ranges[i - 1][1]);
}
@@ -71,11 +76,12 @@
inline HGraph* CreateCFG(ArenaAllocator* allocator,
const uint16_t* data,
Primitive::Type return_type = Primitive::kPrimInt) {
- HGraphBuilder builder(allocator, return_type);
+ HGraph* graph = new (allocator) HGraph(allocator);
+ HGraphBuilder builder(graph, return_type);
const DexFile::CodeItem* item =
reinterpret_cast<const DexFile::CodeItem*>(data);
- HGraph* graph = builder.BuildGraph(*item);
- return graph;
+ bool graph_built = builder.BuildGraph(*item);
+ return graph_built ? graph : nullptr;
}
// Naive string diff data type.
@@ -96,6 +102,11 @@
return result;
}
+// Returns if the instruction is removed from the graph.
+inline bool IsRemoved(HInstruction* instruction) {
+ return instruction->GetBlock() == nullptr;
+}
+
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_
diff --git a/compiler/optimizing/parallel_move_resolver.cc b/compiler/optimizing/parallel_move_resolver.cc
index 1e93ece..7d0641e 100644
--- a/compiler/optimizing/parallel_move_resolver.cc
+++ b/compiler/optimizing/parallel_move_resolver.cc
@@ -37,10 +37,12 @@
// Perform the moves with constant sources.
for (size_t i = 0; i < moves_.Size(); ++i) {
- const MoveOperands& move = *moves_.Get(i);
- if (!move.IsEliminated()) {
- DCHECK(move.GetSource().IsConstant());
+ MoveOperands* move = moves_.Get(i);
+ if (!move->IsEliminated()) {
+ DCHECK(move->GetSource().IsConstant());
EmitMove(i);
+ // Eliminate the move, in case following moves need a scratch register.
+ move->Eliminate();
}
}
@@ -61,8 +63,43 @@
}
}
+// Update the source of `move`, knowing that `updated_location` has been swapped
+// with `new_source`. Note that `updated_location` can be a pair, therefore if
+// `move` is non-pair, we need to extract which register to use.
+static void UpdateSourceOf(MoveOperands* move, Location updated_location, Location new_source) {
+ Location source = move->GetSource();
+ if (new_source.GetKind() == source.GetKind()) {
+ DCHECK(updated_location.Equals(source));
+ move->SetSource(new_source);
+ } else if (new_source.IsStackSlot()
+ || new_source.IsDoubleStackSlot()
+ || source.IsStackSlot()
+ || source.IsDoubleStackSlot()) {
+ // Stack slots never take part of a pair/non-pair swap.
+ DCHECK(updated_location.Equals(source));
+ move->SetSource(new_source);
+ } else if (source.IsRegister()) {
+ DCHECK(new_source.IsRegisterPair()) << new_source;
+ DCHECK(updated_location.IsRegisterPair()) << updated_location;
+ if (updated_location.low() == source.reg()) {
+ move->SetSource(Location::RegisterLocation(new_source.low()));
+ } else {
+ DCHECK_EQ(updated_location.high(), source.reg());
+ move->SetSource(Location::RegisterLocation(new_source.high()));
+ }
+ } else if (source.IsFpuRegister()) {
+ DCHECK(new_source.IsFpuRegisterPair()) << new_source;
+ DCHECK(updated_location.IsFpuRegisterPair()) << updated_location;
+ if (updated_location.low() == source.reg()) {
+ move->SetSource(Location::FpuRegisterLocation(new_source.low()));
+ } else {
+ DCHECK_EQ(updated_location.high(), source.reg());
+ move->SetSource(Location::FpuRegisterLocation(new_source.high()));
+ }
+ }
+}
-void ParallelMoveResolver::PerformMove(size_t index) {
+MoveOperands* ParallelMoveResolver::PerformMove(size_t index) {
// Each call to this function performs a move and deletes it from the move
// graph. We first recursively perform any move blocking this one. We
// mark a move as "pending" on entry to PerformMove in order to detect
@@ -70,35 +107,59 @@
// which means that a call to PerformMove could change any source operand
// in the move graph.
- DCHECK(!moves_.Get(index)->IsPending());
- DCHECK(!moves_.Get(index)->IsRedundant());
+ MoveOperands* move = moves_.Get(index);
+ DCHECK(!move->IsPending());
+ if (move->IsRedundant()) {
+ // Because we swap register pairs first, following, un-pending
+ // moves may become redundant.
+ move->Eliminate();
+ return nullptr;
+ }
// Clear this move's destination to indicate a pending move. The actual
// destination is saved in a stack-allocated local. Recursion may allow
// multiple moves to be pending.
- DCHECK(!moves_.Get(index)->GetSource().IsInvalid());
- Location destination = moves_.Get(index)->MarkPending();
+ DCHECK(!move->GetSource().IsInvalid());
+ Location destination = move->MarkPending();
// Perform a depth-first traversal of the move graph to resolve
// dependencies. Any unperformed, unpending move with a source the same
// as this one's destination blocks this one so recursively perform all
// such moves.
+ MoveOperands* required_swap = nullptr;
for (size_t i = 0; i < moves_.Size(); ++i) {
const MoveOperands& other_move = *moves_.Get(i);
if (other_move.Blocks(destination) && !other_move.IsPending()) {
// Though PerformMove can change any source operand in the move graph,
- // this call cannot create a blocking move via a swap (this loop does
- // not miss any). Assume there is a non-blocking move with source A
+ // calling `PerformMove` cannot create a blocking move via a swap
+ // (this loop does not miss any).
+ // For example, assume there is a non-blocking move with source A
// and this move is blocked on source B and there is a swap of A and
// B. Then A and B must be involved in the same cycle (or they would
// not be swapped). Since this move's destination is B and there is
// only a single incoming edge to an operand, this move must also be
// involved in the same cycle. In that case, the blocking move will
// be created but will be "pending" when we return from PerformMove.
- PerformMove(i);
+ required_swap = PerformMove(i);
+
+ if (required_swap == move) {
+ // If this move is required to swap, we do so without looking
+ // at the next moves. Swapping is not blocked by anything, it just
+ // updates other moves's source.
+ break;
+ } else if (required_swap == moves_.Get(i)) {
+ // If `other_move` was swapped, we iterate again to find a new
+ // potential cycle.
+ required_swap = nullptr;
+ i = 0;
+ } else if (required_swap != nullptr) {
+ // A move is required to swap. We walk back the cycle to find the
+ // move by just returning from this `PerforrmMove`.
+ moves_.Get(index)->ClearPending(destination);
+ return required_swap;
+ }
}
}
- MoveOperands* move = moves_.Get(index);
// We are about to resolve this move and don't need it marked as
// pending, so restore its destination.
@@ -108,19 +169,30 @@
// so it may now be the last move in the cycle. If so remove it.
if (move->GetSource().Equals(destination)) {
move->Eliminate();
- return;
+ DCHECK(required_swap == nullptr);
+ return nullptr;
}
// The move may be blocked on a (at most one) pending move, in which case
// we have a cycle. Search for such a blocking move and perform a swap to
// resolve it.
bool do_swap = false;
- for (size_t i = 0; i < moves_.Size(); ++i) {
- const MoveOperands& other_move = *moves_.Get(i);
- if (other_move.Blocks(destination)) {
- DCHECK(other_move.IsPending());
- do_swap = true;
- break;
+ if (required_swap != nullptr) {
+ DCHECK_EQ(required_swap, move);
+ do_swap = true;
+ } else {
+ for (size_t i = 0; i < moves_.Size(); ++i) {
+ const MoveOperands& other_move = *moves_.Get(i);
+ if (other_move.Blocks(destination)) {
+ DCHECK(other_move.IsPending());
+ if (!destination.IsPair() && other_move.GetSource().IsPair()) {
+ // We swap pairs before swapping non-pairs. Go back from the
+ // cycle by returning the pair that must be swapped.
+ return moves_.Get(i);
+ }
+ do_swap = true;
+ break;
+ }
}
}
@@ -135,15 +207,21 @@
for (size_t i = 0; i < moves_.Size(); ++i) {
const MoveOperands& other_move = *moves_.Get(i);
if (other_move.Blocks(source)) {
- moves_.Get(i)->SetSource(swap_destination);
+ UpdateSourceOf(moves_.Get(i), source, swap_destination);
} else if (other_move.Blocks(swap_destination)) {
- moves_.Get(i)->SetSource(source);
+ UpdateSourceOf(moves_.Get(i), swap_destination, source);
}
}
+ // If the swap was required because of a pair in the middle of a cycle,
+ // we return the swapped move, so that the caller knows it needs to re-iterate
+ // its dependency loop.
+ return required_swap;
} else {
// This move is not blocked.
EmitMove(index);
move->Eliminate();
+ DCHECK(required_swap == nullptr);
+ return nullptr;
}
}
diff --git a/compiler/optimizing/parallel_move_resolver.h b/compiler/optimizing/parallel_move_resolver.h
index 309425e..3fa1b37 100644
--- a/compiler/optimizing/parallel_move_resolver.h
+++ b/compiler/optimizing/parallel_move_resolver.h
@@ -58,6 +58,9 @@
};
bool IsScratchLocation(Location loc);
+
+ // Allocate a scratch register for performing a move. The method will try to use
+ // a register that is the destination of a move, but that move has not been emitted yet.
int AllocateScratchRegister(int blocked, int if_scratch, int register_count, bool* spilled);
// Emit a move.
@@ -80,7 +83,15 @@
// Perform the move at the moves_ index in question (possibly requiring
// other moves to satisfy dependencies).
- void PerformMove(size_t index);
+ //
+ // Return whether another move in the dependency cycle needs to swap. This
+ // is to handle pair swaps, where we want the pair to swap first to avoid
+ // building pairs that are unexpected by the code generator. For example, if
+ // we were to swap R1 with R2, we would need to update all locations using
+ // R2 to R1. So a (R2,R3) pair register could become (R1,R3). We could make
+ // the code generator understand such pairs, but it's easier and cleaner to
+ // just not create such pairs and exchange pairs in priority.
+ MoveOperands* PerformMove(size_t index);
DISALLOW_COPY_AND_ASSIGN(ParallelMoveResolver);
};
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index 62629bc..bb7541d 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -26,16 +26,26 @@
public:
explicit TestParallelMoveResolver(ArenaAllocator* allocator) : ParallelMoveResolver(allocator) {}
+ void Dump(Location location) {
+ if (location.IsConstant()) {
+ message_ << "C";
+ } else if (location.IsPair()) {
+ message_ << location.low() << "," << location.high();
+ } else {
+ message_ << location.reg();
+ }
+ }
+
virtual void EmitMove(size_t index) {
MoveOperands* move = moves_.Get(index);
if (!message_.str().empty()) {
message_ << " ";
}
- message_ << "("
- << move->GetSource().reg()
- << " -> "
- << move->GetDestination().reg()
- << ")";
+ message_ << "(";
+ Dump(move->GetSource());
+ message_ << " -> ";
+ Dump(move->GetDestination());
+ message_ << ")";
}
virtual void EmitSwap(size_t index) {
@@ -43,11 +53,11 @@
if (!message_.str().empty()) {
message_ << " ";
}
- message_ << "("
- << move->GetSource().reg()
- << " <-> "
- << move->GetDestination().reg()
- << ")";
+ message_ << "(";
+ Dump(move->GetSource());
+ message_ << " <-> ";
+ Dump(move->GetDestination());
+ message_ << ")";
}
virtual void SpillScratch(int reg ATTRIBUTE_UNUSED) {}
@@ -69,10 +79,10 @@
size_t number_of_moves) {
HParallelMove* moves = new (allocator) HParallelMove(allocator);
for (size_t i = 0; i < number_of_moves; ++i) {
- moves->AddMove(new (allocator) MoveOperands(
+ moves->AddMove(
Location::RegisterLocation(operands[i][0]),
Location::RegisterLocation(operands[i][1]),
- nullptr));
+ nullptr);
}
return moves;
}
@@ -116,16 +126,158 @@
{
TestParallelMoveResolver resolver(&allocator);
- static constexpr size_t moves[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 1}};
+ static constexpr size_t moves[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 0}};
resolver.EmitNativeCode(BuildParallelMove(&allocator, moves, arraysize(moves)));
- ASSERT_STREQ("(4 <-> 1) (3 <-> 4) (2 <-> 3) (0 -> 1)", resolver.GetMessage().c_str());
+ ASSERT_STREQ("(4 <-> 0) (3 <-> 4) (2 <-> 3) (1 <-> 2)", resolver.GetMessage().c_str());
+ }
+}
+
+TEST(ParallelMoveTest, ConstantLast) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+ TestParallelMoveResolver resolver(&allocator);
+ HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+ moves->AddMove(
+ Location::ConstantLocation(new (&allocator) HIntConstant(0)),
+ Location::RegisterLocation(0),
+ nullptr);
+ moves->AddMove(
+ Location::RegisterLocation(1),
+ Location::RegisterLocation(2),
+ nullptr);
+ resolver.EmitNativeCode(moves);
+ ASSERT_STREQ("(1 -> 2) (C -> 0)", resolver.GetMessage().c_str());
+}
+
+TEST(ParallelMoveTest, Pairs) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ {
+ TestParallelMoveResolver resolver(&allocator);
+ HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+ moves->AddMove(
+ Location::RegisterLocation(2),
+ Location::RegisterLocation(4),
+ nullptr);
+ moves->AddMove(
+ Location::RegisterPairLocation(0, 1),
+ Location::RegisterPairLocation(2, 3),
+ nullptr);
+ resolver.EmitNativeCode(moves);
+ ASSERT_STREQ("(2 -> 4) (0,1 -> 2,3)", resolver.GetMessage().c_str());
}
{
TestParallelMoveResolver resolver(&allocator);
- static constexpr size_t moves[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 1}, {5, 4}};
- resolver.EmitNativeCode(BuildParallelMove(&allocator, moves, arraysize(moves)));
- ASSERT_STREQ("(4 <-> 1) (3 <-> 4) (2 <-> 3) (0 -> 1) (5 -> 4)", resolver.GetMessage().c_str());
+ HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+ moves->AddMove(
+ Location::RegisterPairLocation(0, 1),
+ Location::RegisterPairLocation(2, 3),
+ nullptr);
+ moves->AddMove(
+ Location::RegisterLocation(2),
+ Location::RegisterLocation(4),
+ nullptr);
+ resolver.EmitNativeCode(moves);
+ ASSERT_STREQ("(2 -> 4) (0,1 -> 2,3)", resolver.GetMessage().c_str());
+ }
+
+ {
+ TestParallelMoveResolver resolver(&allocator);
+ HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+ moves->AddMove(
+ Location::RegisterPairLocation(0, 1),
+ Location::RegisterPairLocation(2, 3),
+ nullptr);
+ moves->AddMove(
+ Location::RegisterLocation(2),
+ Location::RegisterLocation(0),
+ nullptr);
+ resolver.EmitNativeCode(moves);
+ ASSERT_STREQ("(0,1 <-> 2,3)", resolver.GetMessage().c_str());
+ }
+ {
+ TestParallelMoveResolver resolver(&allocator);
+ HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+ moves->AddMove(
+ Location::RegisterLocation(2),
+ Location::RegisterLocation(7),
+ nullptr);
+ moves->AddMove(
+ Location::RegisterLocation(7),
+ Location::RegisterLocation(1),
+ nullptr);
+ moves->AddMove(
+ Location::RegisterPairLocation(0, 1),
+ Location::RegisterPairLocation(2, 3),
+ nullptr);
+ resolver.EmitNativeCode(moves);
+ ASSERT_STREQ("(0,1 <-> 2,3) (7 -> 1) (0 -> 7)", resolver.GetMessage().c_str());
+ }
+ {
+ TestParallelMoveResolver resolver(&allocator);
+ HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+ moves->AddMove(
+ Location::RegisterLocation(2),
+ Location::RegisterLocation(7),
+ nullptr);
+ moves->AddMove(
+ Location::RegisterPairLocation(0, 1),
+ Location::RegisterPairLocation(2, 3),
+ nullptr);
+ moves->AddMove(
+ Location::RegisterLocation(7),
+ Location::RegisterLocation(1),
+ nullptr);
+ resolver.EmitNativeCode(moves);
+ ASSERT_STREQ("(0,1 <-> 2,3) (7 -> 1) (0 -> 7)", resolver.GetMessage().c_str());
+ }
+ {
+ TestParallelMoveResolver resolver(&allocator);
+ HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+ moves->AddMove(
+ Location::RegisterPairLocation(0, 1),
+ Location::RegisterPairLocation(2, 3),
+ nullptr);
+ moves->AddMove(
+ Location::RegisterLocation(2),
+ Location::RegisterLocation(7),
+ nullptr);
+ moves->AddMove(
+ Location::RegisterLocation(7),
+ Location::RegisterLocation(1),
+ nullptr);
+ resolver.EmitNativeCode(moves);
+ ASSERT_STREQ("(0,1 <-> 2,3) (7 -> 1) (0 -> 7)", resolver.GetMessage().c_str());
+ }
+ {
+ TestParallelMoveResolver resolver(&allocator);
+ HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+ moves->AddMove(
+ Location::RegisterPairLocation(0, 1),
+ Location::RegisterPairLocation(2, 3),
+ nullptr);
+ moves->AddMove(
+ Location::RegisterPairLocation(2, 3),
+ Location::RegisterPairLocation(0, 1),
+ nullptr);
+ resolver.EmitNativeCode(moves);
+ ASSERT_STREQ("(2,3 <-> 0,1)", resolver.GetMessage().c_str());
+ }
+ {
+ TestParallelMoveResolver resolver(&allocator);
+ HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+ moves->AddMove(
+ Location::RegisterPairLocation(2, 3),
+ Location::RegisterPairLocation(0, 1),
+ nullptr);
+ moves->AddMove(
+ Location::RegisterPairLocation(0, 1),
+ Location::RegisterPairLocation(2, 3),
+ nullptr);
+ resolver.EmitNativeCode(moves);
+ ASSERT_STREQ("(0,1 <-> 2,3)", resolver.GetMessage().c_str());
}
}
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 7186dbe..12acd08 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -55,11 +55,10 @@
void PrepareForRegisterAllocation::VisitCondition(HCondition* condition) {
bool needs_materialization = false;
- if (!condition->HasOnlyOneUse()) {
+ if (!condition->GetUses().HasOnlyOneUse()) {
needs_materialization = true;
} else {
- HUseListNode<HInstruction>* uses = condition->GetUses();
- HInstruction* user = uses->GetUser();
+ HInstruction* user = condition->GetUses().GetFirst()->GetUser();
if (!user->IsIf()) {
needs_materialization = true;
} else {
diff --git a/compiler/optimizing/pretty_printer.h b/compiler/optimizing/pretty_printer.h
index 2c8166e..d2a21c8 100644
--- a/compiler/optimizing/pretty_printer.h
+++ b/compiler/optimizing/pretty_printer.h
@@ -55,7 +55,7 @@
if (instruction->HasUses()) {
PrintString(" [");
bool first = true;
- for (HUseIterator<HInstruction> it(instruction->GetUses()); !it.Done(); it.Advance()) {
+ for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
if (first) {
first = false;
} else {
diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc
index da6b294..9cf8235 100644
--- a/compiler/optimizing/pretty_printer_test.cc
+++ b/compiler/optimizing/pretty_printer_test.cc
@@ -30,10 +30,11 @@
static void TestCode(const uint16_t* data, const char* expected) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraphBuilder builder(&allocator);
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+ HGraphBuilder builder(graph);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- HGraph* graph = builder.BuildGraph(*item);
- ASSERT_NE(graph, nullptr);
+ bool graph_built = builder.BuildGraph(*item);
+ ASSERT_TRUE(graph_built);
StringPrettyPrinter printer(graph);
printer.VisitInsertionOrder();
ASSERT_STREQ(expected, printer.str().c_str());
@@ -100,17 +101,16 @@
TEST(PrettyPrinterTest, CFG3) {
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 5: SuspendCheck\n"
- " 6: Goto 1\n"
+ " 4: SuspendCheck\n"
+ " 5: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 3\n"
" 0: Goto 3\n"
"BasicBlock 2, pred: 3, succ: 4\n"
" 1: ReturnVoid\n"
"BasicBlock 3, pred: 1, succ: 2\n"
- " 2: SuspendCheck\n"
- " 3: Goto 2\n"
+ " 2: Goto 2\n"
"BasicBlock 4, pred: 2\n"
- " 4: Exit\n";
+ " 3: Exit\n";
const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM(
Instruction::GOTO | 0x200,
@@ -160,15 +160,14 @@
TEST(PrettyPrinterTest, CFG5) {
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 4: SuspendCheck\n"
- " 5: Goto 1\n"
+ " 3: SuspendCheck\n"
+ " 4: Goto 1\n"
"BasicBlock 1, pred: 0, 2, succ: 3\n"
" 0: ReturnVoid\n"
"BasicBlock 2, succ: 1\n"
- " 1: SuspendCheck\n"
- " 2: Goto 1\n"
+ " 1: Goto 1\n"
"BasicBlock 3, pred: 1\n"
- " 3: Exit\n";
+ " 2: Exit\n";
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
Instruction::RETURN_VOID,
diff --git a/compiler/optimizing/ssa_type_propagation.cc b/compiler/optimizing/primitive_type_propagation.cc
similarity index 88%
rename from compiler/optimizing/ssa_type_propagation.cc
rename to compiler/optimizing/primitive_type_propagation.cc
index cb5ce20..7e274f6 100644
--- a/compiler/optimizing/ssa_type_propagation.cc
+++ b/compiler/optimizing/primitive_type_propagation.cc
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#include "ssa_builder.h"
-#include "ssa_type_propagation.h"
+#include "primitive_type_propagation.h"
#include "nodes.h"
+#include "ssa_builder.h"
namespace art {
@@ -39,7 +39,7 @@
// Re-compute and update the type of the instruction. Returns
// whether or not the type was changed.
-bool SsaTypePropagation::UpdateType(HPhi* phi) {
+bool PrimitiveTypePropagation::UpdateType(HPhi* phi) {
Primitive::Type existing = phi->GetType();
Primitive::Type new_type = existing;
@@ -67,14 +67,14 @@
return existing != new_type;
}
-void SsaTypePropagation::Run() {
+void PrimitiveTypePropagation::Run() {
for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
VisitBasicBlock(it.Current());
}
ProcessWorklist();
}
-void SsaTypePropagation::VisitBasicBlock(HBasicBlock* block) {
+void PrimitiveTypePropagation::VisitBasicBlock(HBasicBlock* block) {
if (block->IsLoopHeader()) {
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
HPhi* phi = it.Current()->AsPhi();
@@ -100,7 +100,7 @@
}
}
-void SsaTypePropagation::ProcessWorklist() {
+void PrimitiveTypePropagation::ProcessWorklist() {
while (!worklist_.IsEmpty()) {
HPhi* instruction = worklist_.Pop();
if (UpdateType(instruction)) {
@@ -109,12 +109,12 @@
}
}
-void SsaTypePropagation::AddToWorklist(HPhi* instruction) {
+void PrimitiveTypePropagation::AddToWorklist(HPhi* instruction) {
worklist_.Add(instruction);
}
-void SsaTypePropagation::AddDependentInstructionsToWorklist(HPhi* instruction) {
- for (HUseIterator<HInstruction> it(instruction->GetUses()); !it.Done(); it.Advance()) {
+void PrimitiveTypePropagation::AddDependentInstructionsToWorklist(HPhi* instruction) {
+ for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
HPhi* phi = it.Current()->GetUser()->AsPhi();
if (phi != nullptr) {
AddToWorklist(phi);
diff --git a/compiler/optimizing/ssa_type_propagation.h b/compiler/optimizing/primitive_type_propagation.h
similarity index 72%
rename from compiler/optimizing/ssa_type_propagation.h
rename to compiler/optimizing/primitive_type_propagation.h
index f4d3d63..1374cbb 100644
--- a/compiler/optimizing/ssa_type_propagation.h
+++ b/compiler/optimizing/primitive_type_propagation.h
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_OPTIMIZING_SSA_TYPE_PROPAGATION_H_
-#define ART_COMPILER_OPTIMIZING_SSA_TYPE_PROPAGATION_H_
+#ifndef ART_COMPILER_OPTIMIZING_PRIMITIVE_TYPE_PROPAGATION_H_
+#define ART_COMPILER_OPTIMIZING_PRIMITIVE_TYPE_PROPAGATION_H_
#include "nodes.h"
namespace art {
-// Compute and propagate types of phis in the graph.
-class SsaTypePropagation : public ValueObject {
+// Compute and propagate primitive types of phis in the graph.
+class PrimitiveTypePropagation : public ValueObject {
public:
- explicit SsaTypePropagation(HGraph* graph)
+ explicit PrimitiveTypePropagation(HGraph* graph)
: graph_(graph), worklist_(graph->GetArena(), kDefaultWorklistSize) {}
void Run();
@@ -41,9 +41,9 @@
static constexpr size_t kDefaultWorklistSize = 8;
- DISALLOW_COPY_AND_ASSIGN(SsaTypePropagation);
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveTypePropagation);
};
} // namespace art
-#endif // ART_COMPILER_OPTIMIZING_SSA_TYPE_PROPAGATION_H_
+#endif // ART_COMPILER_OPTIMIZING_PRIMITIVE_TYPE_PROPAGATION_H_
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
new file mode 100644
index 0000000..24e6837
--- /dev/null
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#include "reference_type_propagation.h"
+
+namespace art {
+
+// TODO: Only do the analysis on reference types. We currently have to handle
+// the `null` constant, that is represented as a `HIntConstant` and therefore
+// has the Primitive::kPrimInt type.
+
+void ReferenceTypePropagation::Run() {
+ // Compute null status for instructions.
+
+ // To properly propagate not-null info we need to visit in the dominator-based order.
+ // Reverse post order guarantees a node's dominators are visited first.
+ // We take advantage of this order in `VisitBasicBlock`.
+ for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ VisitBasicBlock(it.Current());
+ }
+ ProcessWorklist();
+}
+
+// Re-computes and updates the nullability of the instruction. Returns whether or
+// not the nullability was changed.
+bool ReferenceTypePropagation::UpdateNullability(HPhi* phi) {
+ bool existing_can_be_null = phi->CanBeNull();
+ bool new_can_be_null = false;
+ for (size_t i = 0; i < phi->InputCount(); i++) {
+ new_can_be_null |= phi->InputAt(i)->CanBeNull();
+ }
+ phi->SetCanBeNull(new_can_be_null);
+
+ return existing_can_be_null != new_can_be_null;
+}
+
+
+void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
+ if (block->IsLoopHeader()) {
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ // Set the initial type for the phi. Use the non back edge input for reaching
+ // a fixed point faster.
+ HPhi* phi = it.Current()->AsPhi();
+ AddToWorklist(phi);
+ phi->SetCanBeNull(phi->InputAt(0)->CanBeNull());
+ }
+ } else {
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ // Eagerly compute the type of the phi, for quicker convergence. Note
+ // that we don't need to add users to the worklist because we are
+ // doing a reverse post-order visit, therefore either the phi users are
+ // non-loop phi and will be visited later in the visit, or are loop-phis,
+ // and they are already in the work list.
+ UpdateNullability(it.Current()->AsPhi());
+ }
+ }
+}
+
+void ReferenceTypePropagation::ProcessWorklist() {
+ while (!worklist_.IsEmpty()) {
+ HPhi* instruction = worklist_.Pop();
+ if (UpdateNullability(instruction)) {
+ AddDependentInstructionsToWorklist(instruction);
+ }
+ }
+}
+
+void ReferenceTypePropagation::AddToWorklist(HPhi* instruction) {
+ worklist_.Add(instruction);
+}
+
+void ReferenceTypePropagation::AddDependentInstructionsToWorklist(HPhi* instruction) {
+ for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
+ HPhi* phi = it.Current()->GetUser()->AsPhi();
+ if (phi != nullptr) {
+ AddToWorklist(phi);
+ }
+ }
+}
+
+} // namespace art
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
new file mode 100644
index 0000000..a74319d
--- /dev/null
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -0,0 +1,53 @@
+/*
+ * 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_REFERENCE_TYPE_PROPAGATION_H_
+#define ART_COMPILER_OPTIMIZING_REFERENCE_TYPE_PROPAGATION_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+/**
+ * Propagates reference types to instructions.
+ * TODO: Currently only nullability is computed.
+ */
+class ReferenceTypePropagation : public HOptimization {
+ public:
+ explicit ReferenceTypePropagation(HGraph* graph)
+ : HOptimization(graph, true, "reference_type_propagation"),
+ worklist_(graph->GetArena(), kDefaultWorklistSize) {}
+
+ void Run() OVERRIDE;
+
+ private:
+ void VisitBasicBlock(HBasicBlock* block);
+ void ProcessWorklist();
+ void AddToWorklist(HPhi* phi);
+ void AddDependentInstructionsToWorklist(HPhi* phi);
+ bool UpdateNullability(HPhi* phi);
+
+ GrowableArray<HPhi*> worklist_;
+
+ static constexpr size_t kDefaultWorklistSize = 8;
+
+ DISALLOW_COPY_AND_ASSIGN(ReferenceTypePropagation);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_REFERENCE_TYPE_PROPAGATION_H_
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index a6c0635..bfbe63f 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -27,6 +27,12 @@
static constexpr size_t kMaxLifetimePosition = -1;
static constexpr size_t kDefaultNumberOfSpillSlots = 4;
+// For simplicity, we implement register pairs as (reg, reg + 1).
+// Note that this is a requirement for double registers on ARM, since we
+// allocate SRegister.
+static int GetHighForLowRegister(int reg) { return reg + 1; }
+static bool IsLowRegister(int reg) { return (reg & 1) == 0; }
+
RegisterAllocator::RegisterAllocator(ArenaAllocator* allocator,
CodeGenerator* codegen,
const SsaLivenessAnalysis& liveness)
@@ -50,8 +56,10 @@
blocked_core_registers_(codegen->GetBlockedCoreRegisters()),
blocked_fp_registers_(codegen->GetBlockedFloatingPointRegisters()),
reserved_out_slots_(0),
- maximum_number_of_live_registers_(0) {
- codegen->SetupBlockedRegisters();
+ maximum_number_of_live_core_registers_(0),
+ maximum_number_of_live_fp_registers_(0) {
+ static constexpr bool kIsBaseline = false;
+ codegen->SetupBlockedRegisters(kIsBaseline);
physical_core_register_intervals_.SetSize(codegen->GetNumberOfCoreRegisters());
physical_fp_register_intervals_.SetSize(codegen->GetNumberOfFloatingPointRegisters());
// Always reserve for the current method and the graph's max out registers.
@@ -64,15 +72,18 @@
if (!Supports(instruction_set)) {
return false;
}
+ if (instruction_set == kArm64
+ || instruction_set == kX86_64
+ || instruction_set == kArm
+ || instruction_set == kThumb2) {
+ return true;
+ }
for (size_t i = 0, e = graph.GetBlocks().Size(); i < e; ++i) {
for (HInstructionIterator it(graph.GetBlocks().Get(i)->GetInstructions());
!it.Done();
it.Advance()) {
HInstruction* current = it.Current();
- if (current->GetType() == Primitive::kPrimLong && instruction_set != kX86_64) return false;
- if ((current->GetType() == Primitive::kPrimFloat
- || current->GetType() == Primitive::kPrimDouble)
- && instruction_set != kX86_64) {
+ if (instruction_set == kX86 && current->GetType() == Primitive::kPrimLong) {
return false;
}
}
@@ -128,7 +139,7 @@
: physical_fp_register_intervals_.Get(reg);
Primitive::Type type = location.IsRegister()
? Primitive::kPrimInt
- : Primitive::kPrimDouble;
+ : Primitive::kPrimFloat;
if (interval == nullptr) {
interval = LiveInterval::MakeFixedInterval(allocator_, reg, type);
if (location.IsRegister()) {
@@ -171,9 +182,6 @@
}
LinearScan();
- size_t saved_maximum_number_of_live_registers = maximum_number_of_live_registers_;
- maximum_number_of_live_registers_ = 0;
-
inactive_.Reset();
active_.Reset();
handled_.Reset();
@@ -193,7 +201,6 @@
}
}
LinearScan();
- maximum_number_of_live_registers_ += saved_maximum_number_of_live_registers;
}
void RegisterAllocator::ProcessInstruction(HInstruction* instruction) {
@@ -224,6 +231,12 @@
LiveInterval::MakeTempInterval(allocator_, Primitive::kPrimDouble);
temp_intervals_.Add(interval);
interval->AddRange(position, position + 1);
+ if (codegen_->NeedsTwoRegisters(Primitive::kPrimDouble)) {
+ interval->AddHighInterval(true);
+ LiveInterval* high = interval->GetHighInterval();
+ temp_intervals_.Add(high);
+ unhandled_fp_intervals_.Add(high);
+ }
unhandled_fp_intervals_.Add(interval);
break;
}
@@ -239,8 +252,13 @@
&& (instruction->GetType() != Primitive::kPrimFloat);
if (locations->CanCall()) {
- if (!instruction->IsSuspendCheck()) {
- codegen_->MarkNotLeaf();
+ if (codegen_->IsLeafMethod()) {
+ // TODO: We do this here because we do not want the suspend check to artificially
+ // create live registers. We should find another place, but this is currently the
+ // simplest.
+ DCHECK(instruction->IsSuspendCheckEntry());
+ instruction->GetBlock()->RemoveInstruction(instruction);
+ return;
}
safepoints_.Add(instruction);
if (locations->OnlyCallsOnSlowPath()) {
@@ -262,14 +280,18 @@
if (locations->WillCall()) {
// Block all registers.
for (size_t i = 0; i < codegen_->GetNumberOfCoreRegisters(); ++i) {
- BlockRegister(Location::RegisterLocation(i),
- position,
- position + 1);
+ if (!codegen_->IsCoreCalleeSaveRegister(i)) {
+ BlockRegister(Location::RegisterLocation(i),
+ position,
+ position + 1);
+ }
}
for (size_t i = 0; i < codegen_->GetNumberOfFloatingPointRegisters(); ++i) {
- BlockRegister(Location::FpuRegisterLocation(i),
- position,
- position + 1);
+ if (!codegen_->IsFloatingPointCalleeSaveRegister(i)) {
+ BlockRegister(Location::FpuRegisterLocation(i),
+ position,
+ position + 1);
+ }
}
}
@@ -277,6 +299,9 @@
Location input = locations->InAt(i);
if (input.IsRegister() || input.IsFpuRegister()) {
BlockRegister(input, position, position + 1);
+ } else if (input.IsPair()) {
+ BlockRegister(input.ToLow(), position, position + 1);
+ BlockRegister(input.ToHigh(), position, position + 1);
}
}
@@ -289,6 +314,10 @@
DCHECK(unhandled.IsEmpty() || current->StartsBeforeOrAt(unhandled.Peek()));
+ if (codegen_->NeedsTwoRegisters(current->GetType())) {
+ current->AddHighInterval();
+ }
+
// Some instructions define their output in fixed register/stack slot. We need
// to ensure we know these locations before doing register allocation. For a
// given register, we create an interval that covers these locations. The register
@@ -302,14 +331,30 @@
if (first.IsRegister() || first.IsFpuRegister()) {
current->SetFrom(position + 1);
current->SetRegister(first.reg());
+ } else if (first.IsPair()) {
+ current->SetFrom(position + 1);
+ current->SetRegister(first.low());
+ LiveInterval* high = current->GetHighInterval();
+ high->SetRegister(first.high());
+ high->SetFrom(position + 1);
}
} else if (output.IsRegister() || output.IsFpuRegister()) {
// Shift the interval's start by one to account for the blocked register.
current->SetFrom(position + 1);
current->SetRegister(output.reg());
BlockRegister(output, position, position + 1);
+ } else if (output.IsPair()) {
+ current->SetFrom(position + 1);
+ current->SetRegister(output.low());
+ LiveInterval* high = current->GetHighInterval();
+ high->SetRegister(output.high());
+ high->SetFrom(position + 1);
+ BlockRegister(output.ToLow(), position, position + 1);
+ BlockRegister(output.ToHigh(), position, position + 1);
} else if (output.IsStackSlot() || output.IsDoubleStackSlot()) {
current->SetSpillSlot(output.GetStackIndex());
+ } else {
+ DCHECK(output.IsUnallocated() || output.IsConstant());
}
// If needed, add interval to the list of unhandled intervals.
@@ -445,6 +490,9 @@
BitVector* liveness_of_register = liveness_of_values.Get(current->GetRegister());
for (size_t j = it.CurrentRange()->GetStart(); j < it.CurrentRange()->GetEnd(); ++j) {
if (liveness_of_register->IsBitSet(j)) {
+ if (current->IsUsingInputRegister() && current->CanUseInputRegister()) {
+ continue;
+ }
if (log_fatal_on_failure) {
std::ostringstream message;
message << "Register conflict at " << j << " ";
@@ -514,6 +562,7 @@
LiveInterval* current = unhandled_->Pop();
DCHECK(!current->IsFixed() && !current->HasSpillSlot());
DCHECK(unhandled_->IsEmpty() || unhandled_->Peek()->GetStart() >= current->GetStart());
+ DCHECK(!current->IsLowInterval() || unhandled_->Peek()->IsHighInterval());
size_t position = current->GetStart();
@@ -558,12 +607,24 @@
if (current->IsSlowPathSafepoint()) {
// Synthesized interval to record the maximum number of live registers
// at safepoints. No need to allocate a register for it.
- maximum_number_of_live_registers_ =
- std::max(maximum_number_of_live_registers_, active_.Size());
+ if (processing_core_registers_) {
+ maximum_number_of_live_core_registers_ =
+ std::max(maximum_number_of_live_core_registers_, active_.Size());
+ } else {
+ maximum_number_of_live_fp_registers_ =
+ std::max(maximum_number_of_live_fp_registers_, active_.Size());
+ }
DCHECK(unhandled_->IsEmpty() || unhandled_->Peek()->GetStart() > current->GetStart());
continue;
}
+ if (current->IsHighInterval() && !current->GetLowInterval()->HasRegister()) {
+ DCHECK(!current->HasRegister());
+ // Allocating the low part was unsucessful. The splitted interval for the high part
+ // will be handled next (it is in the `unhandled_` list).
+ continue;
+ }
+
// (4) Try to find an available register.
bool success = TryAllocateFreeReg(current);
@@ -575,7 +636,36 @@
// (6) If the interval had a register allocated, add it to the list of active
// intervals.
if (success) {
+ codegen_->AddAllocatedRegister(processing_core_registers_
+ ? Location::RegisterLocation(current->GetRegister())
+ : Location::FpuRegisterLocation(current->GetRegister()));
active_.Add(current);
+ if (current->HasHighInterval() && !current->GetHighInterval()->HasRegister()) {
+ current->GetHighInterval()->SetRegister(GetHighForLowRegister(current->GetRegister()));
+ }
+ }
+ }
+}
+
+static void FreeIfNotCoverAt(LiveInterval* interval, size_t position, size_t* free_until) {
+ DCHECK(!interval->IsHighInterval());
+ // Note that the same instruction may occur multiple times in the input list,
+ // so `free_until` may have changed already.
+ if (interval->IsDeadAt(position)) {
+ // Set the register to be free. Note that inactive intervals might later
+ // update this.
+ free_until[interval->GetRegister()] = kMaxLifetimePosition;
+ if (interval->HasHighInterval()) {
+ DCHECK(interval->GetHighInterval()->IsDeadAt(position));
+ free_until[interval->GetHighInterval()->GetRegister()] = kMaxLifetimePosition;
+ }
+ } else if (!interval->Covers(position)) {
+ // The interval becomes inactive at `defined_by`. We make its register
+ // available only until the next use strictly after `defined_by`.
+ free_until[interval->GetRegister()] = interval->FirstUseAfter(position);
+ if (interval->HasHighInterval()) {
+ DCHECK(!interval->GetHighInterval()->Covers(position));
+ free_until[interval->GetHighInterval()->GetRegister()] = free_until[interval->GetRegister()];
}
}
}
@@ -597,6 +687,32 @@
free_until[interval->GetRegister()] = 0;
}
+ // An interval that starts an instruction (that is, it is not split), may
+ // re-use the registers used by the inputs of that instruciton, based on the
+ // location summary.
+ HInstruction* defined_by = current->GetDefinedBy();
+ if (defined_by != nullptr && !current->IsSplit()) {
+ LocationSummary* locations = defined_by->GetLocations();
+ if (!locations->OutputCanOverlapWithInputs() && locations->Out().IsUnallocated()) {
+ for (HInputIterator it(defined_by); !it.Done(); it.Advance()) {
+ // Take the last interval of the input. It is the location of that interval
+ // that will be used at `defined_by`.
+ LiveInterval* interval = it.Current()->GetLiveInterval()->GetLastSibling();
+ // Note that interval may have not been processed yet.
+ // TODO: Handle non-split intervals last in the work list.
+ if (interval->HasRegister() && interval->SameRegisterKind(*current)) {
+ // The input must be live until the end of `defined_by`, to comply to
+ // the linear scan algorithm. So we use `defined_by`'s end lifetime
+ // position to check whether the input is dead or is inactive after
+ // `defined_by`.
+ DCHECK(interval->Covers(defined_by->GetLifetimePosition()));
+ size_t position = defined_by->GetLifetimePosition() + 1;
+ FreeIfNotCoverAt(interval, position, free_until);
+ }
+ }
+ }
+ }
+
// For each inactive interval, set its register to be free until
// the next intersection with `current`.
for (size_t i = 0, e = inactive_.Size(); i < e; ++i) {
@@ -624,30 +740,35 @@
}
}
- int reg = -1;
+ int reg = kNoRegister;
if (current->HasRegister()) {
// Some instructions have a fixed register output.
reg = current->GetRegister();
- DCHECK_NE(free_until[reg], 0u);
+ if (free_until[reg] == 0) {
+ DCHECK(current->IsHighInterval());
+ // AllocateBlockedReg will spill the holder of the register.
+ return false;
+ }
} else {
+ DCHECK(!current->IsHighInterval());
int hint = current->FindFirstRegisterHint(free_until);
if (hint != kNoRegister) {
DCHECK(!IsBlocked(hint));
reg = hint;
+ } else if (current->IsLowInterval()) {
+ reg = FindAvailableRegisterPair(free_until, current->GetStart());
} else {
- // Pick the register that is free the longest.
- for (size_t i = 0; i < number_of_registers_; ++i) {
- if (IsBlocked(i)) continue;
- if (reg == -1 || free_until[i] > free_until[reg]) {
- reg = i;
- if (free_until[i] == kMaxLifetimePosition) break;
- }
- }
+ reg = FindAvailableRegister(free_until);
}
}
+ DCHECK_NE(reg, kNoRegister);
// If we could not find a register, we need to spill.
- if (reg == -1 || free_until[reg] == 0) {
+ if (free_until[reg] == 0) {
+ return false;
+ }
+
+ if (current->IsLowInterval() && free_until[GetHighForLowRegister(reg)] == 0) {
return false;
}
@@ -669,6 +790,66 @@
: blocked_fp_registers_[reg];
}
+int RegisterAllocator::FindAvailableRegisterPair(size_t* next_use, size_t starting_at) const {
+ int reg = kNoRegister;
+ // Pick the register pair that is used the last.
+ for (size_t i = 0; i < number_of_registers_; ++i) {
+ if (IsBlocked(i)) continue;
+ if (!IsLowRegister(i)) continue;
+ int high_register = GetHighForLowRegister(i);
+ if (IsBlocked(high_register)) continue;
+ int existing_high_register = GetHighForLowRegister(reg);
+ if ((reg == kNoRegister) || (next_use[i] >= next_use[reg]
+ && next_use[high_register] >= next_use[existing_high_register])) {
+ reg = i;
+ if (next_use[i] == kMaxLifetimePosition
+ && next_use[high_register] == kMaxLifetimePosition) {
+ break;
+ }
+ } else if (next_use[reg] <= starting_at || next_use[existing_high_register] <= starting_at) {
+ // If one of the current register is known to be unavailable, just unconditionally
+ // try a new one.
+ reg = i;
+ }
+ }
+ return reg;
+}
+
+int RegisterAllocator::FindAvailableRegister(size_t* next_use) const {
+ int reg = kNoRegister;
+ // Pick the register that is used the last.
+ for (size_t i = 0; i < number_of_registers_; ++i) {
+ if (IsBlocked(i)) continue;
+ if (reg == kNoRegister || next_use[i] > next_use[reg]) {
+ reg = i;
+ if (next_use[i] == kMaxLifetimePosition) break;
+ }
+ }
+ return reg;
+}
+
+bool RegisterAllocator::TrySplitNonPairIntervalAt(size_t position,
+ size_t first_register_use,
+ size_t* next_use) {
+ for (size_t i = 0, e = active_.Size(); i < e; ++i) {
+ LiveInterval* active = active_.Get(i);
+ DCHECK(active->HasRegister());
+ // Split the first interval found.
+ if (first_register_use <= next_use[active->GetRegister()]
+ && !active->IsLowInterval()
+ && !active->IsHighInterval()) {
+ LiveInterval* split = Split(active, position);
+ active_.DeleteAt(i);
+ if (split != active) {
+ handled_.Add(active);
+ }
+ AddSorted(unhandled_, split);
+ return true;
+ }
+ }
+ return false;
+}
+
// Find the register that is used the last, and spill the interval
// that holds it. If the first use of `current` is after that register
// we spill `current` instead.
@@ -729,24 +910,50 @@
}
}
- // Pick the register that is used the last.
- int reg = -1;
- for (size_t i = 0; i < number_of_registers_; ++i) {
- if (IsBlocked(i)) continue;
- if (reg == -1 || next_use[i] > next_use[reg]) {
- reg = i;
- if (next_use[i] == kMaxLifetimePosition) break;
- }
+ int reg = kNoRegister;
+ bool should_spill = false;
+ if (current->HasRegister()) {
+ DCHECK(current->IsHighInterval());
+ reg = current->GetRegister();
+ // When allocating the low part, we made sure the high register was available.
+ DCHECK_LT(first_register_use, next_use[reg]);
+ } else if (current->IsLowInterval()) {
+ reg = FindAvailableRegisterPair(next_use, current->GetStart());
+ // We should spill if both registers are not available.
+ should_spill = (first_register_use >= next_use[reg])
+ || (first_register_use >= next_use[GetHighForLowRegister(reg)]);
+ } else {
+ DCHECK(!current->IsHighInterval());
+ reg = FindAvailableRegister(next_use);
+ should_spill = (first_register_use >= next_use[reg]);
}
- if (first_register_use >= next_use[reg]) {
- // If the first use of that instruction is after the last use of the found
- // register, we split this interval just before its first register use.
- AllocateSpillSlotFor(current);
- LiveInterval* split = Split(current, first_register_use - 1);
- DCHECK_NE(current, split) << "There is not enough registers available for "
- << split->GetParent()->GetDefinedBy()->DebugName();
- AddSorted(unhandled_, split);
+ DCHECK_NE(reg, kNoRegister);
+ if (should_spill) {
+ DCHECK(!current->IsHighInterval());
+ bool is_allocation_at_use_site = (current->GetStart() == (first_register_use - 1));
+ if (current->IsLowInterval()
+ && is_allocation_at_use_site
+ && TrySplitNonPairIntervalAt(current->GetStart(), first_register_use, next_use)) {
+ // If we're allocating a register for `current` because the instruction at
+ // that position requires it, but we think we should spill, then there are
+ // non-pair intervals blocking the allocation. We split the first
+ // interval found, and put ourselves first in the `unhandled_` list.
+ LiveInterval* existing = unhandled_->Peek();
+ DCHECK(existing->IsHighInterval());
+ DCHECK_EQ(existing->GetLowInterval(), current);
+ unhandled_->Add(current);
+ } else {
+ // If the first use of that instruction is after the last use of the found
+ // register, we split this interval just before its first register use.
+ AllocateSpillSlotFor(current);
+ LiveInterval* split = Split(current, first_register_use - 1);
+ DCHECK_NE(current, split) << "There is not enough registers available for "
+ << split->GetParent()->GetDefinedBy()->DebugName() << " "
+ << split->GetParent()->GetDefinedBy()->GetId()
+ << " at " << first_register_use - 1;
+ AddSorted(unhandled_, split);
+ }
return false;
} else {
// Use this register and spill the active and inactives interval that
@@ -759,8 +966,27 @@
DCHECK(!active->IsFixed());
LiveInterval* split = Split(active, current->GetStart());
active_.DeleteAt(i);
- handled_.Add(active);
+ if (split != active) {
+ handled_.Add(active);
+ }
AddSorted(unhandled_, split);
+
+ if (active->IsLowInterval() || active->IsHighInterval()) {
+ LiveInterval* other_half = active->IsLowInterval()
+ ? active->GetHighInterval()
+ : active->GetLowInterval();
+ // We also need to remove the other half from the list of actives.
+ bool found = false;
+ for (size_t j = 0; j < active_.Size(); ++j) {
+ if (active_.Get(j) == other_half) {
+ found = true;
+ active_.DeleteAt(j);
+ handled_.Add(other_half);
+ break;
+ }
+ }
+ DCHECK(found);
+ }
break;
}
}
@@ -780,14 +1006,38 @@
if (next_intersection != kNoLifetime) {
if (inactive->IsFixed()) {
LiveInterval* split = Split(current, next_intersection);
+ DCHECK_NE(split, current);
AddSorted(unhandled_, split);
} else {
- LiveInterval* split = Split(inactive, next_intersection);
+ // Split at the start of `current`, which will lead to splitting
+ // at the end of the lifetime hole of `inactive`.
+ LiveInterval* split = Split(inactive, current->GetStart());
+ // If it's inactive, it must start before the current interval.
+ DCHECK_NE(split, inactive);
inactive_.DeleteAt(i);
--i;
--e;
handled_.Add(inactive);
AddSorted(unhandled_, split);
+
+ if (inactive->IsLowInterval() || inactive->IsHighInterval()) {
+ LiveInterval* other_half = inactive->IsLowInterval()
+ ? inactive->GetHighInterval()
+ : inactive->GetLowInterval();
+
+ // We also need to remove the other half from the list of inactives.
+ bool found = false;
+ for (size_t j = 0; j < inactive_.Size(); ++j) {
+ if (inactive_.Get(j) == other_half) {
+ found = true;
+ inactive_.DeleteAt(j);
+ --e;
+ handled_.Add(other_half);
+ break;
+ }
+ }
+ DCHECK(found);
+ }
}
}
}
@@ -802,7 +1052,8 @@
size_t insert_at = 0;
for (size_t i = array->Size(); i > 0; --i) {
LiveInterval* current = array->Get(i - 1);
- if (current->StartsAfter(interval)) {
+ // High intervals must be processed right after their low equivalent.
+ if (current->StartsAfter(interval) && !current->IsHighInterval()) {
insert_at = i;
break;
} else if ((current->GetStart() == interval->GetStart()) && current->IsSlowPathSafepoint()) {
@@ -813,23 +1064,49 @@
break;
}
}
+
array->InsertAt(insert_at, interval);
+ // Insert the high interval before the low, to ensure the low is processed before.
+ if (interval->HasHighInterval()) {
+ array->InsertAt(insert_at, interval->GetHighInterval());
+ } else if (interval->HasLowInterval()) {
+ array->InsertAt(insert_at + 1, interval->GetLowInterval());
+ }
}
LiveInterval* RegisterAllocator::Split(LiveInterval* interval, size_t position) {
- DCHECK(position >= interval->GetStart());
+ DCHECK_GE(position, interval->GetStart());
DCHECK(!interval->IsDeadAt(position));
if (position == interval->GetStart()) {
// Spill slot will be allocated when handling `interval` again.
interval->ClearRegister();
+ if (interval->HasHighInterval()) {
+ interval->GetHighInterval()->ClearRegister();
+ } else if (interval->HasLowInterval()) {
+ interval->GetLowInterval()->ClearRegister();
+ }
return interval;
} else {
LiveInterval* new_interval = interval->SplitAt(position);
+ if (interval->HasHighInterval()) {
+ LiveInterval* high = interval->GetHighInterval()->SplitAt(position);
+ new_interval->SetHighInterval(high);
+ high->SetLowInterval(new_interval);
+ } else if (interval->HasLowInterval()) {
+ LiveInterval* low = interval->GetLowInterval()->SplitAt(position);
+ new_interval->SetLowInterval(low);
+ low->SetHighInterval(new_interval);
+ }
return new_interval;
}
}
void RegisterAllocator::AllocateSpillSlotFor(LiveInterval* interval) {
+ if (interval->IsHighInterval()) {
+ // The low interval will contain the spill slot.
+ return;
+ }
+
LiveInterval* parent = interval->GetParent();
// An instruction gets a spill slot for its entire lifetime. If the parent
@@ -895,7 +1172,9 @@
static bool IsValidDestination(Location destination) {
return destination.IsRegister()
+ || destination.IsRegisterPair()
|| destination.IsFpuRegister()
+ || destination.IsFpuRegisterPair()
|| destination.IsStackSlot()
|| destination.IsDoubleStackSlot();
}
@@ -903,7 +1182,6 @@
void RegisterAllocator::AddInputMoveFor(HInstruction* user,
Location source,
Location destination) const {
- DCHECK(IsValidDestination(destination));
if (source.Equals(destination)) return;
DCHECK(!user->IsPhi());
@@ -920,7 +1198,7 @@
move = previous->AsParallelMove();
}
DCHECK_EQ(move->GetLifetimePosition(), user->GetLifetimePosition());
- move->AddMove(new (allocator_) MoveOperands(source, destination, nullptr));
+ move->AddMove(source, destination, nullptr);
}
static bool IsInstructionStart(size_t position) {
@@ -935,7 +1213,7 @@
HInstruction* instruction,
Location source,
Location destination) const {
- DCHECK(IsValidDestination(destination));
+ DCHECK(IsValidDestination(destination)) << destination;
if (source.Equals(destination)) return;
HInstruction* at = liveness_.GetInstructionFromPosition(position / 2);
@@ -992,14 +1270,14 @@
}
}
DCHECK_EQ(move->GetLifetimePosition(), position);
- move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
+ move->AddMove(source, destination, instruction);
}
void RegisterAllocator::InsertParallelMoveAtExitOf(HBasicBlock* block,
HInstruction* instruction,
Location source,
Location destination) const {
- DCHECK(IsValidDestination(destination));
+ DCHECK(IsValidDestination(destination)) << destination;
if (source.Equals(destination)) return;
DCHECK_EQ(block->GetSuccessors().Size(), 1u);
@@ -1022,14 +1300,14 @@
} else {
move = previous->AsParallelMove();
}
- move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
+ move->AddMove(source, destination, instruction);
}
void RegisterAllocator::InsertParallelMoveAtEntryOf(HBasicBlock* block,
HInstruction* instruction,
Location source,
Location destination) const {
- DCHECK(IsValidDestination(destination));
+ DCHECK(IsValidDestination(destination)) << destination;
if (source.Equals(destination)) return;
HInstruction* first = block->GetFirstInstruction();
@@ -1041,13 +1319,13 @@
move->SetLifetimePosition(block->GetLifetimeStart());
block->InsertInstructionBefore(move, first);
}
- move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
+ move->AddMove(source, destination, instruction);
}
void RegisterAllocator::InsertMoveAfter(HInstruction* instruction,
Location source,
Location destination) const {
- DCHECK(IsValidDestination(destination));
+ DCHECK(IsValidDestination(destination)) << destination;
if (source.Equals(destination)) return;
if (instruction->IsPhi()) {
@@ -1065,7 +1343,7 @@
move->SetLifetimePosition(position);
instruction->GetBlock()->InsertInstructionBefore(move, instruction->GetNext());
}
- move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
+ move->AddMove(source, destination, instruction);
}
void RegisterAllocator::ConnectSiblings(LiveInterval* interval) {
@@ -1073,9 +1351,7 @@
if (current->HasSpillSlot() && current->HasRegister()) {
// We spill eagerly, so move must be at definition.
InsertMoveAfter(interval->GetDefinedBy(),
- interval->IsFloatingPoint()
- ? Location::FpuRegisterLocation(interval->GetRegister())
- : Location::RegisterLocation(interval->GetRegister()),
+ interval->ToLocation(),
interval->NeedsTwoSpillSlots()
? Location::DoubleStackSlot(interval->GetParent()->GetSpillSlot())
: Location::StackSlot(interval->GetParent()->GetSpillSlot()));
@@ -1095,10 +1371,17 @@
locations->SetEnvironmentAt(use->GetInputIndex(), source);
} else {
Location expected_location = locations->InAt(use->GetInputIndex());
- if (expected_location.IsUnallocated()) {
- locations->SetInAt(use->GetInputIndex(), source);
- } else if (!expected_location.IsConstant()) {
- AddInputMoveFor(use->GetUser(), source, expected_location);
+ // The expected (actual) location may be invalid in case the input is unused. Currently
+ // this only happens for intrinsics.
+ if (expected_location.IsValid()) {
+ if (expected_location.IsUnallocated()) {
+ locations->SetInAt(use->GetInputIndex(), source);
+ } else if (!expected_location.IsConstant()) {
+ AddInputMoveFor(use->GetUser(), source, expected_location);
+ }
+ } else {
+ DCHECK(use->GetUser()->IsInvoke());
+ DCHECK(use->GetUser()->AsInvoke()->GetIntrinsic() != Intrinsics::kNone);
}
}
use = use->GetNext();
@@ -1135,8 +1418,11 @@
switch (source.GetKind()) {
case Location::kRegister: {
locations->AddLiveRegister(source);
- DCHECK_LE(locations->GetNumberOfLiveRegisters(), maximum_number_of_live_registers_);
-
+ if (kIsDebugBuild && locations->OnlyCallsOnSlowPath()) {
+ DCHECK_LE(locations->GetNumberOfLiveRegisters(),
+ maximum_number_of_live_core_registers_ +
+ maximum_number_of_live_fp_registers_);
+ }
if (current->GetType() == Primitive::kPrimNot) {
locations->SetRegisterBit(source.reg());
}
@@ -1146,6 +1432,13 @@
locations->AddLiveRegister(source);
break;
}
+
+ case Location::kRegisterPair:
+ case Location::kFpuRegisterPair: {
+ locations->AddLiveRegister(source.ToLow());
+ locations->AddLiveRegister(source.ToHigh());
+ break;
+ }
case Location::kStackSlot: // Fall-through
case Location::kDoubleStackSlot: // Fall-through
case Location::kConstant: {
@@ -1223,8 +1516,11 @@
}
void RegisterAllocator::Resolve() {
- codegen_->ComputeFrameSize(
- spill_slots_.Size(), maximum_number_of_live_registers_, reserved_out_slots_);
+ codegen_->InitializeCodeGeneration(spill_slots_.Size(),
+ maximum_number_of_live_core_registers_,
+ maximum_number_of_live_fp_registers_,
+ reserved_out_slots_,
+ liveness_.GetLinearOrder());
// Adjust the Out Location of instructions.
// TODO: Use pointers of Location inside LiveInterval to avoid doing another iteration.
@@ -1258,7 +1554,7 @@
DCHECK(locations->InAt(0).Equals(source));
}
}
- locations->SetOut(source);
+ locations->UpdateOut(source);
} else {
DCHECK(source.Equals(location));
}
@@ -1305,6 +1601,10 @@
size_t temp_index = 0;
for (size_t i = 0; i < temp_intervals_.Size(); ++i) {
LiveInterval* temp = temp_intervals_.Get(i);
+ if (temp->IsHighInterval()) {
+ // High intervals can be skipped, they are already handled by the low interval.
+ continue;
+ }
HInstruction* at = liveness_.GetTempUser(temp);
if (at != current) {
temp_index = 0;
@@ -1318,14 +1618,14 @@
break;
case Primitive::kPrimDouble:
- // TODO: Support the case of ARM, where a double value
- // requires an FPU register pair (note that the ARM back end
- // does not yet use this register allocator when a method uses
- // floats or doubles).
- DCHECK(codegen_->GetInstructionSet() != kArm
- && codegen_->GetInstructionSet() != kThumb2);
- locations->SetTempAt(
- temp_index++, Location::FpuRegisterLocation(temp->GetRegister()));
+ if (codegen_->NeedsTwoRegisters(Primitive::kPrimDouble)) {
+ Location location = Location::FpuRegisterPairLocation(
+ temp->GetRegister(), temp->GetHighInterval()->GetRegister());
+ locations->SetTempAt(temp_index++, location);
+ } else {
+ locations->SetTempAt(
+ temp_index++, Location::FpuRegisterLocation(temp->GetRegister()));
+ }
break;
default:
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index 976ee39..b8f70bd 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -67,10 +67,11 @@
static bool CanAllocateRegistersFor(const HGraph& graph, InstructionSet instruction_set);
static bool Supports(InstructionSet instruction_set) {
- return instruction_set == kX86
- || instruction_set == kArm
- || instruction_set == kX86_64
- || instruction_set == kThumb2;
+ return instruction_set == kArm
+ || instruction_set == kArm64
+ || instruction_set == kThumb2
+ || instruction_set == kX86
+ || instruction_set == kX86_64;
}
size_t GetNumberOfSpillSlots() const {
@@ -127,6 +128,12 @@
bool ValidateInternal(bool log_fatal_on_failure) const;
void DumpInterval(std::ostream& stream, LiveInterval* interval) const;
void DumpAllIntervals(std::ostream& stream) const;
+ int FindAvailableRegisterPair(size_t* next_use, size_t starting_at) const;
+ int FindAvailableRegister(size_t* next_use) const;
+
+ // Try splitting an active non-pair interval at the given `position`.
+ // Returns whether it was successful at finding such an interval.
+ bool TrySplitNonPairIntervalAt(size_t position, size_t first_register_use, size_t* next_use);
ArenaAllocator* const allocator_;
CodeGenerator* const codegen_;
@@ -187,10 +194,14 @@
// Slots reserved for out arguments.
size_t reserved_out_slots_;
- // The maximum live registers at safepoints.
- size_t maximum_number_of_live_registers_;
+ // The maximum live core registers at safepoints.
+ size_t maximum_number_of_live_core_registers_;
+
+ // The maximum live FP registers at safepoints.
+ size_t maximum_number_of_live_fp_registers_;
ART_FRIEND_TEST(RegisterAllocatorTest, FreeUntil);
+ ART_FRIEND_TEST(RegisterAllocatorTest, SpillInactive);
DISALLOW_COPY_AND_ASSIGN(RegisterAllocator);
};
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index 8d75db9..0cc00c0 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -19,6 +19,7 @@
#include "code_generator_x86.h"
#include "dex_file.h"
#include "dex_instruction.h"
+#include "driver/compiler_options.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "register_allocator.h"
@@ -36,13 +37,12 @@
static bool Check(const uint16_t* data) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraphBuilder builder(&allocator);
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+ HGraphBuilder builder(graph);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- HGraph* graph = builder.BuildGraph(*item);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
- graph->AnalyzeNaturalLoops();
- x86::CodeGeneratorX86 codegen(graph);
+ builder.BuildGraph(*item);
+ graph->TryBuildingSsa();
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
RegisterAllocator register_allocator(&allocator, &codegen, liveness);
@@ -58,7 +58,7 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = new (&allocator) HGraph(&allocator);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
GrowableArray<LiveInterval*> intervals(&allocator, 0);
// Test with two intervals of the same range.
@@ -250,12 +250,11 @@
}
static HGraph* BuildSSAGraph(const uint16_t* data, ArenaAllocator* allocator) {
- HGraphBuilder builder(allocator);
+ HGraph* graph = new (allocator) HGraph(allocator);
+ HGraphBuilder builder(graph);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- HGraph* graph = builder.BuildGraph(*item);
- graph->BuildDominatorTree();
- graph->TransformToSSA();
- graph->AnalyzeNaturalLoops();
+ builder.BuildGraph(*item);
+ graph->TryBuildingSsa();
return graph;
}
@@ -299,7 +298,7 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = BuildSSAGraph(data, &allocator);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
RegisterAllocator register_allocator(&allocator, &codegen, liveness);
@@ -331,7 +330,7 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = BuildSSAGraph(data, &allocator);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -384,7 +383,7 @@
ArenaAllocator allocator(&pool);
HGraph* graph = BuildSSAGraph(data, &allocator);
SsaDeadPhiElimination(graph).Run();
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
RegisterAllocator register_allocator(&allocator, &codegen, liveness);
@@ -406,7 +405,7 @@
ArenaAllocator allocator(&pool);
HGraph* graph = BuildSSAGraph(data, &allocator);
SsaDeadPhiElimination(graph).Run();
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
RegisterAllocator register_allocator(&allocator, &codegen, liveness);
@@ -466,7 +465,7 @@
entry->AddSuccessor(block);
HInstruction* test = new (allocator) HInstanceFieldGet(
- parameter, Primitive::kPrimBoolean, MemberOffset(22));
+ parameter, Primitive::kPrimBoolean, MemberOffset(22), false);
block->AddInstruction(test);
block->AddInstruction(new (allocator) HIf(test));
HBasicBlock* then = new (allocator) HBasicBlock(graph);
@@ -485,8 +484,10 @@
*phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
join->AddPhi(*phi);
- *input1 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt, MemberOffset(42));
- *input2 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt, MemberOffset(42));
+ *input1 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt,
+ MemberOffset(42), false);
+ *input2 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt,
+ MemberOffset(42), false);
then->AddInstruction(*input1);
else_->AddInstruction(*input2);
join->AddInstruction(new (allocator) HExit());
@@ -506,7 +507,7 @@
{
HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -521,13 +522,13 @@
{
HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
// Set the phi to a specific register, and check that the inputs get allocated
// the same register.
- phi->GetLocations()->SetOut(Location::RegisterLocation(2));
+ phi->GetLocations()->UpdateOut(Location::RegisterLocation(2));
RegisterAllocator register_allocator(&allocator, &codegen, liveness);
register_allocator.AllocateRegisters();
@@ -538,13 +539,13 @@
{
HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
// Set input1 to a specific register, and check that the phi and other input get allocated
// the same register.
- input1->GetLocations()->SetOut(Location::RegisterLocation(2));
+ input1->GetLocations()->UpdateOut(Location::RegisterLocation(2));
RegisterAllocator register_allocator(&allocator, &codegen, liveness);
register_allocator.AllocateRegisters();
@@ -555,13 +556,13 @@
{
HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
// Set input2 to a specific register, and check that the phi and other input get allocated
// the same register.
- input2->GetLocations()->SetOut(Location::RegisterLocation(2));
+ input2->GetLocations()->UpdateOut(Location::RegisterLocation(2));
RegisterAllocator register_allocator(&allocator, &codegen, liveness);
register_allocator.AllocateRegisters();
@@ -585,7 +586,8 @@
graph->AddBlock(block);
entry->AddSuccessor(block);
- *field = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt, MemberOffset(42));
+ *field = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt,
+ MemberOffset(42), false);
block->AddInstruction(*field);
*ret = new (allocator) HReturn(*field);
block->AddInstruction(*ret);
@@ -604,7 +606,7 @@
{
HGraph* graph = BuildFieldReturn(&allocator, &field, &ret);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -617,7 +619,7 @@
{
HGraph* graph = BuildFieldReturn(&allocator, &field, &ret);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -666,7 +668,7 @@
{
HGraph* graph = BuildTwoAdds(&allocator, &first_add, &second_add);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -680,12 +682,12 @@
{
HGraph* graph = BuildTwoAdds(&allocator, &first_add, &second_add);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
// check that both adds get the same register.
- // Don't use SetOutput because output is already allocated.
+ // Don't use UpdateOutput because output is already allocated.
first_add->InputAt(0)->GetLocations()->output_ = Location::RegisterLocation(2);
ASSERT_EQ(first_add->GetLocations()->Out().GetPolicy(), Location::kSameAsFirstInput);
ASSERT_EQ(second_add->GetLocations()->Out().GetPolicy(), Location::kSameAsFirstInput);
@@ -727,7 +729,7 @@
{
HGraph* graph = BuildDiv(&allocator, &div);
- x86::CodeGeneratorX86 codegen(graph);
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
@@ -739,4 +741,106 @@
}
}
+// Test a bug in the register allocator, where allocating a blocked
+// register would lead to spilling an inactive interval at the wrong
+// position.
+TEST(RegisterAllocatorTest, SpillInactive) {
+ ArenaPool pool;
+
+ // Create a synthesized graph to please the register_allocator and
+ // ssa_liveness_analysis code.
+ ArenaAllocator allocator(&pool);
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+ HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(entry);
+ graph->SetEntryBlock(entry);
+ HInstruction* one = new (&allocator) HParameterValue(0, Primitive::kPrimInt);
+ HInstruction* two = new (&allocator) HParameterValue(0, Primitive::kPrimInt);
+ HInstruction* three = new (&allocator) HParameterValue(0, Primitive::kPrimInt);
+ HInstruction* four = new (&allocator) HParameterValue(0, Primitive::kPrimInt);
+ entry->AddInstruction(one);
+ entry->AddInstruction(two);
+ entry->AddInstruction(three);
+ entry->AddInstruction(four);
+
+ HBasicBlock* block = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block);
+ entry->AddSuccessor(block);
+ block->AddInstruction(new (&allocator) HExit());
+
+ // We create a synthesized user requesting a register, to avoid just spilling the
+ // intervals.
+ HPhi* user = new (&allocator) HPhi(&allocator, 0, 1, Primitive::kPrimInt);
+ user->AddInput(one);
+ user->SetBlock(block);
+ LocationSummary* locations = new (&allocator) LocationSummary(user, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ static constexpr size_t phi_ranges[][2] = {{20, 30}};
+ BuildInterval(phi_ranges, arraysize(phi_ranges), &allocator, -1, user);
+
+ // Create an interval with lifetime holes.
+ static constexpr size_t ranges1[][2] = {{0, 2}, {4, 6}, {8, 10}};
+ LiveInterval* first = BuildInterval(ranges1, arraysize(ranges1), &allocator, -1, one);
+ first->first_use_ = new(&allocator) UsePosition(user, 0, false, 8, first->first_use_);
+ first->first_use_ = new(&allocator) UsePosition(user, 0, false, 7, first->first_use_);
+ first->first_use_ = new(&allocator) UsePosition(user, 0, false, 6, first->first_use_);
+
+ locations = new (&allocator) LocationSummary(first->GetDefinedBy(), LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
+ first = first->SplitAt(1);
+
+ // Create an interval that conflicts with the next interval, to force the next
+ // interval to call `AllocateBlockedReg`.
+ static constexpr size_t ranges2[][2] = {{2, 4}};
+ LiveInterval* second = BuildInterval(ranges2, arraysize(ranges2), &allocator, -1, two);
+ locations = new (&allocator) LocationSummary(second->GetDefinedBy(), LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
+
+ // Create an interval that will lead to splitting the first interval. The bug occured
+ // by splitting at a wrong position, in this case at the next intersection between
+ // this interval and the first interval. We would have then put the interval with ranges
+ // "[0, 2(, [4, 6(" in the list of handled intervals, even though we haven't processed intervals
+ // before lifetime position 6 yet.
+ static constexpr size_t ranges3[][2] = {{2, 4}, {8, 10}};
+ LiveInterval* third = BuildInterval(ranges3, arraysize(ranges3), &allocator, -1, three);
+ third->first_use_ = new(&allocator) UsePosition(user, 0, false, 8, third->first_use_);
+ third->first_use_ = new(&allocator) UsePosition(user, 0, false, 4, third->first_use_);
+ third->first_use_ = new(&allocator) UsePosition(user, 0, false, 3, third->first_use_);
+ locations = new (&allocator) LocationSummary(third->GetDefinedBy(), LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
+ third = third->SplitAt(3);
+
+ // Because the first part of the split interval was considered handled, this interval
+ // was free to allocate the same register, even though it conflicts with it.
+ static constexpr size_t ranges4[][2] = {{4, 6}};
+ LiveInterval* fourth = BuildInterval(ranges4, arraysize(ranges4), &allocator, -1, four);
+ locations = new (&allocator) LocationSummary(fourth->GetDefinedBy(), LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
+
+ x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
+ SsaLivenessAnalysis liveness(*graph, &codegen);
+
+ RegisterAllocator register_allocator(&allocator, &codegen, liveness);
+ register_allocator.unhandled_core_intervals_.Add(fourth);
+ register_allocator.unhandled_core_intervals_.Add(third);
+ register_allocator.unhandled_core_intervals_.Add(second);
+ register_allocator.unhandled_core_intervals_.Add(first);
+
+ // Set just one register available to make all intervals compete for the same.
+ register_allocator.number_of_registers_ = 1;
+ register_allocator.registers_array_ = allocator.AllocArray<size_t>(1);
+ register_allocator.processing_core_registers_ = true;
+ register_allocator.unhandled_ = ®ister_allocator.unhandled_core_intervals_;
+ register_allocator.LinearScan();
+
+ // Test that there is no conflicts between intervals.
+ GrowableArray<LiveInterval*> intervals(&allocator, 0);
+ intervals.Add(first);
+ intervals.Add(second);
+ intervals.Add(third);
+ intervals.Add(fourth);
+ ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
+ intervals, 0, 0, codegen, &allocator, true, false));
+}
+
} // namespace art
diff --git a/compiler/optimizing/side_effects_analysis.cc b/compiler/optimizing/side_effects_analysis.cc
new file mode 100644
index 0000000..ea1ca5a
--- /dev/null
+++ b/compiler/optimizing/side_effects_analysis.cc
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "side_effects_analysis.h"
+
+namespace art {
+
+void SideEffectsAnalysis::Run() {
+ // Inlining might have created more blocks, so we need to increase the size
+ // if needed.
+ block_effects_.SetSize(graph_->GetBlocks().Size());
+ loop_effects_.SetSize(graph_->GetBlocks().Size());
+
+ if (kIsDebugBuild) {
+ for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ SideEffects effects = GetBlockEffects(block);
+ DCHECK(!effects.HasSideEffects() && !effects.HasDependencies());
+ if (block->IsLoopHeader()) {
+ effects = GetLoopEffects(block);
+ DCHECK(!effects.HasSideEffects() && !effects.HasDependencies());
+ }
+ }
+ }
+
+ // Do a post order visit to ensure we visit a loop header after its loop body.
+ for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+
+ SideEffects effects = SideEffects::None();
+ // Update `effects` with the side effects of all instructions in this block.
+ for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
+ inst_it.Advance()) {
+ HInstruction* instruction = inst_it.Current();
+ effects = effects.Union(instruction->GetSideEffects());
+ if (effects.HasAllSideEffects()) {
+ break;
+ }
+ }
+
+ block_effects_.Put(block->GetBlockId(), effects);
+
+ if (block->IsLoopHeader()) {
+ // The side effects of the loop header are part of the loop.
+ UpdateLoopEffects(block->GetLoopInformation(), effects);
+ HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader();
+ if (pre_header->IsInLoop()) {
+ // Update the side effects of the outer loop with the side effects of the inner loop.
+ // Note that this works because we know all the blocks of the inner loop are visited
+ // before the loop header of the outer loop.
+ UpdateLoopEffects(pre_header->GetLoopInformation(), GetLoopEffects(block));
+ }
+ } else if (block->IsInLoop()) {
+ // Update the side effects of the loop with the side effects of this block.
+ UpdateLoopEffects(block->GetLoopInformation(), effects);
+ }
+ }
+ has_run_ = true;
+}
+
+SideEffects SideEffectsAnalysis::GetLoopEffects(HBasicBlock* block) const {
+ DCHECK(block->IsLoopHeader());
+ return loop_effects_.Get(block->GetBlockId());
+}
+
+SideEffects SideEffectsAnalysis::GetBlockEffects(HBasicBlock* block) const {
+ return block_effects_.Get(block->GetBlockId());
+}
+
+void SideEffectsAnalysis::UpdateLoopEffects(HLoopInformation* info, SideEffects effects) {
+ int id = info->GetHeader()->GetBlockId();
+ loop_effects_.Put(id, loop_effects_.Get(id).Union(effects));
+}
+
+} // namespace art
diff --git a/compiler/optimizing/side_effects_analysis.h b/compiler/optimizing/side_effects_analysis.h
new file mode 100644
index 0000000..f1c98ac
--- /dev/null
+++ b/compiler/optimizing/side_effects_analysis.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_SIDE_EFFECTS_ANALYSIS_H_
+#define ART_COMPILER_OPTIMIZING_SIDE_EFFECTS_ANALYSIS_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+class SideEffectsAnalysis : public HOptimization {
+ public:
+ explicit SideEffectsAnalysis(HGraph* graph)
+ : HOptimization(graph, true, "SideEffects"),
+ graph_(graph),
+ block_effects_(graph->GetArena(), graph->GetBlocks().Size(), SideEffects::None()),
+ loop_effects_(graph->GetArena(), graph->GetBlocks().Size(), SideEffects::None()) {}
+
+ SideEffects GetLoopEffects(HBasicBlock* block) const;
+ SideEffects GetBlockEffects(HBasicBlock* block) const;
+
+ // Compute side effects of individual blocks and loops.
+ void Run();
+
+ bool HasRun() const { return has_run_; }
+
+ private:
+ void UpdateLoopEffects(HLoopInformation* info, SideEffects effects);
+
+ HGraph* graph_;
+
+ // Checked in debug build, to ensure the pass has been run prior to
+ // running a pass that depends on it.
+ bool has_run_ = false;
+
+ // Side effects of individual blocks, that is the union of the side effects
+ // of the instructions in the block.
+ GrowableArray<SideEffects> block_effects_;
+
+ // Side effects of loops, that is the union of the side effects of the
+ // blocks contained in that loop.
+ GrowableArray<SideEffects> loop_effects_;
+
+ ART_FRIEND_TEST(GVNTest, LoopSideEffects);
+ DISALLOW_COPY_AND_ASSIGN(SideEffectsAnalysis);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_SIDE_EFFECTS_ANALYSIS_H_
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index edfafcd..c9a21aa 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -17,7 +17,7 @@
#include "ssa_builder.h"
#include "nodes.h"
-#include "ssa_type_propagation.h"
+#include "primitive_type_propagation.h"
#include "ssa_phi_elimination.h"
namespace art {
@@ -52,7 +52,7 @@
// 4) Propagate types of phis. At this point, phis are typed void in the general
// case, or float or double when we created a floating-point equivalent. So we
// need to propagate the types across phis to give them a correct type.
- SsaTypePropagation type_propagation(GetGraph());
+ PrimitiveTypePropagation type_propagation(GetGraph());
type_propagation.Run();
// 5) Clear locals.
@@ -68,7 +68,7 @@
}
HInstruction* SsaBuilder::ValueOfLocal(HBasicBlock* block, size_t local) {
- return GetLocalsFor(block)->Get(local);
+ return GetLocalsFor(block)->GetInstructionAt(local);
}
void SsaBuilder::VisitBasicBlock(HBasicBlock* block) {
@@ -85,7 +85,7 @@
HPhi* phi = new (GetGraph()->GetArena()) HPhi(
GetGraph()->GetArena(), local, 0, Primitive::kPrimVoid);
block->AddPhi(phi);
- current_locals_->Put(local, phi);
+ current_locals_->SetRawEnvAt(local, phi);
}
}
// Save the loop header so that the last phase of the analysis knows which
@@ -125,7 +125,7 @@
block->AddPhi(phi);
value = phi;
}
- current_locals_->Put(local, value);
+ current_locals_->SetRawEnvAt(local, value);
}
}
@@ -235,7 +235,7 @@
}
void SsaBuilder::VisitLoadLocal(HLoadLocal* load) {
- HInstruction* value = current_locals_->Get(load->GetLocal()->GetRegNumber());
+ HInstruction* value = current_locals_->GetInstructionAt(load->GetLocal()->GetRegNumber());
if (load->GetType() != value->GetType()
&& (load->GetType() == Primitive::kPrimFloat || load->GetType() == Primitive::kPrimDouble)) {
// If the operation requests a specific type, we make sure its input is of that type.
@@ -246,7 +246,7 @@
}
void SsaBuilder::VisitStoreLocal(HStoreLocal* store) {
- current_locals_->Put(store->GetLocal()->GetRegNumber(), store->InputAt(1));
+ current_locals_->SetRawEnvAt(store->GetLocal()->GetRegNumber(), store->InputAt(1));
store->GetBlock()->RemoveInstruction(store);
}
@@ -256,7 +256,7 @@
}
HEnvironment* environment = new (GetGraph()->GetArena()) HEnvironment(
GetGraph()->GetArena(), current_locals_->Size());
- environment->Populate(*current_locals_);
+ environment->CopyFrom(current_locals_);
instruction->SetEnvironment(environment);
}
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index 2cbd51a..2eec87b 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -36,14 +36,14 @@
void BuildSsa();
- GrowableArray<HInstruction*>* GetLocalsFor(HBasicBlock* block) {
+ HEnvironment* GetLocalsFor(HBasicBlock* block) {
HEnvironment* env = locals_for_.Get(block->GetBlockId());
if (env == nullptr) {
env = new (GetGraph()->GetArena()) HEnvironment(
GetGraph()->GetArena(), GetGraph()->GetNumberOfVRegs());
locals_for_.Put(block->GetBlockId(), env);
}
- return env->GetVRegs();
+ return env;
}
HInstruction* ValueOfLocal(HBasicBlock* block, size_t local);
@@ -60,7 +60,7 @@
private:
// Locals for the current block being visited.
- GrowableArray<HInstruction*>* current_locals_;
+ HEnvironment* current_locals_;
// Keep track of loop headers found. The last phase of the analysis iterates
// over these blocks to set the inputs of their phis.
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 660a5c5..2a84735 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -115,14 +115,13 @@
// to differentiate between the start and end of an instruction. Adding 2 to
// the lifetime position for each instruction ensures the start of an
// instruction is different than the end of the previous instruction.
- HGraphVisitor* location_builder = codegen_->GetLocationBuilder();
for (HLinearOrderIterator it(*this); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
block->SetLifetimeStart(lifetime_position);
for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
HInstruction* current = inst_it.Current();
- current->Accept(location_builder);
+ codegen_->AllocateLocations(current);
LocationSummary* locations = current->GetLocations();
if (locations != nullptr && locations->Out().IsValid()) {
instructions_from_ssa_index_.Add(current);
@@ -140,7 +139,7 @@
for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
inst_it.Advance()) {
HInstruction* current = inst_it.Current();
- current->Accept(codegen_->GetLocationBuilder());
+ codegen_->AllocateLocations(current);
LocationSummary* locations = current->GetLocations();
if (locations != nullptr && locations->Out().IsValid()) {
instructions_from_ssa_index_.Add(current);
@@ -232,9 +231,9 @@
if (current->HasEnvironment()) {
// All instructions in the environment must be live.
- GrowableArray<HInstruction*>* environment = current->GetEnvironment()->GetVRegs();
+ HEnvironment* environment = current->GetEnvironment();
for (size_t i = 0, e = environment->Size(); i < e; ++i) {
- HInstruction* instruction = environment->Get(i);
+ HInstruction* instruction = environment->GetInstructionAt(i);
if (instruction != nullptr) {
DCHECK(instruction->HasSsaIndex());
live_in->SetBit(instruction->GetSsaIndex());
@@ -419,10 +418,21 @@
}
Location LiveInterval::ToLocation() const {
+ DCHECK(!IsHighInterval());
if (HasRegister()) {
- return IsFloatingPoint()
- ? Location::FpuRegisterLocation(GetRegister())
- : Location::RegisterLocation(GetRegister());
+ if (IsFloatingPoint()) {
+ if (HasHighInterval()) {
+ return Location::FpuRegisterPairLocation(GetRegister(), GetHighInterval()->GetRegister());
+ } else {
+ return Location::FpuRegisterLocation(GetRegister());
+ }
+ } else {
+ if (HasHighInterval()) {
+ return Location::RegisterPairLocation(GetRegister(), GetHighInterval()->GetRegister());
+ } else {
+ return Location::RegisterLocation(GetRegister());
+ }
+ }
} else {
HInstruction* defined_by = GetParent()->GetDefinedBy();
if (defined_by->IsConstant()) {
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 2312389..0e68a61 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_OPTIMIZING_SSA_LIVENESS_ANALYSIS_H_
#include "nodes.h"
+#include <iostream>
namespace art {
@@ -77,6 +78,15 @@
stream << "[" << start_ << ", " << end_ << ")";
}
+ LiveRange* Dup(ArenaAllocator* allocator) const {
+ return new (allocator) LiveRange(
+ start_, end_, next_ == nullptr ? nullptr : next_->Dup(allocator));
+ }
+
+ LiveRange* GetLastRange() {
+ return next_ == nullptr ? this : next_->GetLastRange();
+ }
+
private:
size_t start_;
size_t end_;
@@ -123,6 +133,12 @@
stream << position_;
}
+ UsePosition* Dup(ArenaAllocator* allocator) const {
+ return new (allocator) UsePosition(
+ user_, input_index_, is_environment_, position_,
+ next_ == nullptr ? nullptr : next_->Dup(allocator));
+ }
+
private:
HInstruction* const user_;
const size_t input_index_;
@@ -166,12 +182,21 @@
void AddUse(HInstruction* instruction, size_t input_index, bool is_environment) {
// Set the use within the instruction.
- size_t position = instruction->GetLifetimePosition();
- if (instruction->GetLocations()->InputOverlapsWithOutputOrTemp(input_index, is_environment)) {
- // If it overlaps, we need to make sure the user will not try to allocate a temp
- // or its output to the same register.
- ++position;
+ size_t position = instruction->GetLifetimePosition() + 1;
+ LocationSummary* locations = instruction->GetLocations();
+ if (!is_environment) {
+ if (locations->IsFixedInput(input_index) || locations->OutputUsesSameAs(input_index)) {
+ // For fixed inputs and output same as input, the register allocator
+ // requires to have inputs die at the instruction, so that input moves use the
+ // location of the input just before that instruction (and not potential moves due
+ // to splitting).
+ position = instruction->GetLifetimePosition();
+ }
}
+
+ DCHECK(position == instruction->GetLifetimePosition()
+ || position == instruction->GetLifetimePosition() + 1);
+
if ((first_use_ != nullptr)
&& (first_use_->GetUser() == instruction)
&& (first_use_->GetPosition() < position)) {
@@ -239,16 +264,28 @@
void AddLoopRange(size_t start, size_t end) {
DCHECK(first_range_ != nullptr);
- while (first_range_ != nullptr && first_range_->GetEnd() < end) {
- DCHECK_LE(start, first_range_->GetStart());
- first_range_ = first_range_->GetNext();
+ DCHECK_LE(start, first_range_->GetStart());
+ // Find the range that covers the positions after the loop.
+ LiveRange* after_loop = first_range_;
+ LiveRange* last_in_loop = nullptr;
+ while (after_loop != nullptr && after_loop->GetEnd() < end) {
+ DCHECK_LE(start, after_loop->GetStart());
+ last_in_loop = after_loop;
+ after_loop = after_loop->GetNext();
}
- if (first_range_ == nullptr) {
+ if (after_loop == nullptr) {
// Uses are only in the loop.
first_range_ = last_range_ = new (allocator_) LiveRange(start, end, nullptr);
- } else {
+ } else if (after_loop->GetStart() <= end) {
+ first_range_ = after_loop;
// There are uses after the loop.
first_range_->start_ = start;
+ } else {
+ // The use after the loop is after a lifetime hole.
+ DCHECK(last_in_loop != nullptr);
+ first_range_ = last_in_loop;
+ first_range_->start_ = start;
+ first_range_->end_ = end;
}
}
@@ -274,6 +311,7 @@
LiveInterval* GetParent() const { return parent_; }
LiveRange* GetFirstRange() const { return first_range_; }
+ LiveRange* GetLastRange() const { return last_range_; }
int GetRegister() const { return register_; }
void SetRegister(int reg) { register_ = reg; }
@@ -376,6 +414,23 @@
return FirstRegisterUseAfter(GetStart());
}
+ size_t FirstUseAfter(size_t position) const {
+ if (is_temp_) {
+ return position == GetStart() ? position : kNoLifetime;
+ }
+
+ UsePosition* use = first_use_;
+ size_t end = GetEnd();
+ while (use != nullptr && use->GetPosition() <= end) {
+ size_t use_position = use->GetPosition();
+ if (use_position > position) {
+ return use_position;
+ }
+ use = use->GetNext();
+ }
+ return kNoLifetime;
+ }
+
UsePosition* GetFirstUse() const {
return first_use_;
}
@@ -414,7 +469,7 @@
LiveRange* current = first_range_;
LiveRange* previous = nullptr;
// Iterate over the ranges, and either find a range that covers this position, or
- // a two ranges in between this position (that is, the position is in a lifetime hole).
+ // two ranges in between this position (that is, the position is in a lifetime hole).
do {
if (position >= current->GetEnd()) {
// Move to next range.
@@ -464,10 +519,11 @@
void Dump(std::ostream& stream) const {
stream << "ranges: { ";
LiveRange* current = first_range_;
- do {
+ while (current != nullptr) {
current->Dump(stream);
stream << " ";
- } while ((current = current->GetNext()) != nullptr);
+ current = current->GetNext();
+ }
stream << "}, uses: { ";
UsePosition* use = first_use_;
if (use != nullptr) {
@@ -478,9 +534,18 @@
}
stream << "}";
stream << " is_fixed: " << is_fixed_ << ", is_split: " << IsSplit();
+ stream << " is_high: " << IsHighInterval();
+ stream << " is_low: " << IsLowInterval();
}
LiveInterval* GetNextSibling() const { return next_sibling_; }
+ LiveInterval* GetLastSibling() {
+ LiveInterval* result = this;
+ while (result->next_sibling_ != nullptr) {
+ result = result->next_sibling_;
+ }
+ return result;
+ }
// Returns the first register hint that is at least free before
// the value contained in `free_until`. If none is found, returns
@@ -511,6 +576,115 @@
// Returns whether `other` and `this` share the same kind of register.
bool SameRegisterKind(Location other) const;
+ bool SameRegisterKind(const LiveInterval& other) const {
+ return IsFloatingPoint() == other.IsFloatingPoint();
+ }
+
+ bool HasHighInterval() const {
+ return IsLowInterval();
+ }
+
+ bool HasLowInterval() const {
+ return IsHighInterval();
+ }
+
+ LiveInterval* GetLowInterval() const {
+ DCHECK(HasLowInterval());
+ return high_or_low_interval_;
+ }
+
+ LiveInterval* GetHighInterval() const {
+ DCHECK(HasHighInterval());
+ return high_or_low_interval_;
+ }
+
+ bool IsHighInterval() const {
+ return GetParent()->is_high_interval_;
+ }
+
+ bool IsLowInterval() const {
+ return !IsHighInterval() && (GetParent()->high_or_low_interval_ != nullptr);
+ }
+
+ void SetLowInterval(LiveInterval* low) {
+ DCHECK(IsHighInterval());
+ high_or_low_interval_ = low;
+ }
+
+ void SetHighInterval(LiveInterval* high) {
+ DCHECK(IsLowInterval());
+ high_or_low_interval_ = high;
+ }
+
+ void AddHighInterval(bool is_temp = false) {
+ DCHECK_EQ(GetParent(), this);
+ DCHECK(!HasHighInterval());
+ DCHECK(!HasLowInterval());
+ high_or_low_interval_ = new (allocator_) LiveInterval(
+ allocator_, type_, defined_by_, false, kNoRegister, is_temp, false, true);
+ high_or_low_interval_->high_or_low_interval_ = this;
+ if (first_range_ != nullptr) {
+ high_or_low_interval_->first_range_ = first_range_->Dup(allocator_);
+ high_or_low_interval_->last_range_ = first_range_->GetLastRange();
+ }
+ if (first_use_ != nullptr) {
+ high_or_low_interval_->first_use_ = first_use_->Dup(allocator_);
+ }
+ }
+
+ // Returns whether an interval, when it is non-split, is using
+ // the same register of one of its input.
+ bool IsUsingInputRegister() const {
+ if (defined_by_ != nullptr && !IsSplit()) {
+ for (HInputIterator it(defined_by_); !it.Done(); it.Advance()) {
+ LiveInterval* interval = it.Current()->GetLiveInterval();
+
+ // Find the interval that covers `defined_by`_.
+ while (interval != nullptr && !interval->Covers(defined_by_->GetLifetimePosition())) {
+ interval = interval->GetNextSibling();
+ }
+
+ // Check if both intervals have the same register of the same kind.
+ if (interval != nullptr
+ && interval->SameRegisterKind(*this)
+ && interval->GetRegister() == GetRegister()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ // Returns whether an interval, when it is non-split, can safely use
+ // the same register of one of its input. Note that this method requires
+ // IsUsingInputRegister() to be true.
+ bool CanUseInputRegister() const {
+ DCHECK(IsUsingInputRegister());
+ if (defined_by_ != nullptr && !IsSplit()) {
+ LocationSummary* locations = defined_by_->GetLocations();
+ if (locations->OutputCanOverlapWithInputs()) {
+ return false;
+ }
+ for (HInputIterator it(defined_by_); !it.Done(); it.Advance()) {
+ LiveInterval* interval = it.Current()->GetLiveInterval();
+
+ // Find the interval that covers `defined_by`_.
+ while (interval != nullptr && !interval->Covers(defined_by_->GetLifetimePosition())) {
+ interval = interval->GetNextSibling();
+ }
+
+ if (interval != nullptr
+ && interval->SameRegisterKind(*this)
+ && interval->GetRegister() == GetRegister()) {
+ // We found the input that has the same register. Check if it is live after
+ // `defined_by`_.
+ return !interval->Covers(defined_by_->GetLifetimePosition() + 1);
+ }
+ }
+ }
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+ }
private:
LiveInterval(ArenaAllocator* allocator,
@@ -519,7 +693,8 @@
bool is_fixed = false,
int reg = kNoRegister,
bool is_temp = false,
- bool is_slow_path_safepoint = false)
+ bool is_slow_path_safepoint = false,
+ bool is_high_interval = false)
: allocator_(allocator),
first_range_(nullptr),
last_range_(nullptr),
@@ -532,6 +707,8 @@
is_fixed_(is_fixed),
is_temp_(is_temp),
is_slow_path_safepoint_(is_slow_path_safepoint),
+ is_high_interval_(is_high_interval),
+ high_or_low_interval_(nullptr),
defined_by_(defined_by) {}
ArenaAllocator* const allocator_;
@@ -568,12 +745,21 @@
// Whether the interval is for a safepoint that calls on slow path.
const bool is_slow_path_safepoint_;
+ // Whether this interval is a synthesized interval for register pair.
+ const bool is_high_interval_;
+
+ // If this interval needs a register pair, the high or low equivalent.
+ // `is_high_interval_` tells whether this holds the low or the high.
+ LiveInterval* high_or_low_interval_;
+
// The instruction represented by this interval.
HInstruction* const defined_by_;
static constexpr int kNoRegister = -1;
static constexpr int kNoSpillSlot = -1;
+ ART_FRIEND_TEST(RegisterAllocatorTest, SpillInactive);
+
DISALLOW_COPY_AND_ASSIGN(LiveInterval);
};
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index 58cea77..fd30c1b 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -26,8 +26,8 @@
HPhi* phi = inst_it.Current()->AsPhi();
// Set dead ahead of running through uses. The phi may have no use.
phi->SetDead();
- for (HUseIterator<HInstruction> use_it(phi->GetUses()); !use_it.Done(); use_it.Advance()) {
- HUseListNode<HInstruction>* current = use_it.Current();
+ for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done(); use_it.Advance()) {
+ HUseListNode<HInstruction*>* current = use_it.Current();
HInstruction* user = current->GetUser();
if (!user->IsPhi()) {
worklist_.Add(phi);
@@ -61,9 +61,9 @@
next = current->GetNext();
if (current->AsPhi()->IsDead()) {
if (current->HasUses()) {
- for (HUseIterator<HInstruction> use_it(current->GetUses()); !use_it.Done();
+ for (HUseIterator<HInstruction*> use_it(current->GetUses()); !use_it.Done();
use_it.Advance()) {
- HUseListNode<HInstruction>* user_node = use_it.Current();
+ HUseListNode<HInstruction*>* user_node = use_it.Current();
HInstruction* user = user_node->GetUser();
DCHECK(user->IsLoopHeaderPhi()) << user->GetId();
DCHECK(user->AsPhi()->IsDead()) << user->GetId();
@@ -73,12 +73,12 @@
}
}
if (current->HasEnvironmentUses()) {
- for (HUseIterator<HEnvironment> use_it(current->GetEnvUses()); !use_it.Done();
+ for (HUseIterator<HEnvironment*> use_it(current->GetEnvUses()); !use_it.Done();
use_it.Advance()) {
- HUseListNode<HEnvironment>* user_node = use_it.Current();
+ HUseListNode<HEnvironment*>* user_node = use_it.Current();
HEnvironment* user = user_node->GetUser();
user->SetRawEnvAt(user_node->GetIndex(), nullptr);
- current->RemoveEnvironmentUser(user, user_node->GetIndex());
+ current->RemoveEnvironmentUser(user_node);
}
}
block->RemovePhi(current->AsPhi());
@@ -132,8 +132,8 @@
// Because we're updating the users of this phi, we may have new
// phis candidate for elimination if this phi is in a loop. Add phis that
// used this phi to the worklist.
- for (HUseIterator<HInstruction> it(phi->GetUses()); !it.Done(); it.Advance()) {
- HUseListNode<HInstruction>* current = it.Current();
+ for (HUseIterator<HInstruction*> it(phi->GetUses()); !it.Done(); it.Advance()) {
+ HUseListNode<HInstruction*>* current = it.Current();
HInstruction* user = current->GetUser();
if (user->IsPhi()) {
worklist_.Add(user->AsPhi());
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
index 6174dd4..7e90b37 100644
--- a/compiler/optimizing/ssa_test.cc
+++ b/compiler/optimizing/ssa_test.cc
@@ -78,16 +78,17 @@
static void TestCode(const uint16_t* data, const char* expected) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraphBuilder builder(&allocator);
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+ HGraphBuilder builder(graph);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- HGraph* graph = builder.BuildGraph(*item);
- ASSERT_NE(graph, nullptr);
+ bool graph_built = builder.BuildGraph(*item);
+ ASSERT_TRUE(graph_built);
graph->BuildDominatorTree();
// Suspend checks implementation may change in the future, and this test relies
// on how instructions are ordered.
RemoveSuspendChecks(graph);
- graph->TransformToSSA();
+ graph->TransformToSsa();
ReNumberInstructions(graph);
// Test that phis had their type set.
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 9cfa71c..3974e53 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -111,7 +111,7 @@
}
size_t ComputeStackMapSize() const {
- return stack_maps_.Size() * (StackMap::kFixedSize + StackMaskEncodingSize(stack_mask_max_));
+ return stack_maps_.Size() * StackMap::ComputeAlignedStackMapSize(stack_mask_max_);
}
size_t ComputeDexRegisterMapSize() const {
diff --git a/compiler/optimizing/suspend_check_test.cc b/compiler/optimizing/suspend_check_test.cc
index 2e48ee8..a5a0eb2 100644
--- a/compiler/optimizing/suspend_check_test.cc
+++ b/compiler/optimizing/suspend_check_test.cc
@@ -30,10 +30,11 @@
static void TestCode(const uint16_t* data) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraphBuilder builder(&allocator);
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+ HGraphBuilder builder(graph);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- HGraph* graph = builder.BuildGraph(*item);
- ASSERT_NE(graph, nullptr);
+ bool graph_built = builder.BuildGraph(*item);
+ ASSERT_TRUE(graph_built);
HBasicBlock* first_block = graph->GetEntryBlock()->GetSuccessors().Get(0);
HInstruction* first_instruction = first_block->GetFirstInstruction();
diff --git a/compiler/sea_ir/code_gen/code_gen.cc b/compiler/sea_ir/code_gen/code_gen.cc
deleted file mode 100644
index 8d79c41..0000000
--- a/compiler/sea_ir/code_gen/code_gen.cc
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <llvm/Support/raw_ostream.h>
-
-#include "base/logging.h"
-#include "utils.h"
-
-#include "sea_ir/ir/sea.h"
-#include "sea_ir/code_gen/code_gen.h"
-#include "sea_ir/types/type_inference.h"
-#include "sea_ir/types/types.h"
-
-namespace sea_ir {
-
-void CodeGenPrepassVisitor::Visit(PhiInstructionNode* phi) {
- Region* r = phi->GetRegion();
- const std::vector<Region*>* predecessors = r->GetPredecessors();
- DCHECK(NULL != predecessors);
- DCHECK_GT(predecessors->size(), 0u);
- llvm::PHINode *llvm_phi = llvm_data_->builder_.CreatePHI(
- llvm::Type::getInt32Ty(*llvm_data_->context_), predecessors->size(), phi->StringId());
- llvm_data_->AddValue(phi, llvm_phi);
-}
-
-void CodeGenPassVisitor::Initialize(SeaGraph* graph) {
- Region* root_region;
- ordered_regions_.clear();
- for (std::vector<Region*>::const_iterator cit = graph->GetRegions()->begin();
- cit != graph->GetRegions()->end(); cit++ ) {
- if ((*cit)->GetIDominator() == (*cit)) {
- root_region = *cit;
- }
- }
- ordered_regions_.push_back(root_region);
- for (unsigned int id = 0; id < ordered_regions_.size(); id++) {
- Region* current_region = ordered_regions_.at(id);
- const std::set<Region*>* dominated_regions = current_region->GetIDominatedSet();
- for (std::set<Region*>::const_iterator cit = dominated_regions->begin();
- cit != dominated_regions->end(); cit++ ) {
- ordered_regions_.push_back(*cit);
- }
- }
-}
-
-void CodeGenPostpassVisitor::Visit(SeaGraph* graph) { }
-void CodeGenVisitor::Visit(SeaGraph* graph) { }
-void CodeGenPrepassVisitor::Visit(SeaGraph* graph) {
- std::vector<SignatureNode*>* parameters = graph->GetParameterNodes();
- // TODO: It may be better to extract correct types from dex
- // instead than from type inference.
- DCHECK(parameters != NULL);
- std::vector<llvm::Type*> parameter_types;
- for (std::vector<SignatureNode*>::const_iterator param_iterator = parameters->begin();
- param_iterator!= parameters->end(); param_iterator++) {
- const Type* param_type = graph->ti_->type_data_.FindTypeOf((*param_iterator)->Id());
- DCHECK(param_type->Equals(graph->ti_->type_cache_->Integer()))
- << "Code generation for types other than integer not implemented.";
- parameter_types.push_back(llvm::Type::getInt32Ty(*llvm_data_->context_));
- }
-
- // TODO: Get correct function return type.
- const Type* return_type = graph->ti_->type_data_.FindTypeOf(-1);
- DCHECK(return_type->Equals(graph->ti_->type_cache_->Integer()))
- << "Code generation for types other than integer not implemented.";
- llvm::FunctionType *function_type = llvm::FunctionType::get(
- llvm::Type::getInt32Ty(*llvm_data_->context_),
- parameter_types, false);
-
- llvm_data_->function_ = llvm::Function::Create(function_type,
- llvm::Function::ExternalLinkage, function_name_, &llvm_data_->module_);
- unsigned param_id = 0;
- for (llvm::Function::arg_iterator arg_it = llvm_data_->function_->arg_begin();
- param_id != llvm_data_->function_->arg_size(); ++arg_it, ++param_id) {
- // TODO: The "+1" is because of the Method parameter on position 0.
- DCHECK(parameters->size() > param_id) << "Insufficient parameters for function signature";
- // Build parameter register name for LLVM IR clarity.
- std::string arg_name = art::StringPrintf("r%d", parameters->at(param_id)->GetResultRegister());
- arg_it->setName(arg_name);
- SignatureNode* parameter = parameters->at(param_id);
- llvm_data_->AddValue(parameter, arg_it);
- }
-
- std::vector<Region*>* regions = &ordered_regions_;
- DCHECK_GT(regions->size(), 0u);
- // Then create all other basic blocks.
- for (std::vector<Region*>::const_iterator cit = regions->begin(); cit != regions->end(); cit++) {
- llvm::BasicBlock* new_basic_block = llvm::BasicBlock::Create(*llvm_data_->context_,
- (*cit)->StringId(), llvm_data_->function_);
- llvm_data_->AddBlock((*cit), new_basic_block);
- }
-}
-
-void CodeGenPrepassVisitor::Visit(Region* region) {
- llvm_data_->builder_.SetInsertPoint(llvm_data_->GetBlock(region));
-}
-void CodeGenPostpassVisitor::Visit(Region* region) {
- llvm_data_->builder_.SetInsertPoint(llvm_data_->GetBlock(region));
-}
-void CodeGenVisitor::Visit(Region* region) {
- llvm_data_->builder_.SetInsertPoint(llvm_data_->GetBlock(region));
-}
-
-
-void CodeGenVisitor::Visit(InstructionNode* instruction) {
- std::string instr = instruction->GetInstruction()->DumpString(NULL);
- DCHECK(0); // This whole function is useful only during development.
-}
-
-void CodeGenVisitor::Visit(UnnamedConstInstructionNode* instruction) {
- std::string instr = instruction->GetInstruction()->DumpString(NULL);
- std::cout << "1.Instruction: " << instr << std::endl;
- llvm_data_->AddValue(instruction,
- llvm::ConstantInt::get(*llvm_data_->context_, llvm::APInt(32, instruction->GetConstValue())));
-}
-
-void CodeGenVisitor::Visit(ConstInstructionNode* instruction) {
- std::string instr = instruction->GetInstruction()->DumpString(NULL);
- std::cout << "1.Instruction: " << instr << std::endl;
- llvm_data_->AddValue(instruction,
- llvm::ConstantInt::get(*llvm_data_->context_, llvm::APInt(32, instruction->GetConstValue())));
-}
-void CodeGenVisitor::Visit(ReturnInstructionNode* instruction) {
- std::string instr = instruction->GetInstruction()->DumpString(NULL);
- std::cout << "2.Instruction: " << instr << std::endl;
- DCHECK_GT(instruction->GetSSAProducers().size(), 0u);
- llvm::Value* return_value = llvm_data_->GetValue(instruction->GetSSAProducers().at(0));
- llvm_data_->builder_.CreateRet(return_value);
-}
-void CodeGenVisitor::Visit(IfNeInstructionNode* instruction) {
- std::string instr = instruction->GetInstruction()->DumpString(NULL);
- std::cout << "3.Instruction: " << instr << std::endl;
- std::vector<InstructionNode*> ssa_uses = instruction->GetSSAProducers();
- DCHECK_GT(ssa_uses.size(), 1u);
- InstructionNode* use_l = ssa_uses.at(0);
- llvm::Value* left = llvm_data_->GetValue(use_l);
-
- InstructionNode* use_r = ssa_uses.at(1);
- llvm::Value* right = llvm_data_->GetValue(use_r);
- llvm::Value* ifne = llvm_data_->builder_.CreateICmpNE(left, right, instruction->StringId());
- DCHECK(instruction->GetRegion() != NULL);
- std::vector<Region*>* successors = instruction->GetRegion()->GetSuccessors();
- DCHECK_GT(successors->size(), 0u);
- llvm::BasicBlock* then_block = llvm_data_->GetBlock(successors->at(0));
- llvm::BasicBlock* else_block = llvm_data_->GetBlock(successors->at(1));
-
- llvm_data_->builder_.CreateCondBr(ifne, then_block, else_block);
-}
-
-/*
-void CodeGenVisitor::Visit(AddIntLitInstructionNode* instruction) {
- std::string instr = instruction->GetInstruction()->DumpString(NULL);
- std::cout << "4.Instruction: " << instr << std::endl;
- std::vector<InstructionNode*> ssa_uses = instruction->GetSSAUses();
- InstructionNode* use_l = ssa_uses.at(0);
- llvm::Value* left = llvm_data->GetValue(use_l);
- llvm::Value* right = llvm::ConstantInt::get(*llvm_data->context_,
- llvm::APInt(32, instruction->GetConstValue()));
- llvm::Value* result = llvm_data->builder_.CreateAdd(left, right);
- llvm_data->AddValue(instruction, result);
-}
-*/
-void CodeGenVisitor::Visit(MoveResultInstructionNode* instruction) {
- std::string instr = instruction->GetInstruction()->DumpString(NULL);
- std::cout << "5.Instruction: " << instr << std::endl;
- // TODO: Currently, this "mov" instruction is simulated by "res = return_register + 0".
- // This is inefficient, but should be optimized out by the coalescing phase of the reg alloc.
- // The TODO is to either ensure that this happens, or to
- // remove the move-result instructions completely from the IR
- // by merging them with the invoke-* instructions,
- // since their purpose of minimizing the number of opcodes in dex is
- // not relevant for the IR. (Will need to have different
- // instruction subclasses for functions and procedures.)
- std::vector<InstructionNode*> ssa_uses = instruction->GetSSAProducers();
- InstructionNode* use_l = ssa_uses.at(0);
- llvm::Value* left = llvm_data_->GetValue(use_l);
- llvm::Value* right = llvm::ConstantInt::get(*llvm_data_->context_, llvm::APInt(32, 0));
- llvm::Value* result = llvm_data_->builder_.CreateAdd(left, right);
- llvm_data_->AddValue(instruction, result);
-}
-void CodeGenVisitor::Visit(InvokeStaticInstructionNode* invoke) {
- std::string instr = invoke->GetInstruction()->DumpString(NULL);
- std::cout << "6.Instruction: " << instr << std::endl;
- // TODO: Build callee LLVM function name.
- std::string symbol = "dex_";
- symbol += art::MangleForJni(PrettyMethod(invoke->GetCalledMethodIndex(), dex_file_));
- std::string function_name = "dex_int_00020Main_fibonacci_00028int_00029";
- llvm::Function *callee = llvm_data_->module_.getFunction(function_name);
- // TODO: Add proper checking of the matching between formal and actual signature.
- DCHECK(NULL != callee);
- std::vector<llvm::Value*> parameter_values;
- std::vector<InstructionNode*> parameter_sources = invoke->GetSSAProducers();
- // TODO: Replace first parameter with Method argument instead of 0.
- parameter_values.push_back(llvm::ConstantInt::get(*llvm_data_->context_, llvm::APInt(32, 0)));
- for (std::vector<InstructionNode*>::const_iterator cit = parameter_sources.begin();
- cit != parameter_sources.end(); ++cit) {
- llvm::Value* parameter_value = llvm_data_->GetValue((*cit));
- DCHECK(NULL != parameter_value);
- parameter_values.push_back(parameter_value);
- }
- llvm::Value* return_value = llvm_data_->builder_.CreateCall(callee,
- parameter_values, invoke->StringId());
- llvm_data_->AddValue(invoke, return_value);
-}
-void CodeGenVisitor::Visit(AddIntInstructionNode* instruction) {
- std::string instr = instruction->GetInstruction()->DumpString(NULL);
- std::cout << "7.Instruction: " << instr << std::endl;
- std::vector<InstructionNode*> ssa_uses = instruction->GetSSAProducers();
- DCHECK_GT(ssa_uses.size(), 1u);
- InstructionNode* use_l = ssa_uses.at(0);
- InstructionNode* use_r = ssa_uses.at(1);
- llvm::Value* left = llvm_data_->GetValue(use_l);
- llvm::Value* right = llvm_data_->GetValue(use_r);
- llvm::Value* result = llvm_data_->builder_.CreateAdd(left, right);
- llvm_data_->AddValue(instruction, result);
-}
-void CodeGenVisitor::Visit(GotoInstructionNode* instruction) {
- std::string instr = instruction->GetInstruction()->DumpString(NULL);
- std::cout << "8.Instruction: " << instr << std::endl;
- std::vector<sea_ir::Region*>* targets = instruction->GetRegion()->GetSuccessors();
- DCHECK_EQ(targets->size(), 1u);
- llvm::BasicBlock* target_block = llvm_data_->GetBlock(targets->at(0));
- llvm_data_->builder_.CreateBr(target_block);
-}
-void CodeGenVisitor::Visit(IfEqzInstructionNode* instruction) {
- std::string instr = instruction->GetInstruction()->DumpString(NULL);
- std::cout << "9. Instruction: " << instr << "; Id: " <<instruction << std::endl;
- std::vector<InstructionNode*> ssa_uses = instruction->GetSSAProducers();
- DCHECK_GT(ssa_uses.size(), 0u);
- InstructionNode* use_l = ssa_uses.at(0);
- llvm::Value* left = llvm_data_->GetValue(use_l);
- llvm::Value* ifeqz = llvm_data_->builder_.CreateICmpEQ(left,
- llvm::ConstantInt::get(*llvm_data_->context_, llvm::APInt::getNullValue(32)),
- instruction->StringId());
- DCHECK(instruction->GetRegion() != NULL);
- std::vector<Region*>* successors = instruction->GetRegion()->GetSuccessors();
- DCHECK_GT(successors->size(), 0u);
- llvm::BasicBlock* then_block = llvm_data_->GetBlock(successors->at(0));
- llvm::BasicBlock* else_block = llvm_data_->GetBlock(successors->at(1));
- llvm_data_->builder_.CreateCondBr(ifeqz, then_block, else_block);
-}
-
-void CodeGenPostpassVisitor::Visit(PhiInstructionNode* phi) {
- std::cout << "10. Instruction: Phi(" << phi->GetRegisterNumber() << ")" << std::endl;
- Region* r = phi->GetRegion();
- const std::vector<Region*>* predecessors = r->GetPredecessors();
- DCHECK(NULL != predecessors);
- DCHECK_GT(predecessors->size(), 0u);
- // Prepass (CodeGenPrepassVisitor) should create the phi function value.
- llvm::PHINode* llvm_phi = (llvm::PHINode*) llvm_data_->GetValue(phi);
- int predecessor_pos = 0;
- for (std::vector<Region*>::const_iterator cit = predecessors->begin();
- cit != predecessors->end(); ++cit) {
- std::vector<InstructionNode*>* defining_instructions = phi->GetSSAUses(predecessor_pos++);
- DCHECK_EQ(defining_instructions->size(), 1u);
- InstructionNode* defining_instruction = defining_instructions->at(0);
- DCHECK(NULL != defining_instruction);
- Region* incoming_region = *cit;
- llvm::BasicBlock* incoming_basic_block = llvm_data_->GetBlock(incoming_region);
- llvm::Value* incoming_value = llvm_data_->GetValue(defining_instruction);
- llvm_phi->addIncoming(incoming_value, incoming_basic_block);
- }
-}
-
-void CodeGenVisitor::Visit(SignatureNode* signature) {
- DCHECK_EQ(signature->GetDefinitions().size(), 1u) <<
- "Signature nodes must correspond to a single parameter register.";
-}
-void CodeGenPrepassVisitor::Visit(SignatureNode* signature) {
- DCHECK_EQ(signature->GetDefinitions().size(), 1u) <<
- "Signature nodes must correspond to a single parameter register.";
-}
-void CodeGenPostpassVisitor::Visit(SignatureNode* signature) {
- DCHECK_EQ(signature->GetDefinitions().size(), 1u) <<
- "Signature nodes must correspond to a single parameter register.";
-}
-
-} // namespace sea_ir
diff --git a/compiler/sea_ir/code_gen/code_gen.h b/compiler/sea_ir/code_gen/code_gen.h
deleted file mode 100644
index 544e9f0..0000000
--- a/compiler/sea_ir/code_gen/code_gen.h
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_SEA_IR_CODE_GEN_CODE_GEN_H_
-#define ART_COMPILER_SEA_IR_CODE_GEN_CODE_GEN_H_
-
-#include "instruction_set.h"
-#include "llvm/Analysis/Verifier.h"
-#include "llvm/IR/IRBuilder.h"
-#include "llvm/IR/LLVMContext.h"
-#include "llvm/IR/Module.h"
-#include "llvm/Analysis/Verifier.h"
-#include "sea_ir/ir/visitor.h"
-
-namespace sea_ir {
-// Abstracts away the containers we use to map SEA IR objects to LLVM IR objects.
-class CodeGenData {
- public:
- explicit CodeGenData(): context_(&llvm::getGlobalContext()), module_("sea_ir", *context_),
- builder_(*context_), function_(), blocks_(), values_() { }
- // Returns the llvm::BasicBlock* corresponding to the sea_ir::Region with id @region_id.
- llvm::BasicBlock* GetBlock(int region_id) {
- std::map<int, llvm::BasicBlock*>::iterator block_it = blocks_.find(region_id);
- DCHECK(block_it != blocks_.end());
- return block_it->second;
- }
- // Returns the llvm::BasicBlock* corresponding top the sea_ir::Region @region.
- llvm::BasicBlock* GetBlock(Region* region) {
- return GetBlock(region->Id());
- }
- // Records @block as corresponding to the sea_ir::Region with id @region_id.
- void AddBlock(int region_id, llvm::BasicBlock* block) {
- blocks_.insert(std::pair<int, llvm::BasicBlock*>(region_id, block));
- }
- // Records @block as corresponding to the sea_ir::Region with @region.
- void AddBlock(Region* region, llvm::BasicBlock* block) {
- AddBlock(region->Id(), block);
- }
-
- llvm::Value* GetValue(int instruction_id) {
- std::map<int, llvm::Value*>::iterator value_it = values_.find(instruction_id);
- DCHECK(value_it != values_.end());
- return value_it->second;
- }
- // Returns the llvm::Value* corresponding to the output of @instruction.
- llvm::Value* GetValue(InstructionNode* instruction) {
- return GetValue(instruction->Id());
- }
- // Records @value as corresponding to the sea_ir::InstructionNode with id @instruction_id.
- void AddValue(int instruction_id, llvm::Value* value) {
- values_.insert(std::pair<int, llvm::Value*>(instruction_id, value));
- }
- // Records @value as corresponding to the sea_ir::InstructionNode @instruction.
- void AddValue(InstructionNode* instruction, llvm::Value* value) {
- AddValue(instruction->Id(), value);
- }
- // Generates and returns in @elf the executable code corresponding to the llvm module
- //
- std::string GetElf(art::InstructionSet instruction_set);
-
- llvm::LLVMContext* const context_;
- llvm::Module module_;
- llvm::IRBuilder<> builder_;
- llvm::Function* function_;
-
- private:
- std::map<int, llvm::BasicBlock*> blocks_;
- std::map<int, llvm::Value*> values_;
-};
-
-class CodeGenPassVisitor: public IRVisitor {
- public:
- explicit CodeGenPassVisitor(CodeGenData* cgd): llvm_data_(cgd) { }
- CodeGenPassVisitor(): llvm_data_(new CodeGenData()) { }
- // Initialize any data structure needed before the start of visiting.
- virtual void Initialize(SeaGraph* graph);
- CodeGenData* GetData() {
- return llvm_data_;
- }
- void Write(std::string file) {
- llvm_data_->module_.dump();
- llvm::verifyFunction(*llvm_data_->function_);
- }
-
- protected:
- CodeGenData* const llvm_data_;
-};
-
-class CodeGenPrepassVisitor: public CodeGenPassVisitor {
- public:
- explicit CodeGenPrepassVisitor(const std::string& function_name):
- function_name_(function_name) { }
- void Visit(SeaGraph* graph);
- void Visit(SignatureNode* region);
- void Visit(Region* region);
- void Visit(InstructionNode* instruction) { }
-
- void Visit(UnnamedConstInstructionNode* instruction) { }
- void Visit(ConstInstructionNode* instruction) { }
- void Visit(ReturnInstructionNode* instruction) { }
- void Visit(IfNeInstructionNode* instruction) { }
- // void Visit(AddIntLitInstructionNode* instruction) { }
- void Visit(MoveResultInstructionNode* instruction) { }
- void Visit(InvokeStaticInstructionNode* instruction) { }
- void Visit(AddIntInstructionNode* instruction) { }
- void Visit(GotoInstructionNode* instruction) { }
- void Visit(IfEqzInstructionNode* instruction) { }
- void Visit(PhiInstructionNode* region);
-
- private:
- std::string function_name_;
-};
-
-class CodeGenPostpassVisitor: public CodeGenPassVisitor {
- public:
- explicit CodeGenPostpassVisitor(CodeGenData* code_gen_data): CodeGenPassVisitor(code_gen_data) { }
- void Visit(SeaGraph* graph);
- void Visit(SignatureNode* region);
- void Visit(Region* region);
- void Visit(InstructionNode* region) { }
- void Visit(UnnamedConstInstructionNode* instruction) { }
- void Visit(ConstInstructionNode* instruction) { }
- void Visit(ReturnInstructionNode* instruction) { }
- void Visit(IfNeInstructionNode* instruction) { }
- // void Visit(AddIntLitInstructionNode* instruction) { }
- void Visit(MoveResultInstructionNode* instruction) { }
- void Visit(InvokeStaticInstructionNode* instruction) { }
- void Visit(AddIntInstructionNode* instruction) { }
- void Visit(GotoInstructionNode* instruction) { }
- void Visit(IfEqzInstructionNode* instruction) { }
- void Visit(PhiInstructionNode* region);
-};
-
-class CodeGenVisitor: public CodeGenPassVisitor {
- public:
- explicit CodeGenVisitor(CodeGenData* code_gen_data,
- const art::DexFile& dex_file): CodeGenPassVisitor(code_gen_data), dex_file_(dex_file) { }
- void Visit(SeaGraph* graph);
- void Visit(SignatureNode* region);
- void Visit(Region* region);
- void Visit(InstructionNode* region);
- void Visit(UnnamedConstInstructionNode* instruction);
- void Visit(ConstInstructionNode* instruction);
- void Visit(ReturnInstructionNode* instruction);
- void Visit(IfNeInstructionNode* instruction);
- void Visit(MoveResultInstructionNode* instruction);
- void Visit(InvokeStaticInstructionNode* instruction);
- void Visit(AddIntInstructionNode* instruction);
- void Visit(GotoInstructionNode* instruction);
- void Visit(IfEqzInstructionNode* instruction);
- void Visit(PhiInstructionNode* region) { }
-
- private:
- std::string function_name_;
- const art::DexFile& dex_file_;
-};
-} // namespace sea_ir
-#endif // ART_COMPILER_SEA_IR_CODE_GEN_CODE_GEN_H_
diff --git a/compiler/sea_ir/code_gen/code_gen_data.cc b/compiler/sea_ir/code_gen/code_gen_data.cc
deleted file mode 100644
index 17f64db..0000000
--- a/compiler/sea_ir/code_gen/code_gen_data.cc
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <string>
-#include <llvm/PassManager.h>
-#include <llvm/Support/TargetRegistry.h>
-#include <llvm/Support/FormattedStream.h>
-#include <llvm/Target/TargetMachine.h>
-#include <llvm/Transforms/IPO.h>
-#include <llvm/Transforms/IPO/PassManagerBuilder.h>
-
-#include "base/logging.h"
-#include "driver/compiler_driver.h"
-#include "sea_ir/ir/sea.h"
-#include "sea_ir/code_gen/code_gen.h"
-
-
-namespace sea_ir {
-std::string CodeGenData::GetElf(art::InstructionSet instruction_set) {
- std::string elf;
- ::llvm::raw_string_ostream out_stream(elf);
- // Lookup the LLVM target
- std::string target_triple;
- std::string target_cpu;
- std::string target_attr;
- art::CompilerDriver::InstructionSetToLLVMTarget(instruction_set,
- target_triple, target_cpu, target_attr);
-
- std::string errmsg;
- const ::llvm::Target* target =
- ::llvm::TargetRegistry::lookupTarget(target_triple, errmsg);
-
- CHECK(target != NULL) << errmsg;
-
- // Target options
- ::llvm::TargetOptions target_options;
- target_options.FloatABIType = ::llvm::FloatABI::Soft;
- target_options.NoFramePointerElim = true;
- target_options.NoFramePointerElimNonLeaf = true;
- target_options.UseSoftFloat = false;
- target_options.EnableFastISel = false;
-
- // Create the ::llvm::TargetMachine
- ::llvm::OwningPtr< ::llvm::TargetMachine> target_machine(
- target->createTargetMachine(target_triple, target_cpu, target_attr, target_options,
- ::llvm::Reloc::Static, ::llvm::CodeModel::Small,
- ::llvm::CodeGenOpt::Aggressive));
-
- CHECK(target_machine.get() != NULL) << "Failed to create target machine";
-
- // Add target data
- const ::llvm::DataLayout* data_layout = target_machine->getDataLayout();
-
- // PassManager for code generation passes
- ::llvm::PassManager pm;
- pm.add(new ::llvm::DataLayout(*data_layout));
-
- // FunctionPassManager for optimization pass
- ::llvm::FunctionPassManager fpm(&module_);
- fpm.add(new ::llvm::DataLayout(*data_layout));
-
- // Add optimization pass
- ::llvm::PassManagerBuilder pm_builder;
- // TODO: Use inliner after we can do IPO.
- pm_builder.Inliner = NULL;
- // pm_builder.Inliner = ::llvm::createFunctionInliningPass();
- // pm_builder.Inliner = ::llvm::createAlwaysInlinerPass();
- // pm_builder.Inliner = ::llvm::createPartialInliningPass();
- pm_builder.OptLevel = 3;
- pm_builder.DisableSimplifyLibCalls = 1;
- pm_builder.DisableUnitAtATime = 1;
- pm_builder.populateFunctionPassManager(fpm);
- pm_builder.populateModulePassManager(pm);
- pm.add(::llvm::createStripDeadPrototypesPass());
- // Add passes to emit ELF image
- {
- ::llvm::formatted_raw_ostream formatted_os(out_stream, false);
- // Ask the target to add backend passes as necessary.
- if (target_machine->addPassesToEmitFile(pm,
- formatted_os,
- ::llvm::TargetMachine::CGFT_ObjectFile,
- true)) {
- LOG(FATAL) << "Unable to generate ELF for this target";
- }
-
- // Run the code generation passes
- pm.run(module_);
- }
- return elf;
-}
-} // namespace sea_ir
diff --git a/compiler/sea_ir/debug/dot_gen.cc b/compiler/sea_ir/debug/dot_gen.cc
deleted file mode 100644
index 9442684..0000000
--- a/compiler/sea_ir/debug/dot_gen.cc
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#include "scoped_thread_state_change.h"
-#include "sea_ir/debug/dot_gen.h"
-
-namespace sea_ir {
-
-void DotGenerationVisitor::Initialize(SeaGraph* graph) {
- graph_ = graph;
- Region* root_region;
- ordered_regions_.clear();
- for (std::vector<Region*>::const_iterator cit = graph->GetRegions()->begin();
- cit != graph->GetRegions()->end(); cit++ ) {
- if ((*cit)->GetIDominator() == (*cit)) {
- root_region = *cit;
- }
- }
- ordered_regions_.push_back(root_region);
- for (unsigned int id = 0; id < ordered_regions_.size(); id++) {
- Region* current_region = ordered_regions_.at(id);
- const std::set<Region*>* dominated_regions = current_region->GetIDominatedSet();
- for (std::set<Region*>::const_iterator cit = dominated_regions->begin();
- cit != dominated_regions->end(); cit++ ) {
- ordered_regions_.push_back(*cit);
- }
- }
-}
-
-void DotGenerationVisitor::ToDotSSAEdges(InstructionNode* instruction) {
- std::map<int, InstructionNode*>* definition_edges = instruction->GetSSAProducersMap();
- // SSA definitions:
- for (std::map<int, InstructionNode*>::const_iterator
- def_it = definition_edges->begin();
- def_it != definition_edges->end(); def_it++) {
- if (NULL != def_it->second) {
- dot_text_ += def_it->second->StringId() + " -> ";
- dot_text_ += instruction->StringId() + "[color=gray,label=\"";
- dot_text_ += art::StringPrintf("vR = %d", def_it->first);
- art::SafeMap<int, const Type*>::const_iterator type_it = types_->find(def_it->second->Id());
- if (type_it != types_->end()) {
- art::ScopedObjectAccess soa(art::Thread::Current());
- dot_text_ += "(" + type_it->second->Dump() + ")";
- } else {
- dot_text_ += "()";
- }
- dot_text_ += "\"] ; // SSA edge\n";
- }
- }
-
- // SSA used-by:
- if (options_->WillSaveUseEdges()) {
- std::vector<InstructionNode*>* used_in = instruction->GetSSAConsumers();
- for (std::vector<InstructionNode*>::const_iterator cit = used_in->begin();
- cit != used_in->end(); cit++) {
- dot_text_ += (*cit)->StringId() + " -> " + instruction->StringId() + "[color=gray,label=\"";
- dot_text_ += "\"] ; // SSA used-by edge\n";
- }
- }
-}
-
-void DotGenerationVisitor::ToDotSSAEdges(PhiInstructionNode* instruction) {
- std::vector<InstructionNode*> definition_edges = instruction->GetSSAProducers();
- // SSA definitions:
- for (std::vector<InstructionNode*>::const_iterator
- def_it = definition_edges.begin();
- def_it != definition_edges.end(); def_it++) {
- if (NULL != *def_it) {
- dot_text_ += (*def_it)->StringId() + " -> ";
- dot_text_ += instruction->StringId() + "[color=gray,label=\"";
- dot_text_ += art::StringPrintf("vR = %d", instruction->GetRegisterNumber());
- art::SafeMap<int, const Type*>::const_iterator type_it = types_->find((*def_it)->Id());
- if (type_it != types_->end()) {
- art::ScopedObjectAccess soa(art::Thread::Current());
- dot_text_ += "(" + type_it->second->Dump() + ")";
- } else {
- dot_text_ += "()";
- }
- dot_text_ += "\"] ; // SSA edge\n";
- }
- }
-
- // SSA used-by:
- if (options_->WillSaveUseEdges()) {
- std::vector<InstructionNode*>* used_in = instruction->GetSSAConsumers();
- for (std::vector<InstructionNode*>::const_iterator cit = used_in->begin();
- cit != used_in->end(); cit++) {
- dot_text_ += (*cit)->StringId() + " -> " + instruction->StringId() + "[color=gray,label=\"";
- dot_text_ += "\"] ; // SSA used-by edge\n";
- }
- }
-}
-
-void DotGenerationVisitor::Visit(SignatureNode* parameter) {
- dot_text_ += parameter->StringId() +" [label=\"[" + parameter->StringId() + "] signature:";
- dot_text_ += art::StringPrintf("r%d", parameter->GetResultRegister());
- dot_text_ += "\"] // signature node\n";
- ToDotSSAEdges(parameter);
-}
-
-// Appends to @result a dot language formatted string representing the node and
-// (by convention) outgoing edges, so that the composition of theToDot() of all nodes
-// builds a complete dot graph (without prolog and epilog though).
-void DotGenerationVisitor::Visit(Region* region) {
- dot_text_ += "\n// Region: \nsubgraph " + region->StringId();
- dot_text_ += " { label=\"region " + region->StringId() + "(rpo=";
- dot_text_ += art::StringPrintf("%d", region->GetRPO());
- if (NULL != region->GetIDominator()) {
- dot_text_ += " dom=" + region->GetIDominator()->StringId();
- }
- dot_text_ += ")\";\n";
-
- std::vector<PhiInstructionNode*>* phi_instructions = region->GetPhiNodes();
- for (std::vector<PhiInstructionNode*>::const_iterator cit = phi_instructions->begin();
- cit != phi_instructions->end(); cit++) {
- dot_text_ += (*cit)->StringId() +";\n";
- }
- std::vector<InstructionNode*>* instructions = region->GetInstructions();
- for (std::vector<InstructionNode*>::const_iterator cit = instructions->begin();
- cit != instructions->end(); cit++) {
- dot_text_ += (*cit)->StringId() +";\n";
- }
-
- dot_text_ += "} // End Region.\n";
- std::vector<Region*>* successors = region->GetSuccessors();
- for (std::vector<Region*>::const_iterator cit = successors->begin(); cit != successors->end();
- cit++) {
- DCHECK(NULL != *cit) << "Null successor found for SeaNode" <<
- region->GetLastChild()->StringId() << ".";
- dot_text_ += region->GetLastChild()->StringId() + " -> " +
- (*cit)->GetLastChild()->StringId() +
- "[lhead=" + (*cit)->StringId() + ", " + "ltail=" + region->StringId() + "];\n\n";
- }
-}
-void DotGenerationVisitor::Visit(InstructionNode* instruction) {
- dot_text_ += "// Instruction ("+instruction->StringId()+"): \n" + instruction->StringId() +
- " [label=\"[" + instruction->StringId() + "] " +
- instruction->GetInstruction()->DumpString(graph_->GetDexFile()) + "\"";
- dot_text_ += "];\n";
- ToDotSSAEdges(instruction);
-}
-
-void DotGenerationVisitor::Visit(UnnamedConstInstructionNode* instruction) {
- dot_text_ += "// Instruction ("+instruction->StringId()+"): \n" + instruction->StringId() +
- " [label=\"[" + instruction->StringId() + "] const/x v-3, #" +
- art::StringPrintf("%d", instruction->GetConstValue()) + "\"";
- dot_text_ += "];\n";
- ToDotSSAEdges(instruction);
-}
-
-void DotGenerationVisitor::Visit(PhiInstructionNode* phi) {
- dot_text_ += "// PhiInstruction: \n" + phi->StringId() +
- " [label=\"[" + phi->StringId() + "] PHI(";
- dot_text_ += art::StringPrintf("%d", phi->GetRegisterNumber());
- dot_text_ += ")\"";
- dot_text_ += "];\n";
- ToDotSSAEdges(phi);
-}
-} // namespace sea_ir
diff --git a/compiler/sea_ir/debug/dot_gen.h b/compiler/sea_ir/debug/dot_gen.h
deleted file mode 100644
index a5d6819..0000000
--- a/compiler/sea_ir/debug/dot_gen.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_SEA_IR_DEBUG_DOT_GEN_H_
-#define ART_COMPILER_SEA_IR_DEBUG_DOT_GEN_H_
-
-#include "safe_map.h"
-#include "base/stringprintf.h"
-#include "file_output_stream.h"
-#include "os.h"
-#include "sea_ir/ir/sea.h"
-#include "sea_ir/types/type_inference.h"
-
-namespace sea_ir {
-
-class DotConversionOptions {
- public:
- DotConversionOptions(): save_use_edges_(false) { }
- bool WillSaveUseEdges() const {
- return save_use_edges_;
- }
- private:
- bool save_use_edges_;
-};
-
-class DotGenerationVisitor: public IRVisitor {
- public:
- explicit DotGenerationVisitor(const DotConversionOptions* const options,
- art::SafeMap<int, const Type*>* types): graph_(), types_(types), options_(options) { }
-
- virtual void Initialize(SeaGraph* graph);
- // Saves the ssa def->use edges corresponding to @instruction.
- void ToDotSSAEdges(InstructionNode* instruction);
- void ToDotSSAEdges(PhiInstructionNode* instruction);
- void Visit(SeaGraph* graph) {
- dot_text_ += "digraph seaOfNodes {\ncompound=true\n";
- }
- void Visit(SignatureNode* parameter);
-
- // Appends to @result a dot language formatted string representing the node and
- // (by convention) outgoing edges, so that the composition of theToDot() of all nodes
- // builds a complete dot graph (without prolog and epilog though).
- void Visit(Region* region);
- void Visit(InstructionNode* instruction);
- void Visit(PhiInstructionNode* phi);
- void Visit(UnnamedConstInstructionNode* instruction);
-
- void Visit(ConstInstructionNode* instruction) {
- Visit(reinterpret_cast<InstructionNode*>(instruction));
- }
- void Visit(ReturnInstructionNode* instruction) {
- Visit(reinterpret_cast<InstructionNode*>(instruction));
- }
- void Visit(IfNeInstructionNode* instruction) {
- Visit(reinterpret_cast<InstructionNode*>(instruction));
- }
- void Visit(MoveResultInstructionNode* instruction) {
- Visit(reinterpret_cast<InstructionNode*>(instruction));
- }
- void Visit(InvokeStaticInstructionNode* instruction) {
- Visit(reinterpret_cast<InstructionNode*>(instruction));
- }
- void Visit(AddIntInstructionNode* instruction) {
- Visit(reinterpret_cast<InstructionNode*>(instruction));
- }
- void Visit(GotoInstructionNode* instruction) {
- Visit(reinterpret_cast<InstructionNode*>(instruction));
- }
- void Visit(IfEqzInstructionNode* instruction) {
- Visit(reinterpret_cast<InstructionNode*>(instruction));
- }
-
- std::string GetResult() const {
- return dot_text_;
- }
-
- private:
- std::string dot_text_;
- SeaGraph* graph_;
- art::SafeMap<int, const Type*>* types_;
- const DotConversionOptions* const options_;
-};
-
-// Stores options for turning a SEA IR graph to a .dot file.
-class DotConversion {
- public:
- DotConversion(): options_() { }
- // Saves to @filename the .dot representation of @graph with the options @options.
- void DumpSea(SeaGraph* graph, std::string filename,
- art::SafeMap<int, const Type*>* types) const {
- LOG(INFO) << "Starting to write SEA string to file " << filename << std::endl;
- DotGenerationVisitor dgv = DotGenerationVisitor(&options_, types);
- graph->Accept(&dgv);
- // TODO: std::unique_ptr to close file properly. Switch to BufferedOutputStream.
- art::File* file = art::OS::CreateEmptyFile(filename.c_str());
- art::FileOutputStream fos(file);
- std::string graph_as_string = dgv.GetResult();
- graph_as_string += "}";
- fos.WriteFully(graph_as_string.c_str(), graph_as_string.size());
- LOG(INFO) << "Written SEA string to file.";
- }
-
- private:
- DotConversionOptions options_;
-};
-
-} // namespace sea_ir
-#endif // ART_COMPILER_SEA_IR_DEBUG_DOT_GEN_H_
diff --git a/compiler/sea_ir/frontend.cc b/compiler/sea_ir/frontend.cc
deleted file mode 100644
index b57007b..0000000
--- a/compiler/sea_ir/frontend.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifdef ART_SEA_IR_MODE
-#include <llvm/Support/Threading.h>
-#include <llvm/Support/raw_ostream.h>
-#include <llvm/Bitcode/ReaderWriter.h>
-
-#include "base/logging.h"
-#include "llvm/llvm_compilation_unit.h"
-#include "dex/portable/mir_to_gbc.h"
-#include "driver/compiler_driver.h"
-#include "verifier/method_verifier.h"
-#include "mirror/object.h"
-#include "utils.h"
-
-#include "runtime.h"
-#include "safe_map.h"
-
-#include "sea_ir/ir/sea.h"
-#include "sea_ir/debug/dot_gen.h"
-#include "sea_ir/types/types.h"
-#include "sea_ir/code_gen/code_gen.h"
-
-namespace art {
-
-static CompiledMethod* CompileMethodWithSeaIr(CompilerDriver& compiler,
- CompilerBackend* compiler_backend,
- const DexFile::CodeItem* code_item,
- uint32_t method_access_flags, InvokeType invoke_type,
- uint16_t class_def_idx, uint32_t method_idx,
- jobject class_loader, const DexFile& dex_file,
- void* llvm_compilation_unit) {
- LOG(INFO) << "Compiling " << PrettyMethod(method_idx, dex_file) << ".";
- sea_ir::SeaGraph* ir_graph = sea_ir::SeaGraph::GetGraph(dex_file);
- std::string symbol = "dex_" + MangleForJni(PrettyMethod(method_idx, dex_file));
- sea_ir::CodeGenData* llvm_data = ir_graph->CompileMethod(symbol,
- code_item, class_def_idx, method_idx, method_access_flags, dex_file);
- sea_ir::DotConversion dc;
- SafeMap<int, const sea_ir::Type*>* types = ir_graph->ti_->GetTypeMap();
- dc.DumpSea(ir_graph, "/tmp/temp.dot", types);
- MethodReference mref(&dex_file, method_idx);
- std::string llvm_code = llvm_data->GetElf(compiler.GetInstructionSet());
- CompiledMethod* compiled_method =
- new CompiledMethod(compiler, compiler.GetInstructionSet(), llvm_code,
- *compiler.GetVerifiedMethodsData()->GetDexGcMap(mref), symbol);
- LOG(INFO) << "Compiled SEA IR method " << PrettyMethod(method_idx, dex_file) << ".";
- return compiled_method;
-}
-
-CompiledMethod* SeaIrCompileOneMethod(CompilerDriver& compiler,
- CompilerBackend* backend,
- const DexFile::CodeItem* code_item,
- uint32_t method_access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const DexFile& dex_file,
- void* llvm_compilation_unit) {
- return CompileMethodWithSeaIr(compiler, backend, code_item, method_access_flags, invoke_type,
- class_def_idx, method_idx, class_loader, dex_file, llvm_compilation_unit);
-}
-
-extern "C" art::CompiledMethod*
- SeaIrCompileMethod(art::CompilerDriver& compiler,
- const art::DexFile::CodeItem* code_item,
- uint32_t method_access_flags, art::InvokeType invoke_type,
- uint16_t class_def_idx, uint32_t method_idx, jobject class_loader,
- const art::DexFile& dex_file) {
- // TODO: Check method fingerprint here to determine appropriate backend type.
- // Until then, use build default
- art::CompilerBackend* backend = compiler.GetCompilerBackend();
- return art::SeaIrCompileOneMethod(compiler, backend, code_item, method_access_flags, invoke_type,
- class_def_idx, method_idx, class_loader, dex_file,
- NULL /* use thread llvm_info */);
-}
-#endif
-
-} // namespace art
diff --git a/compiler/sea_ir/ir/instruction_nodes.h b/compiler/sea_ir/ir/instruction_nodes.h
deleted file mode 100644
index 63e89e7..0000000
--- a/compiler/sea_ir/ir/instruction_nodes.h
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_SEA_IR_IR_INSTRUCTION_NODES_H_
-#define ART_COMPILER_SEA_IR_IR_INSTRUCTION_NODES_H_
-#include "dex_instruction-inl.h"
-#include "sea_ir/ir/sea_node.h"
-#include "sea_ir/ir/visitor.h"
-
-
-namespace sea_ir {
-
-enum SpecialRegisters {
- NO_REGISTER = -1, // Usually signifies that there is no register
- // that respects the condition you asked for.
- RETURN_REGISTER = -2, // Written by the invoke* instructions, read by move-results.
- UNNAMED_CONST_REGISTER = -3 // Written by UnnamedConst* instructions, read by *Lit* instruction.
-};
-
-class IRVisitor;
-
-// This class represents an instruction in SEA IR.
-// As we add support for specific classes of instructions,
-// the number of InstructionNode objects should dwindle, while the
-// number of subclasses and instances of subclasses will go up.
-class InstructionNode: public SeaNode {
- public:
- static std::vector<sea_ir::InstructionNode*> Create(const art::Instruction* in);
- // Returns the Dalvik instruction around which this InstructionNode is wrapped.
- const art::Instruction* GetInstruction() const {
- DCHECK(NULL != instruction_) << "Tried to access NULL instruction in an InstructionNode.";
- return instruction_;
- }
- // Returns the register that is defined by the current instruction, or NO_REGISTER otherwise.
- virtual int GetResultRegister() const;
- // Returns the set of registers defined by the current instruction.
- virtual std::vector<int> GetDefinitions() const;
- // Returns the set of register numbers that are used by the instruction.
- virtual std::vector<int> GetUses() const;
- // Mark the current instruction as a downward exposed definition.
- void MarkAsDEDef();
- // Rename the use of @reg_no to refer to the instruction @definition,
- // essentially creating SSA form.
- void RenameToSSA(int reg_no, InstructionNode* definition) {
- definition_edges_.insert(std::pair<int, InstructionNode*>(reg_no, definition));
- DCHECK(NULL != definition) << "SSA definition for register " << reg_no
- << " used in instruction " << Id() << " not found.";
- definition->AddSSAUse(this);
- }
- // Returns the ordered set of Instructions that define the input operands of this instruction.
- // Precondition: SeaGraph.ConvertToSSA().
- virtual std::vector<InstructionNode*> GetSSAProducers() {
- std::vector<int> uses = GetUses();
- std::vector<InstructionNode*> ssa_uses;
- for (std::vector<int>::const_iterator cit = uses.begin(); cit != uses.end(); cit++) {
- ssa_uses.push_back((*definition_edges_.find(*cit)).second);
- }
- return ssa_uses;
- }
- std::map<int, InstructionNode* >* GetSSAProducersMap() {
- return &definition_edges_;
- }
- std::vector<InstructionNode*>* GetSSAConsumers() {
- return &used_in_;
- }
- virtual void AddSSAUse(InstructionNode* use) {
- used_in_.push_back(use);
- }
- void Accept(IRVisitor* v) {
- v->Visit(this);
- v->Traverse(this);
- }
- // Set the region to which this instruction belongs.
- Region* GetRegion() {
- DCHECK(NULL != region_);
- return region_;
- }
- // Get the region to which this instruction belongs.
- void SetRegion(Region* region) {
- region_ = region;
- }
-
- protected:
- explicit InstructionNode(const art::Instruction* in):
- SeaNode(), instruction_(in), used_in_(), de_def_(false), region_(NULL) { }
-
- protected:
- const art::Instruction* const instruction_;
- std::map<int, InstructionNode* > definition_edges_; // Maps used registers to their definitions.
- // Stores pointers to instructions that use the result of the current instruction.
- std::vector<InstructionNode*> used_in_;
- bool de_def_;
- Region* region_;
-};
-
-class ConstInstructionNode: public InstructionNode {
- public:
- explicit ConstInstructionNode(const art::Instruction* inst):
- InstructionNode(inst) { }
-
- void Accept(IRVisitor* v) {
- v->Visit(this);
- v->Traverse(this);
- }
-
- virtual int32_t GetConstValue() const {
- return GetInstruction()->VRegB_11n();
- }
-};
-
-class UnnamedConstInstructionNode: public ConstInstructionNode {
- public:
- explicit UnnamedConstInstructionNode(const art::Instruction* inst, int32_t value):
- ConstInstructionNode(inst), value_(value) { }
-
- void Accept(IRVisitor* v) {
- v->Visit(this);
- v->Traverse(this);
- }
-
- int GetResultRegister() const {
- return UNNAMED_CONST_REGISTER;
- }
-
- int32_t GetConstValue() const {
- return value_;
- }
-
- private:
- const int32_t value_;
-};
-
-class ReturnInstructionNode: public InstructionNode {
- public:
- explicit ReturnInstructionNode(const art::Instruction* inst): InstructionNode(inst) { }
- void Accept(IRVisitor* v) {
- v->Visit(this);
- v->Traverse(this);
- }
-};
-
-class IfNeInstructionNode: public InstructionNode {
- public:
- explicit IfNeInstructionNode(const art::Instruction* inst): InstructionNode(inst) {
- DCHECK(InstructionTools::IsDefinition(inst) == false);
- }
- void Accept(IRVisitor* v) {
- v->Visit(this);
- v->Traverse(this);
- }
-};
-
-
-
-class MoveResultInstructionNode: public InstructionNode {
- public:
- explicit MoveResultInstructionNode(const art::Instruction* inst): InstructionNode(inst) { }
- std::vector<int> GetUses() const {
- std::vector<int> uses; // Using vector<> instead of set<> because order matters.
- uses.push_back(RETURN_REGISTER);
- return uses;
- }
- void Accept(IRVisitor* v) {
- v->Visit(this);
- v->Traverse(this);
- }
-};
-
-class InvokeStaticInstructionNode: public InstructionNode {
- public:
- explicit InvokeStaticInstructionNode(const art::Instruction* inst): InstructionNode(inst),
- method_index_(inst->VRegB_35c()) { }
- int GetResultRegister() const {
- return RETURN_REGISTER;
- }
-
- int GetCalledMethodIndex() const {
- return method_index_;
- }
- void Accept(IRVisitor* v) {
- v->Visit(this);
- v->Traverse(this);
- }
-
- private:
- const uint32_t method_index_;
-};
-
-class AddIntInstructionNode: public InstructionNode {
- public:
- explicit AddIntInstructionNode(const art::Instruction* inst): InstructionNode(inst) { }
- void Accept(IRVisitor* v) {
- v->Visit(this);
- v->Traverse(this);
- }
-};
-
-class AddIntLitInstructionNode: public AddIntInstructionNode {
- public:
- explicit AddIntLitInstructionNode(const art::Instruction* inst):
- AddIntInstructionNode(inst) { }
-
- std::vector<int> GetUses() const {
- std::vector<int> uses = AddIntInstructionNode::GetUses();
- uses.push_back(UNNAMED_CONST_REGISTER);
- return uses;
- }
-
- void Accept(IRVisitor* v) {
- v->Visit(this);
- v->Traverse(this);
- }
-};
-
-class GotoInstructionNode: public InstructionNode {
- public:
- explicit GotoInstructionNode(const art::Instruction* inst): InstructionNode(inst) { }
- void Accept(IRVisitor* v) {
- v->Visit(this);
- v->Traverse(this);
- }
-};
-
-class IfEqzInstructionNode: public InstructionNode {
- public:
- explicit IfEqzInstructionNode(const art::Instruction* inst): InstructionNode(inst) {
- DCHECK(InstructionTools::IsDefinition(inst) == false);
- }
- void Accept(IRVisitor* v) {
- v->Visit(this);
- v->Traverse(this);
- }
-};
-} // namespace sea_ir
-#endif // ART_COMPILER_SEA_IR_IR_INSTRUCTION_NODES_H_
diff --git a/compiler/sea_ir/ir/instruction_tools.cc b/compiler/sea_ir/ir/instruction_tools.cc
deleted file mode 100644
index 143209d..0000000
--- a/compiler/sea_ir/ir/instruction_tools.cc
+++ /dev/null
@@ -1,797 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "sea_ir/ir/instruction_tools.h"
-
-namespace sea_ir {
-
-bool InstructionTools::IsDefinition(const art::Instruction* const instruction) {
- if (0 != (InstructionTools::instruction_attributes_[instruction->Opcode()] & (1 << kDA))) {
- return true;
- }
- return false;
-}
-
-const int InstructionTools::instruction_attributes_[] = {
- // 00 NOP
- DF_NOP,
-
- // 01 MOVE vA, vB
- DF_DA | DF_UB | DF_IS_MOVE,
-
- // 02 MOVE_FROM16 vAA, vBBBB
- DF_DA | DF_UB | DF_IS_MOVE,
-
- // 03 MOVE_16 vAAAA, vBBBB
- DF_DA | DF_UB | DF_IS_MOVE,
-
- // 04 MOVE_WIDE vA, vB
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_IS_MOVE,
-
- // 05 MOVE_WIDE_FROM16 vAA, vBBBB
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_IS_MOVE,
-
- // 06 MOVE_WIDE_16 vAAAA, vBBBB
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_IS_MOVE,
-
- // 07 MOVE_OBJECT vA, vB
- DF_DA | DF_UB | DF_NULL_TRANSFER_0 | DF_IS_MOVE | DF_REF_A | DF_REF_B,
-
- // 08 MOVE_OBJECT_FROM16 vAA, vBBBB
- DF_DA | DF_UB | DF_NULL_TRANSFER_0 | DF_IS_MOVE | DF_REF_A | DF_REF_B,
-
- // 09 MOVE_OBJECT_16 vAAAA, vBBBB
- DF_DA | DF_UB | DF_NULL_TRANSFER_0 | DF_IS_MOVE | DF_REF_A | DF_REF_B,
-
- // 0A MOVE_RESULT vAA
- DF_DA,
-
- // 0B MOVE_RESULT_WIDE vAA
- DF_DA | DF_A_WIDE,
-
- // 0C MOVE_RESULT_OBJECT vAA
- DF_DA | DF_REF_A,
-
- // 0D MOVE_EXCEPTION vAA
- DF_DA | DF_REF_A | DF_NON_NULL_DST,
-
- // 0E RETURN_VOID
- DF_NOP,
-
- // 0F RETURN vAA
- DF_UA,
-
- // 10 RETURN_WIDE vAA
- DF_UA | DF_A_WIDE,
-
- // 11 RETURN_OBJECT vAA
- DF_UA | DF_REF_A,
-
- // 12 CONST_4 vA, #+B
- DF_DA | DF_SETS_CONST,
-
- // 13 CONST_16 vAA, #+BBBB
- DF_DA | DF_SETS_CONST,
-
- // 14 CONST vAA, #+BBBBBBBB
- DF_DA | DF_SETS_CONST,
-
- // 15 CONST_HIGH16 VAA, #+BBBB0000
- DF_DA | DF_SETS_CONST,
-
- // 16 CONST_WIDE_16 vAA, #+BBBB
- DF_DA | DF_A_WIDE | DF_SETS_CONST,
-
- // 17 CONST_WIDE_32 vAA, #+BBBBBBBB
- DF_DA | DF_A_WIDE | DF_SETS_CONST,
-
- // 18 CONST_WIDE vAA, #+BBBBBBBBBBBBBBBB
- DF_DA | DF_A_WIDE | DF_SETS_CONST,
-
- // 19 CONST_WIDE_HIGH16 vAA, #+BBBB000000000000
- DF_DA | DF_A_WIDE | DF_SETS_CONST,
-
- // 1A CONST_STRING vAA, string@BBBB
- DF_DA | DF_REF_A | DF_NON_NULL_DST,
-
- // 1B CONST_STRING_JUMBO vAA, string@BBBBBBBB
- DF_DA | DF_REF_A | DF_NON_NULL_DST,
-
- // 1C CONST_CLASS vAA, type@BBBB
- DF_DA | DF_REF_A | DF_NON_NULL_DST,
-
- // 1D MONITOR_ENTER vAA
- DF_UA | DF_NULL_CHK_0 | DF_REF_A,
-
- // 1E MONITOR_EXIT vAA
- DF_UA | DF_NULL_CHK_0 | DF_REF_A,
-
- // 1F CHK_CAST vAA, type@BBBB
- DF_UA | DF_REF_A | DF_UMS,
-
- // 20 INSTANCE_OF vA, vB, type@CCCC
- DF_DA | DF_UB | DF_CORE_A | DF_REF_B | DF_UMS,
-
- // 21 ARRAY_LENGTH vA, vB
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_CORE_A | DF_REF_B,
-
- // 22 NEW_INSTANCE vAA, type@BBBB
- DF_DA | DF_NON_NULL_DST | DF_REF_A | DF_UMS,
-
- // 23 NEW_ARRAY vA, vB, type@CCCC
- DF_DA | DF_UB | DF_NON_NULL_DST | DF_REF_A | DF_CORE_B | DF_UMS,
-
- // 24 FILLED_NEW_ARRAY {vD, vE, vF, vG, vA}
- DF_FORMAT_35C | DF_NON_NULL_RET | DF_UMS,
-
- // 25 FILLED_NEW_ARRAY_RANGE {vCCCC .. vNNNN}, type@BBBB
- DF_FORMAT_3RC | DF_NON_NULL_RET | DF_UMS,
-
- // 26 FILL_ARRAY_DATA vAA, +BBBBBBBB
- DF_UA | DF_REF_A | DF_UMS,
-
- // 27 THROW vAA
- DF_UA | DF_REF_A | DF_UMS,
-
- // 28 GOTO
- DF_NOP,
-
- // 29 GOTO_16
- DF_NOP,
-
- // 2A GOTO_32
- DF_NOP,
-
- // 2B PACKED_SWITCH vAA, +BBBBBBBB
- DF_UA,
-
- // 2C SPARSE_SWITCH vAA, +BBBBBBBB
- DF_UA,
-
- // 2D CMPL_FLOAT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_FP_B | DF_FP_C | DF_CORE_A,
-
- // 2E CMPG_FLOAT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_FP_B | DF_FP_C | DF_CORE_A,
-
- // 2F CMPL_DOUBLE vAA, vBB, vCC
- DF_DA | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_B | DF_FP_C | DF_CORE_A,
-
- // 30 CMPG_DOUBLE vAA, vBB, vCC
- DF_DA | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_B | DF_FP_C | DF_CORE_A,
-
- // 31 CMP_LONG vAA, vBB, vCC
- DF_DA | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // 32 IF_EQ vA, vB, +CCCC
- DF_UA | DF_UB,
-
- // 33 IF_NE vA, vB, +CCCC
- DF_UA | DF_UB,
-
- // 34 IF_LT vA, vB, +CCCC
- DF_UA | DF_UB,
-
- // 35 IF_GE vA, vB, +CCCC
- DF_UA | DF_UB,
-
- // 36 IF_GT vA, vB, +CCCC
- DF_UA | DF_UB,
-
- // 37 IF_LE vA, vB, +CCCC
- DF_UA | DF_UB,
-
- // 38 IF_EQZ vAA, +BBBB
- DF_UA,
-
- // 39 IF_NEZ vAA, +BBBB
- DF_UA,
-
- // 3A IF_LTZ vAA, +BBBB
- DF_UA,
-
- // 3B IF_GEZ vAA, +BBBB
- DF_UA,
-
- // 3C IF_GTZ vAA, +BBBB
- DF_UA,
-
- // 3D IF_LEZ vAA, +BBBB
- DF_UA,
-
- // 3E UNUSED_3E
- DF_NOP,
-
- // 3F UNUSED_3F
- DF_NOP,
-
- // 40 UNUSED_40
- DF_NOP,
-
- // 41 UNUSED_41
- DF_NOP,
-
- // 42 UNUSED_42
- DF_NOP,
-
- // 43 UNUSED_43
- DF_NOP,
-
- // 44 AGET vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
-
- // 45 AGET_WIDE vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
-
- // 46 AGET_OBJECT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_A | DF_REF_B | DF_CORE_C,
-
- // 47 AGET_BOOLEAN vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
-
- // 48 AGET_BYTE vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
-
- // 49 AGET_CHAR vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
-
- // 4A AGET_SHORT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
-
- // 4B APUT vAA, vBB, vCC
- DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C,
-
- // 4C APUT_WIDE vAA, vBB, vCC
- DF_UA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_2 | DF_RANGE_CHK_3 | DF_REF_B | DF_CORE_C,
-
- // 4D APUT_OBJECT vAA, vBB, vCC
- DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_A | DF_REF_B | DF_CORE_C,
-
- // 4E APUT_BOOLEAN vAA, vBB, vCC
- DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C,
-
- // 4F APUT_BYTE vAA, vBB, vCC
- DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C,
-
- // 50 APUT_CHAR vAA, vBB, vCC
- DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C,
-
- // 51 APUT_SHORT vAA, vBB, vCC
- DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C,
-
- // 52 IGET vA, vB, field@CCCC
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
-
- // 53 IGET_WIDE vA, vB, field@CCCC
- DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
-
- // 54 IGET_OBJECT vA, vB, field@CCCC
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B,
-
- // 55 IGET_BOOLEAN vA, vB, field@CCCC
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
-
- // 56 IGET_BYTE vA, vB, field@CCCC
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
-
- // 57 IGET_CHAR vA, vB, field@CCCC
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
-
- // 58 IGET_SHORT vA, vB, field@CCCC
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
-
- // 59 IPUT vA, vB, field@CCCC
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
-
- // 5A IPUT_WIDE vA, vB, field@CCCC
- DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B,
-
- // 5B IPUT_OBJECT vA, vB, field@CCCC
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_A | DF_REF_B,
-
- // 5C IPUT_BOOLEAN vA, vB, field@CCCC
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
-
- // 5D IPUT_BYTE vA, vB, field@CCCC
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
-
- // 5E IPUT_CHAR vA, vB, field@CCCC
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
-
- // 5F IPUT_SHORT vA, vB, field@CCCC
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
-
- // 60 SGET vAA, field@BBBB
- DF_DA | DF_UMS,
-
- // 61 SGET_WIDE vAA, field@BBBB
- DF_DA | DF_A_WIDE | DF_UMS,
-
- // 62 SGET_OBJECT vAA, field@BBBB
- DF_DA | DF_REF_A | DF_UMS,
-
- // 63 SGET_BOOLEAN vAA, field@BBBB
- DF_DA | DF_UMS,
-
- // 64 SGET_BYTE vAA, field@BBBB
- DF_DA | DF_UMS,
-
- // 65 SGET_CHAR vAA, field@BBBB
- DF_DA | DF_UMS,
-
- // 66 SGET_SHORT vAA, field@BBBB
- DF_DA | DF_UMS,
-
- // 67 SPUT vAA, field@BBBB
- DF_UA | DF_UMS,
-
- // 68 SPUT_WIDE vAA, field@BBBB
- DF_UA | DF_A_WIDE | DF_UMS,
-
- // 69 SPUT_OBJECT vAA, field@BBBB
- DF_UA | DF_REF_A | DF_UMS,
-
- // 6A SPUT_BOOLEAN vAA, field@BBBB
- DF_UA | DF_UMS,
-
- // 6B SPUT_BYTE vAA, field@BBBB
- DF_UA | DF_UMS,
-
- // 6C SPUT_CHAR vAA, field@BBBB
- DF_UA | DF_UMS,
-
- // 6D SPUT_SHORT vAA, field@BBBB
- DF_UA | DF_UMS,
-
- // 6E INVOKE_VIRTUAL {vD, vE, vF, vG, vA}
- DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
-
- // 6F INVOKE_SUPER {vD, vE, vF, vG, vA}
- DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
-
- // 70 INVOKE_DIRECT {vD, vE, vF, vG, vA}
- DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
-
- // 71 INVOKE_STATIC {vD, vE, vF, vG, vA}
- DF_FORMAT_35C | DF_UMS,
-
- // 72 INVOKE_INTERFACE {vD, vE, vF, vG, vA}
- DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
-
- // 73 UNUSED_73
- DF_NOP,
-
- // 74 INVOKE_VIRTUAL_RANGE {vCCCC .. vNNNN}
- DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
-
- // 75 INVOKE_SUPER_RANGE {vCCCC .. vNNNN}
- DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
-
- // 76 INVOKE_DIRECT_RANGE {vCCCC .. vNNNN}
- DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
-
- // 77 INVOKE_STATIC_RANGE {vCCCC .. vNNNN}
- DF_FORMAT_3RC | DF_UMS,
-
- // 78 INVOKE_INTERFACE_RANGE {vCCCC .. vNNNN}
- DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
-
- // 79 UNUSED_79
- DF_NOP,
-
- // 7A UNUSED_7A
- DF_NOP,
-
- // 7B NEG_INT vA, vB
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // 7C NOT_INT vA, vB
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // 7D NEG_LONG vA, vB
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
- // 7E NOT_LONG vA, vB
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
- // 7F NEG_FLOAT vA, vB
- DF_DA | DF_UB | DF_FP_A | DF_FP_B,
-
- // 80 NEG_DOUBLE vA, vB
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
-
- // 81 INT_TO_LONG vA, vB
- DF_DA | DF_A_WIDE | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // 82 INT_TO_FLOAT vA, vB
- DF_DA | DF_UB | DF_FP_A | DF_CORE_B,
-
- // 83 INT_TO_DOUBLE vA, vB
- DF_DA | DF_A_WIDE | DF_UB | DF_FP_A | DF_CORE_B,
-
- // 84 LONG_TO_INT vA, vB
- DF_DA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
- // 85 LONG_TO_FLOAT vA, vB
- DF_DA | DF_UB | DF_B_WIDE | DF_FP_A | DF_CORE_B,
-
- // 86 LONG_TO_DOUBLE vA, vB
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_CORE_B,
-
- // 87 FLOAT_TO_INT vA, vB
- DF_DA | DF_UB | DF_FP_B | DF_CORE_A,
-
- // 88 FLOAT_TO_LONG vA, vB
- DF_DA | DF_A_WIDE | DF_UB | DF_FP_B | DF_CORE_A,
-
- // 89 FLOAT_TO_DOUBLE vA, vB
- DF_DA | DF_A_WIDE | DF_UB | DF_FP_A | DF_FP_B,
-
- // 8A DOUBLE_TO_INT vA, vB
- DF_DA | DF_UB | DF_B_WIDE | DF_FP_B | DF_CORE_A,
-
- // 8B DOUBLE_TO_LONG vA, vB
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_B | DF_CORE_A,
-
- // 8C DOUBLE_TO_FLOAT vA, vB
- DF_DA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
-
- // 8D INT_TO_BYTE vA, vB
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // 8E INT_TO_CHAR vA, vB
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // 8F INT_TO_SHORT vA, vB
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // 90 ADD_INT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // 91 SUB_INT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // 92 MUL_INT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // 93 DIV_INT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // 94 REM_INT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // 95 AND_INT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // 96 OR_INT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // 97 XOR_INT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // 98 SHL_INT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // 99 SHR_INT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // 9A USHR_INT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // 9B ADD_LONG vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // 9C SUB_LONG vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // 9D MUL_LONG vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // 9E DIV_LONG vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // 9F REM_LONG vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // A0 AND_LONG vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // A1 OR_LONG vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // A2 XOR_LONG vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // A3 SHL_LONG vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // A4 SHR_LONG vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // A5 USHR_LONG vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
- // A6 ADD_FLOAT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
-
- // A7 SUB_FLOAT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
-
- // A8 MUL_FLOAT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
-
- // A9 DIV_FLOAT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
-
- // AA REM_FLOAT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
-
- // AB ADD_DOUBLE vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
-
- // AC SUB_DOUBLE vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
-
- // AD MUL_DOUBLE vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
-
- // AE DIV_DOUBLE vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
-
- // AF REM_DOUBLE vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
-
- // B0 ADD_INT_2ADDR vA, vB
- DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // B1 SUB_INT_2ADDR vA, vB
- DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // B2 MUL_INT_2ADDR vA, vB
- DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // B3 DIV_INT_2ADDR vA, vB
- DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // B4 REM_INT_2ADDR vA, vB
- DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // B5 AND_INT_2ADDR vA, vB
- DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // B6 OR_INT_2ADDR vA, vB
- DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // B7 XOR_INT_2ADDR vA, vB
- DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // B8 SHL_INT_2ADDR vA, vB
- DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // B9 SHR_INT_2ADDR vA, vB
- DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // BA USHR_INT_2ADDR vA, vB
- DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // BB ADD_LONG_2ADDR vA, vB
- DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
- // BC SUB_LONG_2ADDR vA, vB
- DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
- // BD MUL_LONG_2ADDR vA, vB
- DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
- // BE DIV_LONG_2ADDR vA, vB
- DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
- // BF REM_LONG_2ADDR vA, vB
- DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
- // C0 AND_LONG_2ADDR vA, vB
- DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
- // C1 OR_LONG_2ADDR vA, vB
- DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
- // C2 XOR_LONG_2ADDR vA, vB
- DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
- // C3 SHL_LONG_2ADDR vA, vB
- DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // C4 SHR_LONG_2ADDR vA, vB
- DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // C5 USHR_LONG_2ADDR vA, vB
- DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // C6 ADD_FLOAT_2ADDR vA, vB
- DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
-
- // C7 SUB_FLOAT_2ADDR vA, vB
- DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
-
- // C8 MUL_FLOAT_2ADDR vA, vB
- DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
-
- // C9 DIV_FLOAT_2ADDR vA, vB
- DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
-
- // CA REM_FLOAT_2ADDR vA, vB
- DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
-
- // CB ADD_DOUBLE_2ADDR vA, vB
- DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
-
- // CC SUB_DOUBLE_2ADDR vA, vB
- DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
-
- // CD MUL_DOUBLE_2ADDR vA, vB
- DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
-
- // CE DIV_DOUBLE_2ADDR vA, vB
- DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
-
- // CF REM_DOUBLE_2ADDR vA, vB
- DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
-
- // D0 ADD_INT_LIT16 vA, vB, #+CCCC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // D1 RSUB_INT vA, vB, #+CCCC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // D2 MUL_INT_LIT16 vA, vB, #+CCCC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // D3 DIV_INT_LIT16 vA, vB, #+CCCC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // D4 REM_INT_LIT16 vA, vB, #+CCCC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // D5 AND_INT_LIT16 vA, vB, #+CCCC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // D6 OR_INT_LIT16 vA, vB, #+CCCC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // D7 XOR_INT_LIT16 vA, vB, #+CCCC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // D8 ADD_INT_LIT8 vAA, vBB, #+CC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // D9 RSUB_INT_LIT8 vAA, vBB, #+CC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // DA MUL_INT_LIT8 vAA, vBB, #+CC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // DB DIV_INT_LIT8 vAA, vBB, #+CC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // DC REM_INT_LIT8 vAA, vBB, #+CC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // DD AND_INT_LIT8 vAA, vBB, #+CC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // DE OR_INT_LIT8 vAA, vBB, #+CC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // DF XOR_INT_LIT8 vAA, vBB, #+CC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // E0 SHL_INT_LIT8 vAA, vBB, #+CC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // E1 SHR_INT_LIT8 vAA, vBB, #+CC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // E2 USHR_INT_LIT8 vAA, vBB, #+CC
- DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
- // E3 IGET_VOLATILE
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
-
- // E4 IPUT_VOLATILE
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
-
- // E5 SGET_VOLATILE
- DF_DA | DF_UMS,
-
- // E6 SPUT_VOLATILE
- DF_UA | DF_UMS,
-
- // E7 IGET_OBJECT_VOLATILE
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B,
-
- // E8 IGET_WIDE_VOLATILE
- DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
-
- // E9 IPUT_WIDE_VOLATILE
- DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B,
-
- // EA SGET_WIDE_VOLATILE
- DF_DA | DF_A_WIDE | DF_UMS,
-
- // EB SPUT_WIDE_VOLATILE
- DF_UA | DF_A_WIDE | DF_UMS,
-
- // EC BREAKPOINT
- DF_NOP,
-
- // ED THROW_VERIFICATION_ERROR
- DF_NOP | DF_UMS,
-
- // EE EXECUTE_INLINE
- DF_FORMAT_35C,
-
- // EF EXECUTE_INLINE_RANGE
- DF_FORMAT_3RC,
-
- // F0 INVOKE_OBJECT_INIT_RANGE
- DF_NOP | DF_NULL_CHK_0,
-
- // F1 RETURN_VOID_BARRIER
- DF_NOP,
-
- // F2 IGET_QUICK
- DF_DA | DF_UB | DF_NULL_CHK_0,
-
- // F3 IGET_WIDE_QUICK
- DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0,
-
- // F4 IGET_OBJECT_QUICK
- DF_DA | DF_UB | DF_NULL_CHK_0,
-
- // F5 IPUT_QUICK
- DF_UA | DF_UB | DF_NULL_CHK_1,
-
- // F6 IPUT_WIDE_QUICK
- DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2,
-
- // F7 IPUT_OBJECT_QUICK
- DF_UA | DF_UB | DF_NULL_CHK_1,
-
- // F8 INVOKE_VIRTUAL_QUICK
- DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
-
- // F9 INVOKE_VIRTUAL_QUICK_RANGE
- DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
-
- // FA INVOKE_SUPER_QUICK
- DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
-
- // FB INVOKE_SUPER_QUICK_RANGE
- DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
-
- // FC IPUT_OBJECT_VOLATILE
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_A | DF_REF_B,
-
- // FD SGET_OBJECT_VOLATILE
- DF_DA | DF_REF_A | DF_UMS,
-
- // FE SPUT_OBJECT_VOLATILE
- DF_UA | DF_REF_A | DF_UMS,
-
- // FF UNUSED_FF
- DF_NOP
-};
-} // namespace sea_ir
diff --git a/compiler/sea_ir/ir/instruction_tools.h b/compiler/sea_ir/ir/instruction_tools.h
deleted file mode 100644
index 895e017..0000000
--- a/compiler/sea_ir/ir/instruction_tools.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "sea.h"
-#include "dex_instruction.h"
-
-#ifndef ART_COMPILER_SEA_IR_IR_INSTRUCTION_TOOLS_H_
-#define ART_COMPILER_SEA_IR_IR_INSTRUCTION_TOOLS_H_
-
-
-// Note: This file has content cannibalized for SEA_IR from the MIR implementation,
-// to avoid having a dependence on MIR.
-namespace sea_ir {
-
-#define DF_NOP 0
-#define DF_UA (1 << kUA)
-#define DF_UB (1 << kUB)
-#define DF_UC (1 << kUC)
-#define DF_A_WIDE (1 << kAWide)
-#define DF_B_WIDE (1 << kBWide)
-#define DF_C_WIDE (1 << kCWide)
-#define DF_DA (1 << kDA)
-#define DF_IS_MOVE (1 << kIsMove)
-#define DF_SETS_CONST (1 << kSetsConst)
-#define DF_FORMAT_35C (1 << kFormat35c)
-#define DF_FORMAT_3RC (1 << kFormat3rc)
-#define DF_NULL_CHK_0 (1 << kNullCheckSrc0)
-#define DF_NULL_CHK_1 (1 << kNullCheckSrc1)
-#define DF_NULL_CHK_2 (1 << kNullCheckSrc2)
-#define DF_NULL_CHK_OUT0 (1 << kNullCheckOut0)
-#define DF_NON_NULL_DST (1 << kDstNonNull)
-#define DF_NON_NULL_RET (1 << kRetNonNull)
-#define DF_NULL_TRANSFER_0 (1 << kNullTransferSrc0)
-#define DF_NULL_TRANSFER_N (1 << kNullTransferSrcN)
-#define DF_RANGE_CHK_1 (1 << kRangeCheckSrc1)
-#define DF_RANGE_CHK_2 (1 << kRangeCheckSrc2)
-#define DF_RANGE_CHK_3 (1 << kRangeCheckSrc3)
-#define DF_FP_A (1 << kFPA)
-#define DF_FP_B (1 << kFPB)
-#define DF_FP_C (1 << kFPC)
-#define DF_CORE_A (1 << kCoreA)
-#define DF_CORE_B (1 << kCoreB)
-#define DF_CORE_C (1 << kCoreC)
-#define DF_REF_A (1 << kRefA)
-#define DF_REF_B (1 << kRefB)
-#define DF_REF_C (1 << kRefC)
-#define DF_UMS (1 << kUsesMethodStar)
-
-#define DF_HAS_USES (DF_UA | DF_UB | DF_UC)
-
-#define DF_HAS_DEFS (DF_DA)
-
-#define DF_HAS_NULL_CHKS (DF_NULL_CHK_0 | \
- DF_NULL_CHK_1 | \
- DF_NULL_CHK_2 | \
- DF_NULL_CHK_OUT0)
-
-#define DF_HAS_RANGE_CHKS (DF_RANGE_CHK_1 | \
- DF_RANGE_CHK_2 | \
- DF_RANGE_CHK_3)
-
-#define DF_HAS_NR_CHKS (DF_HAS_NULL_CHKS | \
- DF_HAS_RANGE_CHKS)
-
-#define DF_A_IS_REG (DF_UA | DF_DA)
-#define DF_B_IS_REG (DF_UB)
-#define DF_C_IS_REG (DF_UC)
-#define DF_IS_GETTER_OR_SETTER (DF_IS_GETTER | DF_IS_SETTER)
-#define DF_USES_FP (DF_FP_A | DF_FP_B | DF_FP_C)
-
-enum DataFlowAttributePos {
- kUA = 0,
- kUB,
- kUC,
- kAWide,
- kBWide,
- kCWide,
- kDA,
- kIsMove,
- kSetsConst,
- kFormat35c,
- kFormat3rc,
- kNullCheckSrc0, // Null check of uses[0].
- kNullCheckSrc1, // Null check of uses[1].
- kNullCheckSrc2, // Null check of uses[2].
- kNullCheckOut0, // Null check out outgoing arg0.
- kDstNonNull, // May assume dst is non-null.
- kRetNonNull, // May assume retval is non-null.
- kNullTransferSrc0, // Object copy src[0] -> dst.
- kNullTransferSrcN, // Phi null check state transfer.
- kRangeCheckSrc1, // Range check of uses[1].
- kRangeCheckSrc2, // Range check of uses[2].
- kRangeCheckSrc3, // Range check of uses[3].
- kFPA,
- kFPB,
- kFPC,
- kCoreA,
- kCoreB,
- kCoreC,
- kRefA,
- kRefB,
- kRefC,
- kUsesMethodStar, // Implicit use of Method*.
-};
-
-class InstructionTools {
- public:
- static bool IsDefinition(const art::Instruction* instruction);
- static const int instruction_attributes_[];
-};
-} // namespace sea_ir
-#endif // ART_COMPILER_SEA_IR_IR_INSTRUCTION_TOOLS_H_
diff --git a/compiler/sea_ir/ir/regions_test.cc b/compiler/sea_ir/ir/regions_test.cc
deleted file mode 100644
index 95bd310..0000000
--- a/compiler/sea_ir/ir/regions_test.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "common_compiler_test.h"
-#include "sea_ir/ir/sea.h"
-
-using utils::ScopedHashtable;
-
-namespace sea_ir {
-
-class RegionsTest : public art::CommonCompilerTest {};
-
-TEST_F(RegionsTest, Basics) {
- sea_ir::SeaGraph sg(*java_lang_dex_file_);
- sea_ir::Region* root = sg.GetNewRegion();
- sea_ir::Region* then_region = sg.GetNewRegion();
- sea_ir::Region* else_region = sg.GetNewRegion();
- std::vector<sea_ir::Region*>* regions = sg.GetRegions();
- // Test that regions have been registered correctly as children of the graph.
- EXPECT_TRUE(std::find(regions->begin(), regions->end(), root) != regions->end());
- EXPECT_TRUE(std::find(regions->begin(), regions->end(), then_region) != regions->end());
- EXPECT_TRUE(std::find(regions->begin(), regions->end(), else_region) != regions->end());
- // Check that an edge recorded correctly in both the head and the tail.
- sg.AddEdge(root, then_region);
- std::vector<sea_ir::Region*>* succs = root->GetSuccessors();
- EXPECT_EQ(1U, succs->size());
- EXPECT_EQ(then_region, succs->at(0));
- std::vector<sea_ir::Region*>* preds = then_region->GetPredecessors();
- EXPECT_EQ(1U, preds->size());
- EXPECT_EQ(root, preds->at(0));
- // Check that two edges are recorded properly for both head and tail.
- sg.AddEdge(root, else_region);
- succs = root->GetSuccessors();
- EXPECT_EQ(2U, succs->size());
- EXPECT_TRUE(std::find(succs->begin(), succs->end(), then_region) != succs->end());
- EXPECT_TRUE(std::find(succs->begin(), succs->end(), else_region) != succs->end());
- preds = then_region->GetPredecessors();
- EXPECT_EQ(1U, preds->size());
- EXPECT_EQ(root, preds->at(0));
- preds = else_region->GetPredecessors();
- EXPECT_EQ(1U, preds->size());
- EXPECT_EQ(root, preds->at(0));
-}
-
-} // namespace sea_ir
diff --git a/compiler/sea_ir/ir/sea.cc b/compiler/sea_ir/ir/sea.cc
deleted file mode 100644
index 2b25f56..0000000
--- a/compiler/sea_ir/ir/sea.cc
+++ /dev/null
@@ -1,681 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "base/stringprintf.h"
-#include "sea_ir/ir/instruction_tools.h"
-#include "sea_ir/ir/sea.h"
-#include "sea_ir/code_gen/code_gen.h"
-#include "sea_ir/types/type_inference.h"
-
-#define MAX_REACHING_DEF_ITERERATIONS (10)
-// TODO: When development is done, this define should not
-// be needed, it is currently used as a cutoff
-// for cases where the iterative fixed point algorithm
-// does not reach a fixed point because of a bug.
-
-namespace sea_ir {
-
-int SeaNode::current_max_node_id_ = 0;
-
-void IRVisitor::Traverse(Region* region) {
- std::vector<PhiInstructionNode*>* phis = region->GetPhiNodes();
- for (std::vector<PhiInstructionNode*>::const_iterator cit = phis->begin();
- cit != phis->end(); cit++) {
- (*cit)->Accept(this);
- }
- std::vector<InstructionNode*>* instructions = region->GetInstructions();
- for (std::vector<InstructionNode*>::const_iterator cit = instructions->begin();
- cit != instructions->end(); cit++) {
- (*cit)->Accept(this);
- }
-}
-
-void IRVisitor::Traverse(SeaGraph* graph) {
- for (std::vector<Region*>::const_iterator cit = ordered_regions_.begin();
- cit != ordered_regions_.end(); cit++ ) {
- (*cit)->Accept(this);
- }
-}
-
-SeaGraph* SeaGraph::GetGraph(const art::DexFile& dex_file) {
- return new SeaGraph(dex_file);
-}
-
-void SeaGraph::AddEdge(Region* src, Region* dst) const {
- src->AddSuccessor(dst);
- dst->AddPredecessor(src);
-}
-
-void SeaGraph::ComputeRPO(Region* current_region, int& current_rpo) {
- current_region->SetRPO(VISITING);
- std::vector<sea_ir::Region*>* succs = current_region->GetSuccessors();
- for (std::vector<sea_ir::Region*>::iterator succ_it = succs->begin();
- succ_it != succs->end(); ++succ_it) {
- if (NOT_VISITED == (*succ_it)->GetRPO()) {
- SeaGraph::ComputeRPO(*succ_it, current_rpo);
- }
- }
- current_region->SetRPO(current_rpo--);
-}
-
-void SeaGraph::ComputeIDominators() {
- bool changed = true;
- while (changed) {
- changed = false;
- // Entry node has itself as IDOM.
- std::vector<Region*>::iterator crt_it;
- std::set<Region*> processedNodes;
- // Find and mark the entry node(s).
- for (crt_it = regions_.begin(); crt_it != regions_.end(); ++crt_it) {
- if ((*crt_it)->GetPredecessors()->size() == 0) {
- processedNodes.insert(*crt_it);
- (*crt_it)->SetIDominator(*crt_it);
- }
- }
- for (crt_it = regions_.begin(); crt_it != regions_.end(); ++crt_it) {
- if ((*crt_it)->GetPredecessors()->size() == 0) {
- continue;
- }
- // NewIDom = first (processed) predecessor of b.
- Region* new_dom = NULL;
- std::vector<Region*>* preds = (*crt_it)->GetPredecessors();
- DCHECK(NULL != preds);
- Region* root_pred = NULL;
- for (std::vector<Region*>::iterator pred_it = preds->begin();
- pred_it != preds->end(); ++pred_it) {
- if (processedNodes.end() != processedNodes.find((*pred_it))) {
- root_pred = *pred_it;
- new_dom = root_pred;
- break;
- }
- }
- // For all other predecessors p of b, if idom is not set,
- // then NewIdom = Intersect(p, NewIdom)
- for (std::vector<Region*>::const_iterator pred_it = preds->begin();
- pred_it != preds->end(); ++pred_it) {
- DCHECK(NULL != *pred_it);
- // if IDOMS[p] != UNDEFINED
- if ((*pred_it != root_pred) && (*pred_it)->GetIDominator() != NULL) {
- DCHECK(NULL != new_dom);
- new_dom = SeaGraph::Intersect(*pred_it, new_dom);
- }
- }
- DCHECK(NULL != *crt_it);
- if ((*crt_it)->GetIDominator() != new_dom) {
- (*crt_it)->SetIDominator(new_dom);
- changed = true;
- }
- processedNodes.insert(*crt_it);
- }
- }
-
- // For easily ordering of regions we need edges dominator->dominated.
- for (std::vector<Region*>::iterator region_it = regions_.begin();
- region_it != regions_.end(); region_it++) {
- Region* idom = (*region_it)->GetIDominator();
- if (idom != *region_it) {
- idom->AddToIDominatedSet(*region_it);
- }
- }
-}
-
-Region* SeaGraph::Intersect(Region* i, Region* j) {
- Region* finger1 = i;
- Region* finger2 = j;
- while (finger1 != finger2) {
- while (finger1->GetRPO() > finger2->GetRPO()) {
- DCHECK(NULL != finger1);
- finger1 = finger1->GetIDominator(); // should have: finger1 != NULL
- DCHECK(NULL != finger1);
- }
- while (finger1->GetRPO() < finger2->GetRPO()) {
- DCHECK(NULL != finger2);
- finger2 = finger2->GetIDominator(); // should have: finger1 != NULL
- DCHECK(NULL != finger2);
- }
- }
- return finger1; // finger1 should be equal to finger2 at this point.
-}
-
-void SeaGraph::ComputeDownExposedDefs() {
- for (std::vector<Region*>::iterator region_it = regions_.begin();
- region_it != regions_.end(); region_it++) {
- (*region_it)->ComputeDownExposedDefs();
- }
-}
-
-void SeaGraph::ComputeReachingDefs() {
- // Iterate until the reaching definitions set doesn't change anymore.
- // (See Cooper & Torczon, "Engineering a Compiler", second edition, page 487)
- bool changed = true;
- int iteration = 0;
- while (changed && (iteration < MAX_REACHING_DEF_ITERERATIONS)) {
- iteration++;
- changed = false;
- // TODO: optimize the ordering if this becomes performance bottleneck.
- for (std::vector<Region*>::iterator regions_it = regions_.begin();
- regions_it != regions_.end();
- regions_it++) {
- changed |= (*regions_it)->UpdateReachingDefs();
- }
- }
- DCHECK(!changed) << "Reaching definitions computation did not reach a fixed point.";
-}
-
-void SeaGraph::InsertSignatureNodes(const art::DexFile::CodeItem* code_item, Region* r) {
- // Insert a fake SignatureNode for the first parameter.
- // TODO: Provide a register enum value for the fake parameter.
- SignatureNode* parameter_def_node = new sea_ir::SignatureNode(0, 0);
- AddParameterNode(parameter_def_node);
- r->AddChild(parameter_def_node);
- // Insert SignatureNodes for each Dalvik register parameter.
- for (unsigned int crt_offset = 0; crt_offset < code_item->ins_size_; crt_offset++) {
- int register_no = code_item->registers_size_ - crt_offset - 1;
- int position = crt_offset + 1;
- SignatureNode* parameter_def_node = new sea_ir::SignatureNode(register_no, position);
- AddParameterNode(parameter_def_node);
- r->AddChild(parameter_def_node);
- }
-}
-
-void SeaGraph::BuildMethodSeaGraph(const art::DexFile::CodeItem* code_item,
- const art::DexFile& dex_file, uint16_t class_def_idx,
- uint32_t method_idx, uint32_t method_access_flags) {
- code_item_ = code_item;
- class_def_idx_ = class_def_idx;
- method_idx_ = method_idx;
- method_access_flags_ = method_access_flags;
- const uint16_t* code = code_item->insns_;
- const size_t size_in_code_units = code_item->insns_size_in_code_units_;
- // This maps target instruction pointers to their corresponding region objects.
- std::map<const uint16_t*, Region*> target_regions;
- size_t i = 0;
- // Pass: Find the start instruction of basic blocks
- // by locating targets and flow-though instructions of branches.
- while (i < size_in_code_units) {
- const art::Instruction* inst = art::Instruction::At(&code[i]);
- if (inst->IsBranch() || inst->IsUnconditional()) {
- int32_t offset = inst->GetTargetOffset();
- if (target_regions.end() == target_regions.find(&code[i + offset])) {
- Region* region = GetNewRegion();
- target_regions.insert(std::pair<const uint16_t*, Region*>(&code[i + offset], region));
- }
- if (inst->CanFlowThrough()
- && (target_regions.end() == target_regions.find(&code[i + inst->SizeInCodeUnits()]))) {
- Region* region = GetNewRegion();
- target_regions.insert(
- std::pair<const uint16_t*, Region*>(&code[i + inst->SizeInCodeUnits()], region));
- }
- }
- i += inst->SizeInCodeUnits();
- }
-
-
- Region* r = GetNewRegion();
-
- InsertSignatureNodes(code_item, r);
- // Pass: Assign instructions to region nodes and
- // assign branches their control flow successors.
- i = 0;
- sea_ir::InstructionNode* last_node = NULL;
- sea_ir::InstructionNode* node = NULL;
- while (i < size_in_code_units) {
- const art::Instruction* inst = art::Instruction::At(&code[i]);
- std::vector<InstructionNode*> sea_instructions_for_dalvik =
- sea_ir::InstructionNode::Create(inst);
- for (std::vector<InstructionNode*>::const_iterator cit = sea_instructions_for_dalvik.begin();
- sea_instructions_for_dalvik.end() != cit; ++cit) {
- last_node = node;
- node = *cit;
-
- if (inst->IsBranch() || inst->IsUnconditional()) {
- int32_t offset = inst->GetTargetOffset();
- std::map<const uint16_t*, Region*>::iterator it = target_regions.find(&code[i + offset]);
- DCHECK(it != target_regions.end());
- AddEdge(r, it->second); // Add edge to branch target.
- }
- std::map<const uint16_t*, Region*>::iterator it = target_regions.find(&code[i]);
- if (target_regions.end() != it) {
- // Get the already created region because this is a branch target.
- Region* nextRegion = it->second;
- if (last_node->GetInstruction()->IsBranch()
- && last_node->GetInstruction()->CanFlowThrough()) {
- AddEdge(r, it->second); // Add flow-through edge.
- }
- r = nextRegion;
- }
- r->AddChild(node);
- }
- i += inst->SizeInCodeUnits();
- }
-}
-
-void SeaGraph::ComputeRPO() {
- int rpo_id = regions_.size() - 1;
- for (std::vector<Region*>::const_iterator crt_it = regions_.begin(); crt_it != regions_.end();
- ++crt_it) {
- if ((*crt_it)->GetPredecessors()->size() == 0) {
- ComputeRPO(*crt_it, rpo_id);
- }
- }
-}
-
-// Performs the renaming phase in traditional SSA transformations.
-// See: Cooper & Torczon, "Engineering a Compiler", second edition, page 505.)
-void SeaGraph::RenameAsSSA() {
- utils::ScopedHashtable<int, InstructionNode*> scoped_table;
- scoped_table.OpenScope();
- for (std::vector<Region*>::iterator region_it = regions_.begin(); region_it != regions_.end();
- region_it++) {
- if ((*region_it)->GetIDominator() == *region_it) {
- RenameAsSSA(*region_it, &scoped_table);
- }
- }
- scoped_table.CloseScope();
-}
-
-void SeaGraph::ConvertToSSA() {
- // Pass: find global names.
- // The map @block maps registers to the blocks in which they are defined.
- std::map<int, std::set<Region*>> blocks;
- // The set @globals records registers whose use
- // is in a different block than the corresponding definition.
- std::set<int> globals;
- for (std::vector<Region*>::iterator region_it = regions_.begin(); region_it != regions_.end();
- region_it++) {
- std::set<int> var_kill;
- std::vector<InstructionNode*>* instructions = (*region_it)->GetInstructions();
- for (std::vector<InstructionNode*>::iterator inst_it = instructions->begin();
- inst_it != instructions->end(); inst_it++) {
- std::vector<int> used_regs = (*inst_it)->GetUses();
- for (std::size_t i = 0; i < used_regs.size(); i++) {
- int used_reg = used_regs[i];
- if (var_kill.find(used_reg) == var_kill.end()) {
- globals.insert(used_reg);
- }
- }
- const int reg_def = (*inst_it)->GetResultRegister();
- if (reg_def != NO_REGISTER) {
- var_kill.insert(reg_def);
- }
-
- blocks.insert(std::pair<int, std::set<Region*>>(reg_def, std::set<Region*>()));
- std::set<Region*>* reg_def_blocks = &(blocks.find(reg_def)->second);
- reg_def_blocks->insert(*region_it);
- }
- }
-
- // Pass: Actually add phi-nodes to regions.
- for (std::set<int>::const_iterator globals_it = globals.begin();
- globals_it != globals.end(); globals_it++) {
- int global = *globals_it;
- // Copy the set, because we will modify the worklist as we go.
- std::set<Region*> worklist((*(blocks.find(global))).second);
- for (std::set<Region*>::const_iterator b_it = worklist.begin();
- b_it != worklist.end(); b_it++) {
- std::set<Region*>* df = (*b_it)->GetDominanceFrontier();
- for (std::set<Region*>::const_iterator df_it = df->begin(); df_it != df->end(); df_it++) {
- if ((*df_it)->InsertPhiFor(global)) {
- // Check that the dominance frontier element is in the worklist already
- // because we only want to break if the element is actually not there yet.
- if (worklist.find(*df_it) == worklist.end()) {
- worklist.insert(*df_it);
- b_it = worklist.begin();
- break;
- }
- }
- }
- }
- }
- // Pass: Build edges to the definition corresponding to each use.
- // (This corresponds to the renaming phase in traditional SSA transformations.
- // See: Cooper & Torczon, "Engineering a Compiler", second edition, page 505.)
- RenameAsSSA();
-}
-
-void SeaGraph::RenameAsSSA(Region* crt_region,
- utils::ScopedHashtable<int, InstructionNode*>* scoped_table) {
- scoped_table->OpenScope();
- // Rename phi nodes defined in the current region.
- std::vector<PhiInstructionNode*>* phis = crt_region->GetPhiNodes();
- for (std::vector<PhiInstructionNode*>::iterator phi_it = phis->begin();
- phi_it != phis->end(); phi_it++) {
- int reg_no = (*phi_it)->GetRegisterNumber();
- scoped_table->Add(reg_no, (*phi_it));
- }
- // Rename operands of instructions from the current region.
- std::vector<InstructionNode*>* instructions = crt_region->GetInstructions();
- for (std::vector<InstructionNode*>::const_iterator instructions_it = instructions->begin();
- instructions_it != instructions->end(); instructions_it++) {
- InstructionNode* current_instruction = (*instructions_it);
- // Rename uses.
- std::vector<int> used_regs = current_instruction->GetUses();
- for (std::vector<int>::const_iterator reg_it = used_regs.begin();
- reg_it != used_regs.end(); reg_it++) {
- int current_used_reg = (*reg_it);
- InstructionNode* definition = scoped_table->Lookup(current_used_reg);
- current_instruction->RenameToSSA(current_used_reg, definition);
- }
- // Update scope table with latest definitions.
- std::vector<int> def_regs = current_instruction->GetDefinitions();
- for (std::vector<int>::const_iterator reg_it = def_regs.begin();
- reg_it != def_regs.end(); reg_it++) {
- int current_defined_reg = (*reg_it);
- scoped_table->Add(current_defined_reg, current_instruction);
- }
- }
- // Fill in uses of phi functions in CFG successor regions.
- const std::vector<Region*>* successors = crt_region->GetSuccessors();
- for (std::vector<Region*>::const_iterator successors_it = successors->begin();
- successors_it != successors->end(); successors_it++) {
- Region* successor = (*successors_it);
- successor->SetPhiDefinitionsForUses(scoped_table, crt_region);
- }
-
- // Rename all successors in the dominators tree.
- const std::set<Region*>* dominated_nodes = crt_region->GetIDominatedSet();
- for (std::set<Region*>::const_iterator dominated_nodes_it = dominated_nodes->begin();
- dominated_nodes_it != dominated_nodes->end(); dominated_nodes_it++) {
- Region* dominated_node = (*dominated_nodes_it);
- RenameAsSSA(dominated_node, scoped_table);
- }
- scoped_table->CloseScope();
-}
-
-CodeGenData* SeaGraph::GenerateLLVM(const std::string& function_name,
- const art::DexFile& dex_file) {
- // Pass: Generate LLVM IR.
- CodeGenPrepassVisitor code_gen_prepass_visitor(function_name);
- std::cout << "Generating code..." << std::endl;
- Accept(&code_gen_prepass_visitor);
- CodeGenVisitor code_gen_visitor(code_gen_prepass_visitor.GetData(), dex_file);
- Accept(&code_gen_visitor);
- CodeGenPostpassVisitor code_gen_postpass_visitor(code_gen_visitor.GetData());
- Accept(&code_gen_postpass_visitor);
- return code_gen_postpass_visitor.GetData();
-}
-
-CodeGenData* SeaGraph::CompileMethod(
- const std::string& function_name,
- const art::DexFile::CodeItem* code_item, uint16_t class_def_idx,
- uint32_t method_idx, uint32_t method_access_flags, const art::DexFile& dex_file) {
- // Two passes: Builds the intermediate structure (non-SSA) of the sea-ir for the function.
- BuildMethodSeaGraph(code_item, dex_file, class_def_idx, method_idx, method_access_flags);
- // Pass: Compute reverse post-order of regions.
- ComputeRPO();
- // Multiple passes: compute immediate dominators.
- ComputeIDominators();
- // Pass: compute downward-exposed definitions.
- ComputeDownExposedDefs();
- // Multiple Passes (iterative fixed-point algorithm): Compute reaching definitions
- ComputeReachingDefs();
- // Pass (O(nlogN)): Compute the dominance frontier for region nodes.
- ComputeDominanceFrontier();
- // Two Passes: Phi node insertion.
- ConvertToSSA();
- // Pass: type inference
- ti_->ComputeTypes(this);
- // Pass: Generate LLVM IR.
- CodeGenData* cgd = GenerateLLVM(function_name, dex_file);
- return cgd;
-}
-
-void SeaGraph::ComputeDominanceFrontier() {
- for (std::vector<Region*>::iterator region_it = regions_.begin();
- region_it != regions_.end(); region_it++) {
- std::vector<Region*>* preds = (*region_it)->GetPredecessors();
- if (preds->size() > 1) {
- for (std::vector<Region*>::iterator pred_it = preds->begin();
- pred_it != preds->end(); pred_it++) {
- Region* runner = *pred_it;
- while (runner != (*region_it)->GetIDominator()) {
- runner->AddToDominanceFrontier(*region_it);
- runner = runner->GetIDominator();
- }
- }
- }
- }
-}
-
-Region* SeaGraph::GetNewRegion() {
- Region* new_region = new Region();
- AddRegion(new_region);
- return new_region;
-}
-
-void SeaGraph::AddRegion(Region* r) {
- DCHECK(r) << "Tried to add NULL region to SEA graph.";
- regions_.push_back(r);
-}
-
-SeaGraph::SeaGraph(const art::DexFile& df)
- :ti_(new TypeInference()), class_def_idx_(0), method_idx_(0), method_access_flags_(),
- regions_(), parameters_(), dex_file_(df), code_item_(NULL) { }
-
-void Region::AddChild(sea_ir::InstructionNode* instruction) {
- DCHECK(instruction) << "Tried to add NULL instruction to region node.";
- instructions_.push_back(instruction);
- instruction->SetRegion(this);
-}
-
-SeaNode* Region::GetLastChild() const {
- if (instructions_.size() > 0) {
- return instructions_.back();
- }
- return NULL;
-}
-
-void Region::ComputeDownExposedDefs() {
- for (std::vector<InstructionNode*>::const_iterator inst_it = instructions_.begin();
- inst_it != instructions_.end(); inst_it++) {
- int reg_no = (*inst_it)->GetResultRegister();
- std::map<int, InstructionNode*>::iterator res = de_defs_.find(reg_no);
- if ((reg_no != NO_REGISTER) && (res == de_defs_.end())) {
- de_defs_.insert(std::pair<int, InstructionNode*>(reg_no, *inst_it));
- } else {
- res->second = *inst_it;
- }
- }
- for (std::map<int, sea_ir::InstructionNode*>::const_iterator cit = de_defs_.begin();
- cit != de_defs_.end(); cit++) {
- (*cit).second->MarkAsDEDef();
- }
-}
-
-const std::map<int, sea_ir::InstructionNode*>* Region::GetDownExposedDefs() const {
- return &de_defs_;
-}
-
-std::map<int, std::set<sea_ir::InstructionNode*>* >* Region::GetReachingDefs() {
- return &reaching_defs_;
-}
-
-bool Region::UpdateReachingDefs() {
- std::map<int, std::set<sea_ir::InstructionNode*>* > new_reaching;
- for (std::vector<Region*>::const_iterator pred_it = predecessors_.begin();
- pred_it != predecessors_.end(); pred_it++) {
- // The reaching_defs variable will contain reaching defs __for current predecessor only__
- std::map<int, std::set<sea_ir::InstructionNode*>* > reaching_defs;
- std::map<int, std::set<sea_ir::InstructionNode*>* >* pred_reaching =
- (*pred_it)->GetReachingDefs();
- const std::map<int, InstructionNode*>* de_defs = (*pred_it)->GetDownExposedDefs();
-
- // The definitions from the reaching set of the predecessor
- // may be shadowed by downward exposed definitions from the predecessor,
- // otherwise the defs from the reaching set are still good.
- for (std::map<int, InstructionNode*>::const_iterator de_def = de_defs->begin();
- de_def != de_defs->end(); de_def++) {
- std::set<InstructionNode*>* solo_def;
- solo_def = new std::set<InstructionNode*>();
- solo_def->insert(de_def->second);
- reaching_defs.insert(
- std::pair<int const, std::set<InstructionNode*>*>(de_def->first, solo_def));
- }
- reaching_defs.insert(pred_reaching->begin(), pred_reaching->end());
-
- // Now we combine the reaching map coming from the current predecessor (reaching_defs)
- // with the accumulated set from all predecessors so far (from new_reaching).
- std::map<int, std::set<sea_ir::InstructionNode*>*>::iterator reaching_it =
- reaching_defs.begin();
- for (; reaching_it != reaching_defs.end(); reaching_it++) {
- std::map<int, std::set<sea_ir::InstructionNode*>*>::iterator crt_entry =
- new_reaching.find(reaching_it->first);
- if (new_reaching.end() != crt_entry) {
- crt_entry->second->insert(reaching_it->second->begin(), reaching_it->second->end());
- } else {
- new_reaching.insert(
- std::pair<int, std::set<sea_ir::InstructionNode*>*>(
- reaching_it->first,
- reaching_it->second) );
- }
- }
- }
- bool changed = false;
- // Because the sets are monotonically increasing,
- // we can compare sizes instead of using set comparison.
- // TODO: Find formal proof.
- int old_size = 0;
- if (-1 == reaching_defs_size_) {
- std::map<int, std::set<sea_ir::InstructionNode*>*>::iterator reaching_it =
- reaching_defs_.begin();
- for (; reaching_it != reaching_defs_.end(); reaching_it++) {
- old_size += (*reaching_it).second->size();
- }
- } else {
- old_size = reaching_defs_size_;
- }
- int new_size = 0;
- std::map<int, std::set<sea_ir::InstructionNode*>*>::iterator reaching_it = new_reaching.begin();
- for (; reaching_it != new_reaching.end(); reaching_it++) {
- new_size += (*reaching_it).second->size();
- }
- if (old_size != new_size) {
- changed = true;
- }
- if (changed) {
- reaching_defs_ = new_reaching;
- reaching_defs_size_ = new_size;
- }
- return changed;
-}
-
-bool Region::InsertPhiFor(int reg_no) {
- if (!ContainsPhiFor(reg_no)) {
- phi_set_.insert(reg_no);
- PhiInstructionNode* new_phi = new PhiInstructionNode(reg_no);
- new_phi->SetRegion(this);
- phi_instructions_.push_back(new_phi);
- return true;
- }
- return false;
-}
-
-void Region::SetPhiDefinitionsForUses(
- const utils::ScopedHashtable<int, InstructionNode*>* scoped_table, Region* predecessor) {
- int predecessor_id = -1;
- for (unsigned int crt_pred_id = 0; crt_pred_id < predecessors_.size(); crt_pred_id++) {
- if (predecessors_.at(crt_pred_id) == predecessor) {
- predecessor_id = crt_pred_id;
- }
- }
- DCHECK_NE(-1, predecessor_id);
- for (std::vector<PhiInstructionNode*>::iterator phi_it = phi_instructions_.begin();
- phi_it != phi_instructions_.end(); phi_it++) {
- PhiInstructionNode* phi = (*phi_it);
- int reg_no = phi->GetRegisterNumber();
- InstructionNode* definition = scoped_table->Lookup(reg_no);
- phi->RenameToSSA(reg_no, definition, predecessor_id);
- }
-}
-
-std::vector<InstructionNode*> InstructionNode::Create(const art::Instruction* in) {
- std::vector<InstructionNode*> sea_instructions;
- switch (in->Opcode()) {
- case art::Instruction::CONST_4:
- sea_instructions.push_back(new ConstInstructionNode(in));
- break;
- case art::Instruction::RETURN:
- sea_instructions.push_back(new ReturnInstructionNode(in));
- break;
- case art::Instruction::IF_NE:
- sea_instructions.push_back(new IfNeInstructionNode(in));
- break;
- case art::Instruction::ADD_INT_LIT8:
- sea_instructions.push_back(new UnnamedConstInstructionNode(in, in->VRegC_22b()));
- sea_instructions.push_back(new AddIntLitInstructionNode(in));
- break;
- case art::Instruction::MOVE_RESULT:
- sea_instructions.push_back(new MoveResultInstructionNode(in));
- break;
- case art::Instruction::INVOKE_STATIC:
- sea_instructions.push_back(new InvokeStaticInstructionNode(in));
- break;
- case art::Instruction::ADD_INT:
- sea_instructions.push_back(new AddIntInstructionNode(in));
- break;
- case art::Instruction::GOTO:
- sea_instructions.push_back(new GotoInstructionNode(in));
- break;
- case art::Instruction::IF_EQZ:
- sea_instructions.push_back(new IfEqzInstructionNode(in));
- break;
- default:
- // Default, generic IR instruction node; default case should never be reached
- // when support for all instructions ahs been added.
- sea_instructions.push_back(new InstructionNode(in));
- }
- return sea_instructions;
-}
-
-void InstructionNode::MarkAsDEDef() {
- de_def_ = true;
-}
-
-int InstructionNode::GetResultRegister() const {
- if (instruction_->HasVRegA() && InstructionTools::IsDefinition(instruction_)) {
- return instruction_->VRegA();
- }
- return NO_REGISTER;
-}
-
-std::vector<int> InstructionNode::GetDefinitions() const {
- // TODO: Extend this to handle instructions defining more than one register (if any)
- // The return value should be changed to pointer to field then; for now it is an object
- // so that we avoid possible memory leaks from allocating objects dynamically.
- std::vector<int> definitions;
- int result = GetResultRegister();
- if (NO_REGISTER != result) {
- definitions.push_back(result);
- }
- return definitions;
-}
-
-std::vector<int> InstructionNode::GetUses() const {
- std::vector<int> uses; // Using vector<> instead of set<> because order matters.
- if (!InstructionTools::IsDefinition(instruction_) && (instruction_->HasVRegA())) {
- int vA = instruction_->VRegA();
- uses.push_back(vA);
- }
- if (instruction_->HasVRegB()) {
- int vB = instruction_->VRegB();
- uses.push_back(vB);
- }
- if (instruction_->HasVRegC()) {
- int vC = instruction_->VRegC();
- uses.push_back(vC);
- }
- return uses;
-}
-} // namespace sea_ir
diff --git a/compiler/sea_ir/ir/sea.h b/compiler/sea_ir/ir/sea.h
deleted file mode 100644
index 26b16be..0000000
--- a/compiler/sea_ir/ir/sea.h
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef ART_COMPILER_SEA_IR_IR_SEA_H_
-#define ART_COMPILER_SEA_IR_IR_SEA_H_
-
-#include <set>
-#include <map>
-
-#include "utils/scoped_hashtable.h"
-#include "gtest/gtest_prod.h"
-#include "dex_file.h"
-#include "dex_instruction.h"
-#include "sea_ir/ir/instruction_tools.h"
-#include "sea_ir/ir/instruction_nodes.h"
-
-namespace sea_ir {
-
-// Reverse post-order numbering constants
-enum RegionNumbering {
- NOT_VISITED = -1,
- VISITING = -2
-};
-
-class TypeInference;
-class CodeGenData;
-
-class Region;
-class InstructionNode;
-class PhiInstructionNode;
-class SignatureNode;
-
-// A SignatureNode is a declaration of one parameter in the function signature.
-// This class is used to provide place-holder definitions to which instructions
-// can return from the GetSSAUses() calls, instead of having missing SSA edges.
-class SignatureNode: public InstructionNode {
- public:
- // Creates a new signature node representing the initial definition of the
- // register @register_no which is the @signature_position-th argument to the method.
- explicit SignatureNode(unsigned int register_no, unsigned int signature_position):
- InstructionNode(NULL), register_no_(register_no), position_(signature_position) { }
-
- int GetResultRegister() const {
- return register_no_;
- }
-
- unsigned int GetPositionInSignature() const {
- return position_;
- }
-
- std::vector<int> GetUses() const {
- return std::vector<int>();
- }
-
- void Accept(IRVisitor* v) {
- v->Visit(this);
- v->Traverse(this);
- }
-
- private:
- const unsigned int register_no_;
- const unsigned int position_; // The position of this parameter node is
- // in the function parameter list.
-};
-
-class PhiInstructionNode: public InstructionNode {
- public:
- explicit PhiInstructionNode(int register_no):
- InstructionNode(NULL), register_no_(register_no), definition_edges_() {}
- // Returns the register on which this phi-function is used.
- int GetRegisterNumber() const {
- return register_no_;
- }
-
- // Renames the use of @reg_no to refer to the instruction @definition.
- // Phi-functions are different than normal instructions in that they
- // have multiple predecessor regions; this is why RenameToSSA has
- // the additional parameter specifying that @parameter_id is the incoming
- // edge for @definition, essentially creating SSA form.
- void RenameToSSA(int reg_no, InstructionNode* definition, unsigned int predecessor_id) {
- DCHECK(NULL != definition) << "Tried to rename to SSA using a NULL definition for "
- << StringId() << " register " << reg_no;
- if (definition_edges_.size() < predecessor_id+1) {
- definition_edges_.resize(predecessor_id+1, NULL);
- }
- if (NULL == definition_edges_.at(predecessor_id)) {
- definition_edges_[predecessor_id] = new std::vector<InstructionNode*>();
- }
- definition_edges_[predecessor_id]->push_back(definition);
- definition->AddSSAUse(this);
- }
-
- // Returns the ordered set of Instructions that define the input operands of this instruction.
- // Precondition: SeaGraph.ConvertToSSA().
- std::vector<InstructionNode*> GetSSAProducers() {
- std::vector<InstructionNode*> producers;
- for (std::vector<std::vector<InstructionNode*>*>::const_iterator
- cit = definition_edges_.begin(); cit != definition_edges_.end(); cit++) {
- producers.insert(producers.end(), (*cit)->begin(), (*cit)->end());
- }
- return producers;
- }
-
- // Returns the instruction that defines the phi register from predecessor
- // on position @predecessor_pos. Note that the return value is vector<> just
- // for consistency with the return value of GetSSAUses() on regular instructions,
- // The returned vector should always have a single element because the IR is SSA.
- std::vector<InstructionNode*>* GetSSAUses(int predecessor_pos) {
- return definition_edges_.at(predecessor_pos);
- }
-
- void Accept(IRVisitor* v) {
- v->Visit(this);
- v->Traverse(this);
- }
-
- private:
- int register_no_;
- // This vector has one entry for each predecessors, each with a single
- // element, storing the id of the instruction that defines the register
- // corresponding to this phi function.
- std::vector<std::vector<InstructionNode*>*> definition_edges_;
-};
-
-// This class corresponds to a basic block in traditional compiler IRs.
-// The dataflow analysis relies on this class both during execution and
-// for storing its results.
-class Region : public SeaNode {
- public:
- explicit Region():
- SeaNode(), successors_(), predecessors_(), reaching_defs_size_(0),
- rpo_number_(NOT_VISITED), idom_(NULL), idominated_set_(), df_(), phi_set_() {
- string_id_ = "cluster_" + string_id_;
- }
- // Adds @instruction as an instruction node child in the current region.
- void AddChild(sea_ir::InstructionNode* instruction);
- // Returns the last instruction node child of the current region.
- // This child has the CFG successors pointing to the new regions.
- SeaNode* GetLastChild() const;
- // Returns all the child instructions of this region, in program order.
- std::vector<InstructionNode*>* GetInstructions() {
- return &instructions_;
- }
-
- // Computes Downward Exposed Definitions for the current node.
- void ComputeDownExposedDefs();
- const std::map<int, sea_ir::InstructionNode*>* GetDownExposedDefs() const;
- // Performs one iteration of the reaching definitions algorithm
- // and returns true if the reaching definitions set changed.
- bool UpdateReachingDefs();
- // Returns the set of reaching definitions for the current region.
- std::map<int, std::set<sea_ir::InstructionNode*>* >* GetReachingDefs();
-
- void SetRPO(int rpo) {
- rpo_number_ = rpo;
- }
-
- int GetRPO() {
- return rpo_number_;
- }
-
- void SetIDominator(Region* dom) {
- idom_ = dom;
- }
-
- Region* GetIDominator() const {
- return idom_;
- }
-
- void AddToIDominatedSet(Region* dominated) {
- idominated_set_.insert(dominated);
- }
-
- const std::set<Region*>* GetIDominatedSet() {
- return &idominated_set_;
- }
- // Adds @df_reg to the dominance frontier of the current region.
- void AddToDominanceFrontier(Region* df_reg) {
- df_.insert(df_reg);
- }
- // Returns the dominance frontier of the current region.
- // Preconditions: SeaGraph.ComputeDominanceFrontier()
- std::set<Region*>* GetDominanceFrontier() {
- return &df_;
- }
- // Returns true if the region contains a phi function for @reg_no.
- bool ContainsPhiFor(int reg_no) {
- return (phi_set_.end() != phi_set_.find(reg_no));
- }
- // Returns the phi-functions from the region.
- std::vector<PhiInstructionNode*>* GetPhiNodes() {
- return &phi_instructions_;
- }
- // Adds a phi-function for @reg_no to this region.
- // Note: The insertion order does not matter, as phi-functions
- // are conceptually executed at the same time.
- bool InsertPhiFor(int reg_no);
- // Sets the phi-function uses to be as defined in @scoped_table for predecessor @@predecessor.
- void SetPhiDefinitionsForUses(const utils::ScopedHashtable<int, InstructionNode*>* scoped_table,
- Region* predecessor);
-
- void Accept(IRVisitor* v) {
- v->Visit(this);
- v->Traverse(this);
- }
-
- void AddSuccessor(Region* successor) {
- DCHECK(successor) << "Tried to add NULL successor to SEA node.";
- successors_.push_back(successor);
- return;
- }
- void AddPredecessor(Region* predecessor) {
- DCHECK(predecessor) << "Tried to add NULL predecessor to SEA node.";
- predecessors_.push_back(predecessor);
- }
-
- std::vector<sea_ir::Region*>* GetSuccessors() {
- return &successors_;
- }
- std::vector<sea_ir::Region*>* GetPredecessors() {
- return &predecessors_;
- }
-
- private:
- std::vector<sea_ir::Region*> successors_; // CFG successor nodes (regions)
- std::vector<sea_ir::Region*> predecessors_; // CFG predecessor nodes (instructions/regions)
- std::vector<sea_ir::InstructionNode*> instructions_;
- std::map<int, sea_ir::InstructionNode*> de_defs_;
- std::map<int, std::set<sea_ir::InstructionNode*>* > reaching_defs_;
- int reaching_defs_size_;
- int rpo_number_; // reverse postorder number of the region
- // Immediate dominator node.
- Region* idom_;
- // The set of nodes immediately dominated by the region.
- std::set<Region*> idominated_set_;
- // Records the dominance frontier.
- std::set<Region*> df_;
- // Records the set of register numbers that have phi nodes in this region.
- std::set<int> phi_set_;
- std::vector<PhiInstructionNode*> phi_instructions_;
-};
-
-// A SeaGraph instance corresponds to a source code function.
-// Its main point is to encapsulate the SEA IR representation of it
-// and acts as starting point for visitors (ex: during code generation).
-class SeaGraph: IVisitable {
- public:
- static SeaGraph* GetGraph(const art::DexFile&);
-
- CodeGenData* CompileMethod(const std::string& function_name,
- const art::DexFile::CodeItem* code_item, uint16_t class_def_idx,
- uint32_t method_idx, uint32_t method_access_flags, const art::DexFile& dex_file);
- // Returns all regions corresponding to this SeaGraph.
- std::vector<Region*>* GetRegions() {
- return ®ions_;
- }
- // Recursively computes the reverse postorder value for @crt_bb and successors.
- static void ComputeRPO(Region* crt_bb, int& crt_rpo);
- // Returns the "lowest common ancestor" of @i and @j in the dominator tree.
- static Region* Intersect(Region* i, Region* j);
- // Returns the vector of parameters of the function.
- std::vector<SignatureNode*>* GetParameterNodes() {
- return ¶meters_;
- }
-
- const art::DexFile* GetDexFile() const {
- return &dex_file_;
- }
-
- virtual void Accept(IRVisitor* visitor) {
- visitor->Initialize(this);
- visitor->Visit(this);
- visitor->Traverse(this);
- }
-
- TypeInference* ti_;
- uint16_t class_def_idx_;
- uint32_t method_idx_;
- uint32_t method_access_flags_;
-
- protected:
- explicit SeaGraph(const art::DexFile& df);
- virtual ~SeaGraph() { }
-
- private:
- FRIEND_TEST(RegionsTest, Basics);
- // Registers @childReg as a region belonging to the SeaGraph instance.
- void AddRegion(Region* childReg);
- // Returns new region and registers it with the SeaGraph instance.
- Region* GetNewRegion();
- // Adds a (formal) parameter node to the vector of parameters of the function.
- void AddParameterNode(SignatureNode* parameterNode) {
- parameters_.push_back(parameterNode);
- }
- // Adds a CFG edge from @src node to @dst node.
- void AddEdge(Region* src, Region* dst) const;
- // Builds the non-SSA sea-ir representation of the function @code_item from @dex_file
- // with class id @class_def_idx and method id @method_idx.
- void BuildMethodSeaGraph(const art::DexFile::CodeItem* code_item,
- const art::DexFile& dex_file, uint16_t class_def_idx,
- uint32_t method_idx, uint32_t method_access_flags);
- // Computes immediate dominators for each region.
- // Precondition: ComputeMethodSeaGraph()
- void ComputeIDominators();
- // Computes Downward Exposed Definitions for all regions in the graph.
- void ComputeDownExposedDefs();
- // Computes the reaching definitions set following the equations from
- // Cooper & Torczon, "Engineering a Compiler", second edition, page 491.
- // Precondition: ComputeDEDefs()
- void ComputeReachingDefs();
- // Computes the reverse-postorder numbering for the region nodes.
- // Precondition: ComputeDEDefs()
- void ComputeRPO();
- // Computes the dominance frontier for all regions in the graph,
- // following the algorithm from
- // Cooper & Torczon, "Engineering a Compiler", second edition, page 499.
- // Precondition: ComputeIDominators()
- void ComputeDominanceFrontier();
- // Converts the IR to semi-pruned SSA form.
- void ConvertToSSA();
- // Performs the renaming phase of the SSA transformation during ConvertToSSA() execution.
- void RenameAsSSA();
- // Identifies the definitions corresponding to uses for region @node
- // by using the scoped hashtable of names @ scoped_table.
- void RenameAsSSA(Region* node, utils::ScopedHashtable<int, InstructionNode*>* scoped_table);
- // Generate LLVM IR for the method.
- // Precondition: ConvertToSSA().
- CodeGenData* GenerateLLVM(const std::string& function_name, const art::DexFile& dex_file);
- // Inserts one SignatureNode for each argument of the function in
- void InsertSignatureNodes(const art::DexFile::CodeItem* code_item, Region* r);
-
- static SeaGraph graph_;
- std::vector<Region*> regions_;
- std::vector<SignatureNode*> parameters_;
- const art::DexFile& dex_file_;
- const art::DexFile::CodeItem* code_item_;
-};
-} // namespace sea_ir
-#endif // ART_COMPILER_SEA_IR_IR_SEA_H_
diff --git a/compiler/sea_ir/ir/sea_node.h b/compiler/sea_ir/ir/sea_node.h
deleted file mode 100644
index 4dab5cb..0000000
--- a/compiler/sea_ir/ir/sea_node.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_SEA_IR_IR_SEA_NODE_H_
-#define ART_COMPILER_SEA_IR_IR_SEA_NODE_H_
-
-#include "base/stringprintf.h"
-
-namespace sea_ir {
-class Region;
-class IRVisitor;
-
-class IVisitable {
- public:
- virtual void Accept(IRVisitor* visitor) = 0;
- virtual ~IVisitable() {}
-};
-
-// This abstract class provides the essential services that
-// we want each SEA IR element to have.
-// At the moment, these are:
-// - an id and corresponding string representation.
-// - a .dot graph language representation for .dot output.
-//
-// Note that SEA IR nodes could also be Regions, Projects
-// which are not instructions.
-class SeaNode: public IVisitable {
- public:
- explicit SeaNode():id_(GetNewId()), string_id_() {
- string_id_ = art::StringPrintf("%d", id_);
- }
-
- // Adds CFG predecessors and successors to each block.
- void AddSuccessor(Region* successor);
- void AddPredecessor(Region* predecesor);
-
- // Returns the id of the current block as string
- const std::string& StringId() const {
- return string_id_;
- }
- // Returns the id of this node as int. The id is supposed to be unique among
- // all instances of all subclasses of this class.
- int Id() const {
- return id_;
- }
-
- virtual ~SeaNode() { }
-
- protected:
- static int GetNewId() {
- return current_max_node_id_++;
- }
-
- const int id_;
- std::string string_id_;
-
- private:
- static int current_max_node_id_;
- // Creating new instances of sea node objects should not be done through copy or assignment
- // operators because that would lead to duplication of their unique ids.
- DISALLOW_COPY_AND_ASSIGN(SeaNode);
-};
-} // namespace sea_ir
-#endif // ART_COMPILER_SEA_IR_IR_SEA_NODE_H_
diff --git a/compiler/sea_ir/ir/visitor.h b/compiler/sea_ir/ir/visitor.h
deleted file mode 100644
index cc7b5d1..0000000
--- a/compiler/sea_ir/ir/visitor.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_SEA_IR_IR_VISITOR_H_
-#define ART_COMPILER_SEA_IR_IR_VISITOR_H_
-
-namespace sea_ir {
-
-class SeaGraph;
-class Region;
-class InstructionNode;
-class PhiInstructionNode;
-class SignatureNode;
-class UnnamedConstInstructionNode;
-class ConstInstructionNode;
-class ReturnInstructionNode;
-class IfNeInstructionNode;
-class AddIntLit8InstructionNode;
-class MoveResultInstructionNode;
-class InvokeStaticInstructionNode;
-class AddIntInstructionNode;
-class AddIntLitInstructionNode;
-class GotoInstructionNode;
-class IfEqzInstructionNode;
-
-
-
-
-class IRVisitor {
- public:
- explicit IRVisitor(): ordered_regions_() { }
- virtual void Initialize(SeaGraph* graph) = 0;
- virtual void Visit(SeaGraph* graph) = 0;
- virtual void Visit(Region* region) = 0;
- virtual void Visit(PhiInstructionNode* region) = 0;
- virtual void Visit(SignatureNode* region) = 0;
-
- virtual void Visit(InstructionNode* region) = 0;
- virtual void Visit(ConstInstructionNode* instruction) = 0;
- virtual void Visit(UnnamedConstInstructionNode* instruction) = 0;
- virtual void Visit(ReturnInstructionNode* instruction) = 0;
- virtual void Visit(IfNeInstructionNode* instruction) = 0;
- virtual void Visit(MoveResultInstructionNode* instruction) = 0;
- virtual void Visit(InvokeStaticInstructionNode* instruction) = 0;
- virtual void Visit(AddIntInstructionNode* instruction) = 0;
- virtual void Visit(GotoInstructionNode* instruction) = 0;
- virtual void Visit(IfEqzInstructionNode* instruction) = 0;
-
- // Note: This flavor of visitor separates the traversal functions from the actual visiting part
- // so that the Visitor subclasses don't duplicate code and can't get the traversal wrong.
- // The disadvantage is the increased number of functions (and calls).
- virtual void Traverse(SeaGraph* graph);
- virtual void Traverse(Region* region);
- // The following functions are meant to be empty and not pure virtual,
- // because the parameter classes have no children to traverse.
- virtual void Traverse(InstructionNode* region) { }
- virtual void Traverse(ConstInstructionNode* instruction) { }
- virtual void Traverse(ReturnInstructionNode* instruction) { }
- virtual void Traverse(IfNeInstructionNode* instruction) { }
- virtual void Traverse(AddIntLit8InstructionNode* instruction) { }
- virtual void Traverse(MoveResultInstructionNode* instruction) { }
- virtual void Traverse(InvokeStaticInstructionNode* instruction) { }
- virtual void Traverse(AddIntInstructionNode* instruction) { }
- virtual void Traverse(GotoInstructionNode* instruction) { }
- virtual void Traverse(IfEqzInstructionNode* instruction) { }
- virtual void Traverse(PhiInstructionNode* phi) { }
- virtual void Traverse(SignatureNode* sig) { }
- virtual ~IRVisitor() { }
-
- protected:
- std::vector<Region*> ordered_regions_;
-};
-} // namespace sea_ir
-#endif // ART_COMPILER_SEA_IR_IR_VISITOR_H_
diff --git a/compiler/sea_ir/types/type_data_test.cc b/compiler/sea_ir/types/type_data_test.cc
deleted file mode 100644
index 42c6973..0000000
--- a/compiler/sea_ir/types/type_data_test.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "common_compiler_test.h"
-#include "sea_ir/types/types.h"
-
-namespace sea_ir {
-
-class TypeDataTest : public art::CommonCompilerTest {};
-
-TEST_F(TypeDataTest, Basics) {
- TypeData td;
- art::verifier::RegTypeCache type_cache(false);
- int first_instruction_id = 1;
- int second_instruction_id = 3;
- EXPECT_TRUE(NULL == td.FindTypeOf(first_instruction_id));
- const Type* int_type = &type_cache.Integer();
- const Type* byte_type = &type_cache.Byte();
- td.SetTypeOf(first_instruction_id, int_type);
- EXPECT_TRUE(int_type == td.FindTypeOf(first_instruction_id));
- EXPECT_TRUE(NULL == td.FindTypeOf(second_instruction_id));
- td.SetTypeOf(second_instruction_id, byte_type);
- EXPECT_TRUE(int_type == td.FindTypeOf(first_instruction_id));
- EXPECT_TRUE(byte_type == td.FindTypeOf(second_instruction_id));
-}
-
-} // namespace sea_ir
diff --git a/compiler/sea_ir/types/type_inference.cc b/compiler/sea_ir/types/type_inference.cc
deleted file mode 100644
index 1731987..0000000
--- a/compiler/sea_ir/types/type_inference.cc
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "scoped_thread_state_change.h"
-#include "sea_ir/types/type_inference.h"
-#include "sea_ir/types/type_inference_visitor.h"
-#include "sea_ir/ir/sea.h"
-
-namespace sea_ir {
-
-bool TypeInference::IsPrimitiveDescriptor(char descriptor) {
- switch (descriptor) {
- case 'I':
- case 'C':
- case 'S':
- case 'B':
- case 'Z':
- case 'F':
- case 'D':
- case 'J':
- return true;
- default:
- return false;
- }
-}
-
-FunctionTypeInfo::FunctionTypeInfo(const SeaGraph* graph, art::verifier::RegTypeCache* types)
- : dex_file_(graph->GetDexFile()), dex_method_idx_(graph->method_idx_), type_cache_(types),
- method_access_flags_(graph->method_access_flags_) {
- const art::DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
- const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_));
- declaring_class_ = &(type_cache_->FromDescriptor(NULL, descriptor, false));
-}
-
-FunctionTypeInfo::FunctionTypeInfo(const SeaGraph* graph, InstructionNode* inst,
- art::verifier::RegTypeCache* types): dex_file_(graph->GetDexFile()),
- dex_method_idx_(inst->GetInstruction()->VRegB_35c()), type_cache_(types),
- method_access_flags_(0) {
- // TODO: Test that GetDeclaredArgumentTypes() works correctly when using this constructor.
- const art::DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
- const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_));
- declaring_class_ = &(type_cache_->FromDescriptor(NULL, descriptor, false));
-}
-
-const Type* FunctionTypeInfo::GetReturnValueType() {
- const art::DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
- uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
- const char* descriptor = dex_file_->StringByTypeIdx(return_type_idx);
- art::ScopedObjectAccess soa(art::Thread::Current());
- const Type& return_type = type_cache_->FromDescriptor(NULL, descriptor, false);
- return &return_type;
-}
-
-
-
-std::vector<const Type*> FunctionTypeInfo::GetDeclaredArgumentTypes() {
- art::ScopedObjectAccess soa(art::Thread::Current());
- std::vector<const Type*> argument_types;
- // TODO: The additional (fake) Method parameter is added on the first position,
- // but is represented as integer because we don't support pointers yet.
- argument_types.push_back(&(type_cache_->Integer()));
- // Include the "this" pointer.
- size_t cur_arg = 0;
- if (!IsStatic()) {
- // If this is a constructor for a class other than java.lang.Object, mark the first ("this")
- // argument as uninitialized. This restricts field access until the superclass constructor is
- // called.
- const art::verifier::RegType& declaring_class = GetDeclaringClass();
- if (IsConstructor() && !declaring_class.IsJavaLangObject()) {
- argument_types.push_back(&(type_cache_->UninitializedThisArgument(declaring_class)));
- } else {
- argument_types.push_back(&declaring_class);
- }
- cur_arg++;
- }
- // Include the types of the parameters in the Java method signature.
- const art::DexFile::ProtoId& proto_id =
- dex_file_->GetMethodPrototype(dex_file_->GetMethodId(dex_method_idx_));
- art::DexFileParameterIterator iterator(*dex_file_, proto_id);
-
- for (; iterator.HasNext(); iterator.Next()) {
- const char* descriptor = iterator.GetDescriptor();
- if (descriptor == NULL) {
- LOG(FATAL) << "Error: Encountered null type descriptor for function argument.";
- }
- switch (descriptor[0]) {
- case 'L':
- case '[':
- // We assume that reference arguments are initialized. The only way it could be otherwise
- // (assuming the caller was verified) is if the current method is <init>, but in that case
- // it's effectively considered initialized the instant we reach here (in the sense that we
- // can return without doing anything or call virtual methods).
- {
- const Type& reg_type = type_cache_->FromDescriptor(NULL, descriptor, false);
- argument_types.push_back(®_type);
- }
- break;
- case 'Z':
- argument_types.push_back(&type_cache_->Boolean());
- break;
- case 'C':
- argument_types.push_back(&type_cache_->Char());
- break;
- case 'B':
- argument_types.push_back(&type_cache_->Byte());
- break;
- case 'I':
- argument_types.push_back(&type_cache_->Integer());
- break;
- case 'S':
- argument_types.push_back(&type_cache_->Short());
- break;
- case 'F':
- argument_types.push_back(&type_cache_->Float());
- break;
- case 'J':
- case 'D': {
- // TODO: Figure out strategy for two-register operands (double, long)
- LOG(FATAL) << "Error: Type inference for 64-bit variables has not been implemented.";
- break;
- }
- default:
- LOG(FATAL) << "Error: Unexpected signature encountered during type inference.";
- }
- cur_arg++;
- }
- return argument_types;
-}
-
-// TODO: Lock is only used for dumping types (during development). Remove this for performance.
-void TypeInference::ComputeTypes(SeaGraph* graph) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- std::vector<Region*>* regions = graph->GetRegions();
- std::list<InstructionNode*> worklist;
- // Fill the work-list with all instructions.
- for (std::vector<Region*>::const_iterator region_it = regions->begin();
- region_it != regions->end(); region_it++) {
- std::vector<PhiInstructionNode*>* phi_instructions = (*region_it)->GetPhiNodes();
- std::copy(phi_instructions->begin(), phi_instructions->end(), std::back_inserter(worklist));
- std::vector<InstructionNode*>* instructions = (*region_it)->GetInstructions();
- std::copy(instructions->begin(), instructions->end(), std::back_inserter(worklist));
- }
- TypeInferenceVisitor tiv(graph, &type_data_, type_cache_);
- // Record return type of the function.
- graph->Accept(&tiv);
- const Type* new_type = tiv.GetType();
- type_data_.SetTypeOf(-1, new_type); // TODO: Record this info in a way that
- // does not need magic constants.
- // Make SeaGraph a SeaNode?
-
- // Sparse (SSA) fixed-point algorithm that processes each instruction in the work-list,
- // adding consumers of instructions whose result changed type back into the work-list.
- // Note: According to [1] list iterators should not be invalidated on insertion,
- // which simplifies the implementation; not 100% sure other STL implementations
- // maintain this invariant, but they should.
- // [1] http://www.sgi.com/tech/stl/List.html
- // TODO: Making this conditional (as in sparse conditional constant propagation) would be good.
- // TODO: Remove elements as I go.
- for (std::list<InstructionNode*>::const_iterator instruction_it = worklist.begin();
- instruction_it != worklist.end(); instruction_it++) {
- (*instruction_it)->Accept(&tiv);
- const Type* old_type = type_data_.FindTypeOf((*instruction_it)->Id());
- const Type* new_type = tiv.GetType();
- bool type_changed = (old_type != new_type);
- if (type_changed) {
- type_data_.SetTypeOf((*instruction_it)->Id(), new_type);
- // Add SSA consumers of the current instruction to the work-list.
- std::vector<InstructionNode*>* consumers = (*instruction_it)->GetSSAConsumers();
- for (std::vector<InstructionNode*>::iterator consumer = consumers->begin();
- consumer != consumers->end(); consumer++) {
- worklist.push_back(*consumer);
- }
- }
- }
-}
-} // namespace sea_ir
diff --git a/compiler/sea_ir/types/type_inference.h b/compiler/sea_ir/types/type_inference.h
deleted file mode 100644
index 7a178b2..0000000
--- a/compiler/sea_ir/types/type_inference.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_SEA_IR_TYPES_TYPE_INFERENCE_H_
-#define ART_COMPILER_SEA_IR_TYPES_TYPE_INFERENCE_H_
-
-#include "safe_map.h"
-#include "dex_file-inl.h"
-#include "sea_ir/types/types.h"
-
-namespace sea_ir {
-
-class SeaGraph;
-class InstructionNode;
-
-// The type inference in SEA IR is different from the verifier in that it is concerned
-// with a rich type hierarchy (TODO) usable in optimization and does not perform
-// precise verification (which is the job of the verifier).
-class TypeInference {
- public:
- TypeInference() : type_cache_(new art::verifier::RegTypeCache(false)) {
- }
-
- // Computes the types for the method with SEA IR representation provided by @graph.
- void ComputeTypes(SeaGraph* graph);
-
- art::SafeMap<int, const Type*>* GetTypeMap() {
- return type_data_.GetTypeMap();
- }
- // Returns true if @descriptor corresponds to a primitive type.
- static bool IsPrimitiveDescriptor(char descriptor);
- TypeData type_data_; // TODO: Make private, add accessor and not publish a SafeMap above.
- art::verifier::RegTypeCache* const type_cache_; // TODO: Make private.
-};
-
-// Stores information about the exact type of a function.
-class FunctionTypeInfo {
- public:
- // Finds method information about the method encoded by a SEA IR graph.
- // @graph provides the input method SEA IR representation.
- // @types provides the input cache of types from which the
- // parameter types of the function are found.
- FunctionTypeInfo(const SeaGraph* graph, art::verifier::RegTypeCache* types);
- // Finds method information about the method encoded by
- // an invocation instruction in a SEA IR graph.
- // @graph provides the input method SEA IR representation.
- // @inst is an invocation instruction for the desired method.
- // @types provides the input cache of types from which the
- // parameter types of the function are found.
- FunctionTypeInfo(const SeaGraph* graph, InstructionNode* inst,
- art::verifier::RegTypeCache* types);
- // Returns the ordered vector of types corresponding to the function arguments.
- std::vector<const Type*> GetDeclaredArgumentTypes();
- // Returns the declared return value type.
- const Type* GetReturnValueType();
- // Returns the type corresponding to the class that declared the method.
- const Type& GetDeclaringClass() {
- return *declaring_class_;
- }
-
- bool IsConstructor() const {
- return (method_access_flags_ & kAccConstructor) != 0;
- }
-
- bool IsStatic() const {
- return (method_access_flags_ & kAccStatic) != 0;
- }
-
- protected:
- const Type* declaring_class_;
- const art::DexFile* dex_file_;
- const uint32_t dex_method_idx_;
- art::verifier::RegTypeCache* type_cache_;
- const uint32_t method_access_flags_; // Method's access flags.
-};
-} // namespace sea_ir
-
-#endif // ART_COMPILER_SEA_IR_TYPES_TYPE_INFERENCE_H_
diff --git a/compiler/sea_ir/types/type_inference_visitor.cc b/compiler/sea_ir/types/type_inference_visitor.cc
deleted file mode 100644
index 27bb5d8..0000000
--- a/compiler/sea_ir/types/type_inference_visitor.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "scoped_thread_state_change.h"
-#include "sea_ir/types/type_inference_visitor.h"
-#include "sea_ir/types/type_inference.h"
-#include "sea_ir/ir/sea.h"
-
-namespace sea_ir {
-
-void TypeInferenceVisitor::Visit(SeaGraph* graph) {
- FunctionTypeInfo fti(graph_, type_cache_);
- const Type* return_type = fti.GetReturnValueType();
- crt_type_.push_back(return_type);
-}
-
-void TypeInferenceVisitor::Visit(SignatureNode* parameter) {
- FunctionTypeInfo fti(graph_, type_cache_);
- std::vector<const Type*> arguments = fti.GetDeclaredArgumentTypes();
- DCHECK_LT(parameter->GetPositionInSignature(), arguments.size())
- << "Signature node position not present in signature.";
- crt_type_.push_back(arguments.at(parameter->GetPositionInSignature()));
-}
-
-void TypeInferenceVisitor::Visit(UnnamedConstInstructionNode* instruction) {
- crt_type_.push_back(&type_cache_->Integer());
-}
-
-void TypeInferenceVisitor::Visit(PhiInstructionNode* instruction) {
- std::vector<const Type*> types_to_merge = GetOperandTypes(instruction);
- const Type* result_type = MergeTypes(types_to_merge);
- crt_type_.push_back(result_type);
-}
-
-void TypeInferenceVisitor::Visit(AddIntInstructionNode* instruction) {
- std::vector<const Type*> operand_types = GetOperandTypes(instruction);
- for (std::vector<const Type*>::const_iterator cit = operand_types.begin();
- cit != operand_types.end(); cit++) {
- if (*cit != NULL) {
- DCHECK((*cit)->IsInteger());
- }
- }
- crt_type_.push_back(&type_cache_->Integer());
-}
-
-void TypeInferenceVisitor::Visit(MoveResultInstructionNode* instruction) {
- std::vector<const Type*> operand_types = GetOperandTypes(instruction);
- const Type* operand_type = operand_types.at(0);
- crt_type_.push_back(operand_type);
-}
-
-void TypeInferenceVisitor::Visit(InvokeStaticInstructionNode* instruction) {
- FunctionTypeInfo fti(graph_, instruction, type_cache_);
- const Type* result_type = fti.GetReturnValueType();
- crt_type_.push_back(result_type);
-}
-
-std::vector<const Type*> TypeInferenceVisitor::GetOperandTypes(
- InstructionNode* instruction) const {
- std::vector<InstructionNode*> sources = instruction->GetSSAProducers();
- std::vector<const Type*> types_to_merge;
- for (std::vector<InstructionNode*>::const_iterator cit = sources.begin(); cit != sources.end();
- cit++) {
- const Type* source_type = type_data_->FindTypeOf((*cit)->Id());
- if (source_type != NULL) {
- types_to_merge.push_back(source_type);
- }
- }
- return types_to_merge;
-}
-
-const Type* TypeInferenceVisitor::MergeTypes(std::vector<const Type*>& types) const {
- const Type* type = NULL;
- if (types.size() > 0) {
- type = *(types.begin());
- if (types.size() > 1) {
- for (std::vector<const Type*>::const_iterator cit = types.begin();
- cit != types.end(); cit++) {
- if (!type->Equals(**cit)) {
- type = MergeTypes(type, *cit);
- }
- }
- }
- }
- return type;
-}
-
-const Type* TypeInferenceVisitor::MergeTypes(const Type* t1, const Type* t2) const {
- DCHECK(t2 != NULL);
- DCHECK(t1 != NULL);
- art::ScopedObjectAccess soa(art::Thread::Current());
- const Type* result = &(t1->Merge(*t2, type_cache_));
- return result;
-}
-
-} // namespace sea_ir
diff --git a/compiler/sea_ir/types/type_inference_visitor.h b/compiler/sea_ir/types/type_inference_visitor.h
deleted file mode 100644
index d715151..0000000
--- a/compiler/sea_ir/types/type_inference_visitor.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_SEA_IR_TYPES_TYPE_INFERENCE_VISITOR_H_
-#define ART_COMPILER_SEA_IR_TYPES_TYPE_INFERENCE_VISITOR_H_
-
-
-#include "dex_file-inl.h"
-#include "sea_ir/ir/visitor.h"
-#include "sea_ir/types/types.h"
-
-namespace sea_ir {
-
-// The TypeInferenceVisitor visits each instruction and computes its type taking into account
-// the current type of the operands. The type is stored in the visitor.
-// We may be better off by using a separate visitor type hierarchy that has return values
-// or that passes data as parameters, than to use fields to store information that should
-// in fact be returned after visiting each element. Ideally, I would prefer to use templates
-// to specify the returned value type, but I am not aware of a possible implementation
-// that does not horribly duplicate the visitor infrastructure code (version 1: no return value,
-// version 2: with template return value).
-class TypeInferenceVisitor: public IRVisitor {
- public:
- TypeInferenceVisitor(SeaGraph* graph, TypeData* type_data,
- art::verifier::RegTypeCache* types):
- graph_(graph), type_data_(type_data), type_cache_(types), crt_type_() {
- }
- // There are no type related actions to be performed on these classes.
- void Initialize(SeaGraph* graph) { }
- void Visit(SeaGraph* graph);
- void Visit(Region* region) { }
-
- void Visit(PhiInstructionNode* instruction);
- void Visit(SignatureNode* parameter);
- void Visit(InstructionNode* instruction) { }
- void Visit(UnnamedConstInstructionNode* instruction);
- void Visit(ConstInstructionNode* instruction) { }
- void Visit(ReturnInstructionNode* instruction) { }
- void Visit(IfNeInstructionNode* instruction) { }
- void Visit(MoveResultInstructionNode* instruction);
- void Visit(InvokeStaticInstructionNode* instruction);
- void Visit(AddIntInstructionNode* instruction);
- void Visit(GotoInstructionNode* instruction) { }
- void Visit(IfEqzInstructionNode* instruction) { }
-
- const Type* MergeTypes(std::vector<const Type*>& types) const;
- const Type* MergeTypes(const Type* t1, const Type* t2) const;
- std::vector<const Type*> GetOperandTypes(InstructionNode* instruction) const;
- const Type* GetType() {
- // TODO: Currently multiple defined types are not supported.
- if (!crt_type_.empty()) {
- const Type* single_type = crt_type_.at(0);
- crt_type_.clear();
- return single_type;
- }
- return NULL;
- }
-
- protected:
- const SeaGraph* const graph_;
- TypeData* type_data_;
- art::verifier::RegTypeCache* type_cache_;
- std::vector<const Type*> crt_type_; // Stored temporarily between two calls to Visit.
-};
-
-} // namespace sea_ir
-
-#endif // ART_COMPILER_SEA_IR_TYPES_TYPE_INFERENCE_VISITOR_H_
diff --git a/compiler/sea_ir/types/type_inference_visitor_test.cc b/compiler/sea_ir/types/type_inference_visitor_test.cc
deleted file mode 100644
index ccb6991..0000000
--- a/compiler/sea_ir/types/type_inference_visitor_test.cc
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "common_compiler_test.h"
-#include "sea_ir/types/type_inference_visitor.h"
-#include "sea_ir/ir/sea.h"
-
-namespace sea_ir {
-
-class TestInstructionNode:public InstructionNode {
- public:
- explicit TestInstructionNode(std::vector<InstructionNode*> prods): InstructionNode(NULL),
- producers_(prods) { }
- std::vector<InstructionNode*> GetSSAProducers() {
- return producers_;
- }
- protected:
- std::vector<InstructionNode*> producers_;
-};
-
-class TypeInferenceVisitorTest : public art::CommonCompilerTest {};
-
-TEST_F(TypeInferenceVisitorTest, MergeIntWithByte) {
- TypeData td;
- art::verifier::RegTypeCache type_cache(false);
- TypeInferenceVisitor tiv(NULL, &td, &type_cache);
- const Type* int_type = &type_cache.Integer();
- const Type* byte_type = &type_cache.Byte();
- const Type* ib_type = tiv.MergeTypes(int_type, byte_type);
- const Type* bi_type = tiv.MergeTypes(byte_type, int_type);
- EXPECT_TRUE(ib_type == int_type);
- EXPECT_TRUE(bi_type == int_type);
-}
-
-TEST_F(TypeInferenceVisitorTest, MergeIntWithShort) {
- TypeData td;
- art::verifier::RegTypeCache type_cache(false);
- TypeInferenceVisitor tiv(NULL, &td, &type_cache);
- const Type* int_type = &type_cache.Integer();
- const Type* short_type = &type_cache.Short();
- const Type* is_type = tiv.MergeTypes(int_type, short_type);
- const Type* si_type = tiv.MergeTypes(short_type, int_type);
- EXPECT_TRUE(is_type == int_type);
- EXPECT_TRUE(si_type == int_type);
-}
-
-TEST_F(TypeInferenceVisitorTest, MergeMultipleInts) {
- int N = 10; // Number of types to merge.
- TypeData td;
- art::verifier::RegTypeCache type_cache(false);
- TypeInferenceVisitor tiv(NULL, &td, &type_cache);
- std::vector<const Type*> types;
- for (int i = 0; i < N; i++) {
- const Type* new_type = &type_cache.Integer();
- types.push_back(new_type);
- }
- const Type* merged_type = tiv.MergeTypes(types);
- EXPECT_TRUE(merged_type == &type_cache.Integer());
-}
-
-TEST_F(TypeInferenceVisitorTest, MergeMultipleShorts) {
- int N = 10; // Number of types to merge.
- TypeData td;
- art::verifier::RegTypeCache type_cache(false);
- TypeInferenceVisitor tiv(NULL, &td, &type_cache);
- std::vector<const Type*> types;
- for (int i = 0; i < N; i++) {
- const Type* new_type = &type_cache.Short();
- types.push_back(new_type);
- }
- const Type* merged_type = tiv.MergeTypes(types);
- EXPECT_TRUE(merged_type == &type_cache.Short());
-}
-
-TEST_F(TypeInferenceVisitorTest, MergeMultipleIntsWithShorts) {
- int N = 10; // Number of types to merge.
- TypeData td;
- art::verifier::RegTypeCache type_cache(false);
- TypeInferenceVisitor tiv(NULL, &td, &type_cache);
- std::vector<const Type*> types;
- for (int i = 0; i < N; i++) {
- const Type* short_type = &type_cache.Short();
- const Type* int_type = &type_cache.Integer();
- types.push_back(short_type);
- types.push_back(int_type);
- }
- const Type* merged_type = tiv.MergeTypes(types);
- EXPECT_TRUE(merged_type == &type_cache.Integer());
-}
-
-TEST_F(TypeInferenceVisitorTest, GetOperandTypes) {
- int N = 10; // Number of types to merge.
- TypeData td;
- art::verifier::RegTypeCache type_cache(false);
- TypeInferenceVisitor tiv(NULL, &td, &type_cache);
- std::vector<const Type*> types;
- std::vector<InstructionNode*> preds;
- for (int i = 0; i < N; i++) {
- const Type* short_type = &type_cache.Short();
- const Type* int_type = &type_cache.Integer();
- TestInstructionNode* short_inst =
- new TestInstructionNode(std::vector<InstructionNode*>());
- TestInstructionNode* int_inst =
- new TestInstructionNode(std::vector<InstructionNode*>());
- preds.push_back(short_inst);
- preds.push_back(int_inst);
- td.SetTypeOf(short_inst->Id(), short_type);
- td.SetTypeOf(int_inst->Id(), int_type);
- types.push_back(short_type);
- types.push_back(int_type);
- }
- TestInstructionNode* inst_to_test = new TestInstructionNode(preds);
- std::vector<const Type*> result = tiv.GetOperandTypes(inst_to_test);
- EXPECT_TRUE(result.size() == types.size());
- EXPECT_TRUE(true == std::equal(types.begin(), types.begin() + 2, result.begin()));
-}
-
-
-} // namespace sea_ir
diff --git a/compiler/sea_ir/types/types.h b/compiler/sea_ir/types/types.h
deleted file mode 100644
index 64f2524..0000000
--- a/compiler/sea_ir/types/types.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_SEA_IR_TYPES_TYPES_H_
-#define ART_COMPILER_SEA_IR_TYPES_TYPES_H_
-
-#include "safe_map.h"
-#include "verifier/reg_type.h"
-#include "verifier/reg_type_cache.h"
-
-namespace sea_ir {
-
-// TODO: Replace typedef with an actual class implementation when we have more types.
-typedef art::verifier::RegType Type;
-
-// Stores information about the result type of each instruction.
-// Note: Main purpose is to encapsulate the map<instruction id, type*>,
-// so that we can replace the underlying storage at any time.
-class TypeData {
- public:
- art::SafeMap<int, const Type*>* GetTypeMap() {
- return &type_map_;
- }
- // Returns the type associated with instruction with @instruction_id.
- const Type* FindTypeOf(int instruction_id) {
- art::SafeMap<int, const Type*>::const_iterator result_it = type_map_.find(instruction_id);
- if (type_map_.end() != result_it) {
- return result_it->second;
- }
- return NULL;
- }
-
- // Saves the fact that instruction @instruction_id produces a value of type @type.
- void SetTypeOf(int instruction_id, const Type* type) {
- type_map_.Overwrite(instruction_id, type);
- }
-
- private:
- art::SafeMap<int, const Type*> type_map_;
-};
-
-
-
-} // namespace sea_ir
-#endif // ART_COMPILER_SEA_IR_TYPES_TYPES_H_
diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc
index cb07ffa..cb51ed8 100644
--- a/compiler/trampolines/trampoline_compiler.cc
+++ b/compiler/trampolines/trampoline_compiler.cc
@@ -20,6 +20,7 @@
#include "utils/arm/assembler_arm.h"
#include "utils/arm64/assembler_arm64.h"
#include "utils/mips/assembler_mips.h"
+#include "utils/mips64/assembler_mips64.h"
#include "utils/x86/assembler_x86.h"
#include "utils/x86_64/assembler_x86_64.h"
@@ -40,8 +41,7 @@
__ LoadFromOffset(kLoadWord, IP, R0, JNIEnvExt::SelfOffset().Int32Value());
__ LoadFromOffset(kLoadWord, PC, IP, offset.Int32Value());
break;
- case kPortableAbi: // R9 holds Thread*.
- case kQuickAbi: // Fall-through.
+ case kQuickAbi: // R9 holds Thread*.
__ LoadFromOffset(kLoadWord, PC, R9, offset.Int32Value());
}
__ bkpt(0);
@@ -75,8 +75,7 @@
Arm64ManagedRegister::FromXRegister(IP0));
break;
- case kPortableAbi: // X18 holds Thread*.
- case kQuickAbi: // Fall-through.
+ case kQuickAbi: // X18 holds Thread*.
__ JumpTo(Arm64ManagedRegister::FromXRegister(TR), Offset(offset.Int32Value()),
Arm64ManagedRegister::FromXRegister(IP0));
@@ -106,8 +105,7 @@
__ LoadFromOffset(kLoadWord, T9, A0, JNIEnvExt::SelfOffset().Int32Value());
__ LoadFromOffset(kLoadWord, T9, T9, offset.Int32Value());
break;
- case kPortableAbi: // S1 holds Thread*.
- case kQuickAbi: // Fall-through.
+ case kQuickAbi: // S1 holds Thread*.
__ LoadFromOffset(kLoadWord, T9, S1, offset.Int32Value());
}
__ Jr(T9);
@@ -123,6 +121,35 @@
}
} // namespace mips
+namespace mips64 {
+static const std::vector<uint8_t>* CreateTrampoline(EntryPointCallingConvention abi,
+ ThreadOffset<8> offset) {
+ std::unique_ptr<Mips64Assembler> assembler(static_cast<Mips64Assembler*>(Assembler::Create(kMips64)));
+
+ switch (abi) {
+ case kInterpreterAbi: // Thread* is first argument (A0) in interpreter ABI.
+ __ LoadFromOffset(kLoadDoubleword, T9, A0, offset.Int32Value());
+ break;
+ case kJniAbi: // Load via Thread* held in JNIEnv* in first argument (A0).
+ __ LoadFromOffset(kLoadDoubleword, T9, A0, JNIEnvExt::SelfOffset().Int32Value());
+ __ LoadFromOffset(kLoadDoubleword, T9, T9, offset.Int32Value());
+ break;
+ case kQuickAbi: // Fall-through.
+ __ LoadFromOffset(kLoadDoubleword, T9, S1, offset.Int32Value());
+ }
+ __ Jr(T9);
+ __ Nop();
+ __ Break();
+
+ size_t cs = assembler->CodeSize();
+ std::unique_ptr<std::vector<uint8_t>> entry_stub(new std::vector<uint8_t>(cs));
+ MemoryRegion code(&(*entry_stub)[0], entry_stub->size());
+ assembler->FinalizeInstructions(code);
+
+ return entry_stub.release();
+}
+} // namespace mips64
+
namespace x86 {
static const std::vector<uint8_t>* CreateTrampoline(ThreadOffset<4> offset) {
std::unique_ptr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));
@@ -163,6 +190,8 @@
switch (isa) {
case kArm64:
return arm64::CreateTrampoline(abi, offset);
+ case kMips64:
+ return mips64::CreateTrampoline(abi, offset);
case kX86_64:
return x86_64::CreateTrampoline(offset);
default:
diff --git a/compiler/utils/arena_bit_vector.h b/compiler/utils/arena_bit_vector.h
index 34f1ca9..e5e1b70 100644
--- a/compiler/utils/arena_bit_vector.h
+++ b/compiler/utils/arena_bit_vector.h
@@ -35,14 +35,10 @@
kBitMapDominators,
kBitMapIDominated,
kBitMapDomFrontier,
- kBitMapPhi,
- kBitMapTmpBlocks,
- kBitMapInputBlocks,
kBitMapRegisterV,
kBitMapTempSSARegisterV,
kBitMapNullCheck,
kBitMapClInitCheck,
- kBitMapTmpBlockV,
kBitMapPredecessors,
kNumBitMapKinds
};
diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc
index 0528773..1f44f19 100644
--- a/compiler/utils/arm/assembler_arm.cc
+++ b/compiler/utils/arm/assembler_arm.cc
@@ -245,6 +245,7 @@
// This is very like the ARM encoding except the offset is 10 bits.
uint32_t Address::encodingThumbLdrdStrd() const {
+ DCHECK(IsImmediate());
uint32_t encoding;
uint32_t am = am_;
// If P is 0 then W must be 1 (Different from ARM).
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index c86ec4b..0d84ba7 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -429,6 +429,8 @@
virtual void ldrex(Register rd, Register rn, Condition cond = AL) = 0;
virtual void strex(Register rd, Register rt, Register rn, Condition cond = AL) = 0;
+ virtual void ldrexd(Register rt, Register rt2, Register rn, Condition cond = AL) = 0;
+ virtual void strexd(Register rd, Register rt, Register rt2, Register rn, Condition cond = AL) = 0;
// Miscellaneous instructions.
virtual void clrex(Condition cond = AL) = 0;
@@ -532,6 +534,23 @@
// Load and Store. May clobber IP.
virtual void LoadImmediate(Register rd, int32_t value, Condition cond = AL) = 0;
+ void LoadSImmediate(SRegister sd, float value, Condition cond = AL) {
+ if (!vmovs(sd, value, cond)) {
+ LoadImmediate(IP, bit_cast<int32_t, float>(value), cond);
+ vmovsr(sd, IP, cond);
+ }
+ }
+
+ void LoadDImmediate(DRegister sd, double value, Condition cond = AL) {
+ if (!vmovd(sd, value, cond)) {
+ uint64_t int_value = bit_cast<uint64_t, double>(value);
+ LoadSImmediate(
+ static_cast<SRegister>(sd << 1), bit_cast<float, uint32_t>(Low32Bits(int_value)));
+ LoadSImmediate(
+ static_cast<SRegister>((sd << 1) + 1), bit_cast<float, uint32_t>(High32Bits(int_value)));
+ }
+ }
+
virtual void MarkExceptionHandler(Label* label) = 0;
virtual void LoadFromOffset(LoadOperandType type,
Register reg,
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index 8f6d45a..8d1fb60 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -778,6 +778,7 @@
Emit(encoding);
}
+
void Arm32Assembler::ldrex(Register rt, Register rn, Condition cond) {
CHECK_NE(rn, kNoRegister);
CHECK_NE(rt, kNoRegister);
@@ -793,6 +794,25 @@
}
+void Arm32Assembler::ldrexd(Register rt, Register rt2, Register rn, Condition cond) {
+ CHECK_NE(rn, kNoRegister);
+ CHECK_NE(rt, kNoRegister);
+ CHECK_NE(rt2, kNoRegister);
+ CHECK_NE(rt, R14);
+ CHECK_EQ(0u, static_cast<uint32_t>(rt) % 2);
+ CHECK_EQ(static_cast<uint32_t>(rt) + 1, static_cast<uint32_t>(rt2));
+ CHECK_NE(cond, kNoCondition);
+
+ int32_t encoding =
+ (static_cast<uint32_t>(cond) << kConditionShift) |
+ B24 | B23 | B21 | B20 |
+ static_cast<uint32_t>(rn) << 16 |
+ static_cast<uint32_t>(rt) << 12 |
+ B11 | B10 | B9 | B8 | B7 | B4 | B3 | B2 | B1 | B0;
+ Emit(encoding);
+}
+
+
void Arm32Assembler::strex(Register rd,
Register rt,
Register rn,
@@ -811,6 +831,28 @@
Emit(encoding);
}
+void Arm32Assembler::strexd(Register rd, Register rt, Register rt2, Register rn, Condition cond) {
+ CHECK_NE(rd, kNoRegister);
+ CHECK_NE(rn, kNoRegister);
+ CHECK_NE(rt, kNoRegister);
+ CHECK_NE(rt2, kNoRegister);
+ CHECK_NE(rt, R14);
+ CHECK_NE(rd, rt);
+ CHECK_NE(rd, rt2);
+ CHECK_EQ(0u, static_cast<uint32_t>(rt) % 2);
+ CHECK_EQ(static_cast<uint32_t>(rt) + 1, static_cast<uint32_t>(rt2));
+ CHECK_NE(cond, kNoCondition);
+
+ int32_t encoding =
+ (static_cast<uint32_t>(cond) << kConditionShift) |
+ B24 | B23 | B21 |
+ static_cast<uint32_t>(rn) << 16 |
+ static_cast<uint32_t>(rd) << 12 |
+ B11 | B10 | B9 | B8 | B7 | B4 |
+ static_cast<uint32_t>(rt);
+ Emit(encoding);
+}
+
void Arm32Assembler::clrex(Condition cond) {
CHECK_EQ(cond, AL); // This cannot be conditional on ARM.
diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h
index 6c8d415..b922d66 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -123,6 +123,8 @@
void ldrex(Register rd, Register rn, Condition cond = AL) OVERRIDE;
void strex(Register rd, Register rt, Register rn, Condition cond = AL) OVERRIDE;
+ void ldrexd(Register rt, Register rt2, Register rn, Condition cond = AL) OVERRIDE;
+ void strexd(Register rd, Register rt, Register rt2, Register rn, Condition cond = AL) OVERRIDE;
// Miscellaneous instructions.
void clrex(Condition cond = AL) OVERRIDE;
diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc
index 951792d..4a0ae0b 100644
--- a/compiler/utils/arm/assembler_arm32_test.cc
+++ b/compiler/utils/arm/assembler_arm32_test.cc
@@ -697,4 +697,28 @@
DriverStr(expected, "vmrs");
}
+TEST_F(AssemblerArm32Test, ldrexd) {
+ GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R0);
+ GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R1);
+ GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R2);
+
+ const char* expected =
+ "ldrexd r0, r1, [r0]\n"
+ "ldrexd r0, r1, [r1]\n"
+ "ldrexd r0, r1, [r2]\n";
+ DriverStr(expected, "ldrexd");
+}
+
+TEST_F(AssemblerArm32Test, strexd) {
+ GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R0);
+ GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R1);
+ GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R2);
+
+ const char* expected =
+ "strexd r9, r0, r1, [r0]\n"
+ "strexd r9, r0, r1, [r1]\n"
+ "strexd r9, r0, r1, [r2]\n";
+ DriverStr(expected, "strexd");
+}
+
} // namespace art
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 479186c..5383c28 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -25,8 +25,8 @@
namespace art {
namespace arm {
-bool Thumb2Assembler::ShifterOperandCanHold(Register rd,
- Register rn,
+bool Thumb2Assembler::ShifterOperandCanHold(Register rd ATTRIBUTE_UNUSED,
+ Register rn ATTRIBUTE_UNUSED,
Opcode opcode,
uint32_t immediate,
ShifterOperand* shifter_op) {
@@ -37,13 +37,6 @@
switch (opcode) {
case ADD:
case SUB:
- if (rn == SP) {
- if (rd == SP) {
- return immediate < (1 << 9); // 9 bits allowed.
- } else {
- return immediate < (1 << 12); // 12 bits.
- }
- }
if (immediate < (1 << 12)) { // Less than (or equal to) 12 bits can always be done.
return true;
}
@@ -698,48 +691,37 @@
return true;
}
- bool can_contain_high_register = (opcode == MOV)
- || ((opcode == ADD || opcode == SUB) && (rn == rd));
-
- if (IsHighRegister(rd) || IsHighRegister(rn)) {
- if (can_contain_high_register) {
- // There are high register instructions available for this opcode.
- // However, there is no RRX available.
- if (so.IsShift() && so.GetShift() == RRX) {
- return true;
+ // Check special case for SP relative ADD and SUB immediate.
+ if ((opcode == ADD || opcode == SUB) && rn == SP && so.IsImmediate()) {
+ // If the immediate is in range, use 16 bit.
+ if (rd == SP) {
+ if (so.GetImmediate() < (1 << 9)) { // 9 bit immediate.
+ return false;
}
-
- // Check special case for SP relative ADD and SUB immediate.
- if ((opcode == ADD || opcode == SUB) && so.IsImmediate()) {
- // If rn is SP and rd is a high register we need to use a 32 bit encoding.
- if (rn == SP && rd != SP && IsHighRegister(rd)) {
- return true;
- }
-
- uint32_t imm = so.GetImmediate();
- // If the immediates are out of range use 32 bit.
- if (rd == SP && rn == SP) {
- if (imm > (1 << 9)) { // 9 bit immediate.
- return true;
- }
- } else if (opcode == ADD && rd != SP && rn == SP) { // 10 bit immediate.
- if (imm > (1 << 10)) {
- return true;
- }
- } else if (opcode == SUB && rd != SP && rn == SP) {
- // SUB rd, SP, #imm is always 32 bit.
- return true;
- }
+ } else if (!IsHighRegister(rd) && opcode == ADD) {
+ if (so.GetImmediate() < (1 << 10)) { // 10 bit immediate.
+ return false;
}
}
+ }
- // The ADD,SUB and MOV instructions that work with high registers don't have
- // immediate variants.
- if (so.IsImmediate()) {
+ bool can_contain_high_register = (opcode == MOV)
+ || ((opcode == ADD) && (rn == rd) && !set_cc);
+
+ if (IsHighRegister(rd) || IsHighRegister(rn)) {
+ if (!can_contain_high_register) {
return true;
}
- if (!can_contain_high_register) {
+ // There are high register instructions available for this opcode.
+ // However, there is no actual shift available, neither for ADD nor for MOV (ASR/LSR/LSL/ROR).
+ if (so.IsShift() && (so.GetShift() == RRX || so.GetImmediate() != 0u)) {
+ return true;
+ }
+
+ // The ADD and MOV instructions that work with high registers don't have 16-bit
+ // immediate variants.
+ if (so.IsImmediate()) {
return true;
}
}
@@ -938,41 +920,71 @@
if (so.IsImmediate()) {
use_immediate = true;
immediate = so.GetImmediate();
+ } else {
+ // Adjust rn and rd: only two registers will be emitted.
+ switch (opcode) {
+ case AND:
+ case ORR:
+ case EOR:
+ case RSB:
+ case ADC:
+ case SBC:
+ case BIC: {
+ if (rn == rd) {
+ rn = so.GetRegister();
+ } else {
+ CHECK_EQ(rd, so.GetRegister());
+ }
+ break;
+ }
+ case CMP:
+ case CMN: {
+ CHECK_EQ(rd, 0);
+ rd = rn;
+ rn = so.GetRegister();
+ break;
+ }
+ case TST:
+ case TEQ:
+ case MVN: {
+ CHECK_EQ(rn, 0);
+ rn = so.GetRegister();
+ break;
+ }
+ default:
+ break;
+ }
}
switch (opcode) {
case AND: thumb_opcode = 0U /* 0b0000 */; break;
+ case ORR: thumb_opcode = 12U /* 0b1100 */; break;
case EOR: thumb_opcode = 1U /* 0b0001 */; break;
- case SUB: break;
case RSB: thumb_opcode = 9U /* 0b1001 */; break;
- case ADD: break;
case ADC: thumb_opcode = 5U /* 0b0101 */; break;
case SBC: thumb_opcode = 6U /* 0b0110 */; break;
- case RSC: break;
- case TST: thumb_opcode = 8U /* 0b1000 */; rn = so.GetRegister(); break;
- case TEQ: break;
- case CMP:
+ case BIC: thumb_opcode = 14U /* 0b1110 */; break;
+ case TST: thumb_opcode = 8U /* 0b1000 */; CHECK(!use_immediate); break;
+ case MVN: thumb_opcode = 15U /* 0b1111 */; CHECK(!use_immediate); break;
+ case CMP: {
if (use_immediate) {
// T2 encoding.
- dp_opcode = 0;
- opcode_shift = 11;
- thumb_opcode = 5U /* 0b101 */;
- rd_shift = 8;
- rn_shift = 8;
+ dp_opcode = 0;
+ opcode_shift = 11;
+ thumb_opcode = 5U /* 0b101 */;
+ rd_shift = 8;
+ rn_shift = 8;
} else {
thumb_opcode = 10U /* 0b1010 */;
- rd = rn;
- rn = so.GetRegister();
}
break;
+ }
case CMN: {
+ CHECK(!use_immediate);
thumb_opcode = 11U /* 0b1011 */;
- rd = rn;
- rn = so.GetRegister();
break;
}
- case ORR: thumb_opcode = 12U /* 0b1100 */; break;
case MOV:
dp_opcode = 0;
if (use_immediate) {
@@ -995,9 +1007,11 @@
}
}
break;
- case BIC: thumb_opcode = 14U /* 0b1110 */; break;
- case MVN: thumb_opcode = 15U /* 0b1111 */; rn = so.GetRegister(); break;
+
+ case TEQ:
+ case RSC:
default:
+ LOG(FATAL) << "Invalid thumb1 opcode " << opcode;
break;
}
}
@@ -1020,7 +1034,7 @@
// ADD and SUB are complex enough to warrant their own emitter.
void Thumb2Assembler::Emit16BitAddSub(Condition cond ATTRIBUTE_UNUSED,
Opcode opcode,
- bool set_cc ATTRIBUTE_UNUSED,
+ bool set_cc,
Register rn,
Register rd,
const ShifterOperand& so) {
@@ -1030,7 +1044,7 @@
uint8_t rn_shift = 3;
uint8_t immediate_shift = 0;
bool use_immediate = false;
- uint8_t immediate = 0;
+ uint32_t immediate = 0; // Should be at most 9 bits but keep the full immediate for CHECKs.
uint8_t thumb_opcode;;
if (so.IsImmediate()) {
@@ -1042,7 +1056,7 @@
case ADD:
if (so.IsRegister()) {
Register rm = so.GetRegister();
- if (rn == rd) {
+ if (rn == rd && !set_cc) {
// Can use T2 encoding (allows 4 bit registers)
dp_opcode = 1U /* 0b01 */;
opcode_shift = 10;
@@ -1066,8 +1080,8 @@
dp_opcode = 2U /* 0b10 */;
thumb_opcode = 3U /* 0b11 */;
opcode_shift = 12;
- CHECK_LT(immediate, (1 << 9));
- CHECK_EQ((immediate & 3 /* 0b11 */), 0);
+ CHECK_LT(immediate, (1u << 9));
+ CHECK_EQ((immediate & 3u /* 0b11 */), 0u);
// Remove rd and rn from instruction by orring it with immed and clearing bits.
rn = R0;
@@ -1080,8 +1094,8 @@
dp_opcode = 2U /* 0b10 */;
thumb_opcode = 5U /* 0b101 */;
opcode_shift = 11;
- CHECK_LT(immediate, (1 << 10));
- CHECK_EQ((immediate & 3 /* 0b11 */), 0);
+ CHECK_LT(immediate, (1u << 10));
+ CHECK_EQ((immediate & 3u /* 0b11 */), 0u);
// Remove rn from instruction.
rn = R0;
@@ -1117,8 +1131,8 @@
dp_opcode = 2U /* 0b10 */;
thumb_opcode = 0x61 /* 0b1100001 */;
opcode_shift = 7;
- CHECK_LT(immediate, (1 << 9));
- CHECK_EQ((immediate & 3 /* 0b11 */), 0);
+ CHECK_LT(immediate, (1u << 9));
+ CHECK_EQ((immediate & 3u /* 0b11 */), 0u);
// Remove rd and rn from instruction by orring it with immed and clearing bits.
rn = R0;
@@ -1673,9 +1687,6 @@
CHECK_NE(rn, kNoRegister);
CHECK_NE(rt, kNoRegister);
CheckCondition(cond);
- CHECK_NE(rn, kNoRegister);
- CHECK_NE(rt, kNoRegister);
- CheckCondition(cond);
CHECK_LT(imm, (1u << 10));
int32_t encoding = B31 | B30 | B29 | B27 | B22 | B20 |
@@ -1712,6 +1723,22 @@
}
+void Thumb2Assembler::ldrexd(Register rt, Register rt2, Register rn, Condition cond) {
+ CHECK_NE(rn, kNoRegister);
+ CHECK_NE(rt, kNoRegister);
+ CHECK_NE(rt2, kNoRegister);
+ CHECK_NE(rt, rt2);
+ CheckCondition(cond);
+
+ int32_t encoding = B31 | B30 | B29 | B27 | B23 | B22 | B20 |
+ static_cast<uint32_t>(rn) << 16 |
+ static_cast<uint32_t>(rt) << 12 |
+ static_cast<uint32_t>(rt2) << 8 |
+ B6 | B5 | B4 | B3 | B2 | B1 | B0;
+ Emit32(encoding);
+}
+
+
void Thumb2Assembler::strex(Register rd,
Register rt,
Register rn,
@@ -1720,6 +1747,26 @@
}
+void Thumb2Assembler::strexd(Register rd, Register rt, Register rt2, Register rn, Condition cond) {
+ CHECK_NE(rd, kNoRegister);
+ CHECK_NE(rn, kNoRegister);
+ CHECK_NE(rt, kNoRegister);
+ CHECK_NE(rt2, kNoRegister);
+ CHECK_NE(rt, rt2);
+ CHECK_NE(rd, rt);
+ CHECK_NE(rd, rt2);
+ CheckCondition(cond);
+
+ int32_t encoding = B31 | B30 | B29 | B27 | B23 | B22 |
+ static_cast<uint32_t>(rn) << 16 |
+ static_cast<uint32_t>(rt) << 12 |
+ static_cast<uint32_t>(rt2) << 8 |
+ B6 | B5 | B4 |
+ static_cast<uint32_t>(rd);
+ Emit32(encoding);
+}
+
+
void Thumb2Assembler::clrex(Condition cond) {
CheckCondition(cond);
int32_t encoding = B31 | B30 | B29 | B27 | B28 | B25 | B24 | B23 |
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index 48a3a7e..81dd138 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -149,6 +149,8 @@
void ldrex(Register rd, Register rn, uint16_t imm, Condition cond = AL);
void strex(Register rd, Register rt, Register rn, uint16_t imm, Condition cond = AL);
+ void ldrexd(Register rt, Register rt2, Register rn, Condition cond = AL) OVERRIDE;
+ void strexd(Register rd, Register rt, Register rt2, Register rn, Condition cond = AL) OVERRIDE;
// Miscellaneous instructions.
void clrex(Condition cond = AL) OVERRIDE;
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 6ae95a4..ebea9d4 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -30,11 +30,15 @@
}
std::string GetAssemblerParameters() OVERRIDE {
- return " -mthumb -mfpu=neon";
+ return " -march=armv7-a -mcpu=cortex-a15 -mfpu=neon -mthumb";
+ }
+
+ const char* GetAssemblyHeader() OVERRIDE {
+ return kThumb2AssemblyHeader;
}
std::string GetDisassembleParameters() OVERRIDE {
- return " -D -bbinary -marm --no-show-raw-insn";
+ return " -D -bbinary -marm --disassembler-options=force-thumb --no-show-raw-insn";
}
void SetUpHelpers() OVERRIDE {
@@ -76,6 +80,8 @@
private:
std::vector<arm::Register*> registers_;
+
+ static constexpr const char* kThumb2AssemblyHeader = ".syntax unified\n.thumb\n";
};
@@ -164,4 +170,61 @@
DriverStr(expected, "vmrs");
}
+TEST_F(AssemblerThumb2Test, ldrexd) {
+ GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R0);
+ GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R1);
+ GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R2);
+ GetAssembler()->ldrexd(arm::R5, arm::R3, arm::R7);
+
+ const char* expected =
+ "ldrexd r0, r1, [r0]\n"
+ "ldrexd r0, r1, [r1]\n"
+ "ldrexd r0, r1, [r2]\n"
+ "ldrexd r5, r3, [r7]\n";
+ DriverStr(expected, "ldrexd");
+}
+
+TEST_F(AssemblerThumb2Test, strexd) {
+ GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R0);
+ GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R1);
+ GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R2);
+ GetAssembler()->strexd(arm::R9, arm::R5, arm::R3, arm::R7);
+
+ const char* expected =
+ "strexd r9, r0, r1, [r0]\n"
+ "strexd r9, r0, r1, [r1]\n"
+ "strexd r9, r0, r1, [r2]\n"
+ "strexd r9, r5, r3, [r7]\n";
+ DriverStr(expected, "strexd");
+}
+
+TEST_F(AssemblerThumb2Test, LdrdStrd) {
+ GetAssembler()->ldrd(arm::R0, arm::Address(arm::R2, 8));
+ GetAssembler()->ldrd(arm::R0, arm::Address(arm::R12));
+ GetAssembler()->strd(arm::R0, arm::Address(arm::R2, 8));
+
+ const char* expected =
+ "ldrd r0, r1, [r2, #8]\n"
+ "ldrd r0, r1, [r12]\n"
+ "strd r0, r1, [r2, #8]\n";
+ DriverStr(expected, "ldrdstrd");
+}
+
+TEST_F(AssemblerThumb2Test, eor) {
+#define __ GetAssembler()->
+ __ eor(arm::R1, arm::R1, arm::ShifterOperand(arm::R0));
+ __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R1));
+ __ eor(arm::R1, arm::R8, arm::ShifterOperand(arm::R0));
+ __ eor(arm::R8, arm::R1, arm::ShifterOperand(arm::R0));
+ __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R8));
+
+ const char* expected =
+ "eors r1, r0\n"
+ "eor r1, r0, r1\n"
+ "eor r1, r8, r0\n"
+ "eor r8, r1, r0\n"
+ "eor r1, r0, r8\n";
+ DriverStr(expected, "abs");
+}
+
} // namespace art
diff --git a/compiler/utils/array_ref.h b/compiler/utils/array_ref.h
index 1a7f2e8..b1b0ee5 100644
--- a/compiler/utils/array_ref.h
+++ b/compiler/utils/array_ref.h
@@ -84,7 +84,7 @@
template <typename U, typename Alloc>
ArrayRef(const std::vector<U, Alloc>& v,
- typename std::enable_if<std::is_same<T, const U>::value, tag>::tag
+ typename std::enable_if<std::is_same<T, const U>::value, tag>::type
t ATTRIBUTE_UNUSED = tag())
: array_(v.data()), size_(v.size()) {
}
diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc
index 6834512..5340dd3 100644
--- a/compiler/utils/assembler.cc
+++ b/compiler/utils/assembler.cc
@@ -23,6 +23,7 @@
#include "arm/assembler_thumb2.h"
#include "arm64/assembler_arm64.h"
#include "mips/assembler_mips.h"
+#include "mips64/assembler_mips64.h"
#include "x86/assembler_x86.h"
#include "x86_64/assembler_x86_64.h"
#include "globals.h"
@@ -115,6 +116,8 @@
return new arm64::Arm64Assembler();
case kMips:
return new mips::MipsAssembler();
+ case kMips64:
+ return new mips64::Mips64Assembler();
case kX86:
return new x86::X86Assembler();
case kX86_64:
diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h
index 67711e3..923ecdb 100644
--- a/compiler/utils/assembler.h
+++ b/compiler/utils/assembler.h
@@ -47,6 +47,9 @@
namespace mips {
class MipsAssembler;
}
+namespace mips64 {
+ class Mips64Assembler;
+}
namespace x86 {
class X86Assembler;
}
@@ -120,6 +123,7 @@
friend class arm::Thumb2Assembler;
friend class arm64::Arm64Assembler;
friend class mips::MipsAssembler;
+ friend class mips64::Mips64Assembler;
friend class x86::X86Assembler;
friend class x86_64::X86_64Assembler;
@@ -502,6 +506,8 @@
virtual void InitializeFrameDescriptionEntry() {}
virtual void FinalizeFrameDescriptionEntry() {}
+ // Give a vector containing FDE data, or null if not used. Note: the assembler must take care
+ // of handling the lifecycle.
virtual std::vector<uint8_t>* GetFrameDescriptionEntry() { return nullptr; }
virtual ~Assembler() {}
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index 2b55120..6f8b301 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -29,6 +29,10 @@
namespace art {
+// If you want to take a look at the differences between the ART assembler and GCC, set this flag
+// to true. The disassembled files will then remain in the tmp directory.
+static constexpr bool kKeepDisassembledFiles = false;
+
// Helper for a constexpr string length.
constexpr size_t ConstexprStrLen(char const* str, size_t count = 0) {
return ('\0' == str[0]) ? count : ConstexprStrLen(str+1, count+1);
@@ -685,12 +689,12 @@
bool result = CompareFiles(data_name + ".dis", as_name + ".dis");
- // If you want to take a look at the differences between the ART assembler and GCC, comment
- // out the removal code.
-// std::remove(data_name.c_str());
-// std::remove(as_name.c_str());
-// std::remove((data_name + ".dis").c_str());
-// std::remove((as_name + ".dis").c_str());
+ if (!kKeepDisassembledFiles) {
+ std::remove(data_name.c_str());
+ std::remove(as_name.c_str());
+ std::remove((data_name + ".dis").c_str());
+ std::remove((as_name + ".dis").c_str());
+ }
return result;
}
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index e3a9580..a171e59 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -309,13 +309,13 @@
// 16 bit variants.
__ add(R0, R1, ShifterOperand());
__ sub(R0, R1, ShifterOperand());
- __ and_(R0, R1, ShifterOperand());
- __ orr(R0, R1, ShifterOperand());
- __ eor(R0, R1, ShifterOperand());
- __ bic(R0, R1, ShifterOperand());
- __ adc(R0, R1, ShifterOperand());
- __ sbc(R0, R1, ShifterOperand());
- __ rsb(R0, R1, ShifterOperand());
+ __ and_(R0, R0, ShifterOperand(R1));
+ __ orr(R0, R0, ShifterOperand(R1));
+ __ eor(R0, R0, ShifterOperand(R1));
+ __ bic(R0, R0, ShifterOperand(R1));
+ __ adc(R0, R0, ShifterOperand(R1));
+ __ sbc(R0, R0, ShifterOperand(R1));
+ __ rsb(R0, R0, ShifterOperand(R1));
__ tst(R0, ShifterOperand(R1));
__ teq(R0, ShifterOperand(R1));
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 3f2641c..3d03234 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -102,11 +102,11 @@
" 4: 11a3 asrs r3, r4, #6\n",
" 6: ea4f 13f4 mov.w r3, r4, ror #7\n",
" a: 41e3 rors r3, r4\n",
- " c: 0128 lsls r0, r5, #4\n",
- " e: 0968 lsrs r0, r5, #5\n",
- " 10: 11a8 asrs r0, r5, #6\n",
- " 12: ea4f 18f4 mov.w r8, r4, ror #7\n",
- " 16: ea4f 0834 mov.w r8, r4, rrx\n",
+ " c: ea4f 1804 mov.w r8, r4, lsl #4\n",
+ " 10: ea4f 1854 mov.w r8, r4, lsr #5\n",
+ " 14: ea4f 18a4 mov.w r8, r4, asr #6\n",
+ " 18: ea4f 18f4 mov.w r8, r4, ror #7\n",
+ " 1c: ea4f 0834 mov.w r8, r4, rrx\n",
nullptr
};
const char* BasicLoadResults[] = {
@@ -340,15 +340,15 @@
nullptr
};
const char* SpecialAddSubResults[] = {
- " 0: f20d 0250 addw r2, sp, #80 ; 0x50\n",
- " 4: f20d 0d50 addw sp, sp, #80 ; 0x50\n",
- " 8: f20d 0850 addw r8, sp, #80 ; 0x50\n",
- " c: f60d 7200 addw r2, sp, #3840 ; 0xf00\n",
- " 10: f60d 7d00 addw sp, sp, #3840 ; 0xf00\n",
- " 14: f2ad 0d50 subw sp, sp, #80 ; 0x50\n",
- " 18: f2ad 0050 subw r0, sp, #80 ; 0x50\n",
- " 1c: f2ad 0850 subw r8, sp, #80 ; 0x50\n",
- " 20: f6ad 7d00 subw sp, sp, #3840 ; 0xf00\n",
+ " 0: aa14 add r2, sp, #80 ; 0x50\n",
+ " 2: b014 add sp, #80 ; 0x50\n",
+ " 4: f20d 0850 addw r8, sp, #80 ; 0x50\n",
+ " 8: f60d 7200 addw r2, sp, #3840 ; 0xf00\n",
+ " c: f60d 7d00 addw sp, sp, #3840 ; 0xf00\n",
+ " 10: b094 sub sp, #80 ; 0x50\n",
+ " 12: f2ad 0050 subw r0, sp, #80 ; 0x50\n",
+ " 16: f2ad 0850 subw r8, sp, #80 ; 0x50\n",
+ " 1a: f6ad 7d00 subw sp, sp, #3840 ; 0xf00\n",
nullptr
};
const char* StoreToOffsetResults[] = {
diff --git a/compiler/utils/dedupe_set.h b/compiler/utils/dedupe_set.h
index 4c52174..b062a2a 100644
--- a/compiler/utils/dedupe_set.h
+++ b/compiler/utils/dedupe_set.h
@@ -17,50 +17,89 @@
#ifndef ART_COMPILER_UTILS_DEDUPE_SET_H_
#define ART_COMPILER_UTILS_DEDUPE_SET_H_
+#include <algorithm>
+#include <inttypes.h>
+#include <memory>
#include <set>
#include <string>
#include "base/mutex.h"
#include "base/stl_util.h"
#include "base/stringprintf.h"
+#include "utils/swap_space.h"
namespace art {
// A set of Keys that support a HashFunc returning HashType. Used to find duplicates of Key in the
// Add method. The data-structure is thread-safe through the use of internal locks, it also
// supports the lock being sharded.
-template <typename Key, typename HashType, typename HashFunc, HashType kShard = 1>
+template <typename InKey, typename StoreKey, typename HashType, typename HashFunc,
+ HashType kShard = 1>
class DedupeSet {
- typedef std::pair<HashType, Key*> HashedKey;
+ typedef std::pair<HashType, const InKey*> HashedInKey;
+ struct HashedKey {
+ StoreKey* store_ptr;
+ union {
+ HashType store_hash; // Valid if store_ptr != nullptr.
+ const HashedInKey* in_key; // Valid if store_ptr == nullptr.
+ };
+ };
class Comparator {
public:
bool operator()(const HashedKey& a, const HashedKey& b) const {
- if (a.first != b.first) {
- return a.first < b.first;
+ HashType a_hash = (a.store_ptr != nullptr) ? a.store_hash : a.in_key->first;
+ HashType b_hash = (b.store_ptr != nullptr) ? b.store_hash : b.in_key->first;
+ if (a_hash != b_hash) {
+ return a_hash < b_hash;
+ }
+ if (a.store_ptr != nullptr && b.store_ptr != nullptr) {
+ return std::lexicographical_compare(a.store_ptr->begin(), a.store_ptr->end(),
+ b.store_ptr->begin(), b.store_ptr->end());
+ } else if (a.store_ptr != nullptr && b.store_ptr == nullptr) {
+ return std::lexicographical_compare(a.store_ptr->begin(), a.store_ptr->end(),
+ b.in_key->second->begin(), b.in_key->second->end());
+ } else if (a.store_ptr == nullptr && b.store_ptr != nullptr) {
+ return std::lexicographical_compare(a.in_key->second->begin(), a.in_key->second->end(),
+ b.store_ptr->begin(), b.store_ptr->end());
} else {
- return *a.second < *b.second;
+ return std::lexicographical_compare(a.in_key->second->begin(), a.in_key->second->end(),
+ b.in_key->second->begin(), b.in_key->second->end());
}
}
};
public:
- Key* Add(Thread* self, const Key& key) {
+ StoreKey* Add(Thread* self, const InKey& key) {
+ uint64_t hash_start;
+ if (kIsDebugBuild) {
+ hash_start = NanoTime();
+ }
HashType raw_hash = HashFunc()(key);
+ if (kIsDebugBuild) {
+ uint64_t hash_end = NanoTime();
+ hash_time_ += hash_end - hash_start;
+ }
HashType shard_hash = raw_hash / kShard;
HashType shard_bin = raw_hash % kShard;
- HashedKey hashed_key(shard_hash, const_cast<Key*>(&key));
+ HashedInKey hashed_in_key(shard_hash, &key);
+ HashedKey hashed_key;
+ hashed_key.store_ptr = nullptr;
+ hashed_key.in_key = &hashed_in_key;
MutexLock lock(self, *lock_[shard_bin]);
auto it = keys_[shard_bin].find(hashed_key);
if (it != keys_[shard_bin].end()) {
- return it->second;
+ DCHECK(it->store_ptr != nullptr);
+ return it->store_ptr;
}
- hashed_key.second = new Key(key);
+ hashed_key.store_ptr = CreateStoreKey(key);
+ hashed_key.store_hash = shard_hash;
keys_[shard_bin].insert(hashed_key);
- return hashed_key.second;
+ return hashed_key.store_ptr;
}
- explicit DedupeSet(const char* set_name) {
+ explicit DedupeSet(const char* set_name, SwapAllocator<void>& alloc)
+ : allocator_(alloc), hash_time_(0) {
for (HashType i = 0; i < kShard; ++i) {
std::ostringstream oss;
oss << set_name << " lock " << i;
@@ -70,15 +109,59 @@
}
~DedupeSet() {
- for (HashType i = 0; i < kShard; ++i) {
- STLDeleteValues(&keys_[i]);
+ // Have to manually free all pointers.
+ for (auto& shard : keys_) {
+ for (const auto& hashed_key : shard) {
+ DCHECK(hashed_key.store_ptr != nullptr);
+ DeleteStoreKey(hashed_key.store_ptr);
+ }
}
}
+ std::string DumpStats() const {
+ size_t collision_sum = 0;
+ size_t collision_max = 0;
+ for (HashType shard = 0; shard < kShard; ++shard) {
+ HashType last_hash = 0;
+ size_t collision_cur_max = 0;
+ for (const HashedKey& key : keys_[shard]) {
+ DCHECK(key.store_ptr != nullptr);
+ if (key.store_hash == last_hash) {
+ collision_cur_max++;
+ if (collision_cur_max > 1) {
+ collision_sum++;
+ if (collision_cur_max > collision_max) {
+ collision_max = collision_cur_max;
+ }
+ }
+ } else {
+ collision_cur_max = 1;
+ last_hash = key.store_hash;
+ }
+ }
+ }
+ return StringPrintf("%zu collisions, %zu max bucket size, %" PRIu64 " ns hash time",
+ collision_sum, collision_max, hash_time_);
+ }
+
private:
+ StoreKey* CreateStoreKey(const InKey& key) {
+ StoreKey* ret = allocator_.allocate(1);
+ allocator_.construct(ret, key.begin(), key.end(), allocator_);
+ return ret;
+ }
+
+ void DeleteStoreKey(StoreKey* key) {
+ SwapAllocator<StoreKey> alloc(allocator_);
+ alloc.destroy(key);
+ alloc.deallocate(key, 1);
+ }
+
std::string lock_name_[kShard];
std::unique_ptr<Mutex> lock_[kShard];
std::set<HashedKey, Comparator> keys_[kShard];
+ SwapAllocator<StoreKey> allocator_;
+ uint64_t hash_time_;
DISALLOW_COPY_AND_ASSIGN(DedupeSet);
};
diff --git a/compiler/utils/dedupe_set_test.cc b/compiler/utils/dedupe_set_test.cc
index 8abe6de..637964e 100644
--- a/compiler/utils/dedupe_set_test.cc
+++ b/compiler/utils/dedupe_set_test.cc
@@ -15,6 +15,10 @@
*/
#include "dedupe_set.h"
+
+#include <algorithm>
+#include <cstdio>
+
#include "gtest/gtest.h"
#include "thread-inl.h"
@@ -35,19 +39,22 @@
TEST(DedupeSetTest, Test) {
Thread* self = Thread::Current();
typedef std::vector<uint8_t> ByteArray;
- DedupeSet<ByteArray, size_t, DedupeHashFunc> deduplicator("test");
- ByteArray* array1;
+ SwapAllocator<void> swap(nullptr);
+ DedupeSet<ByteArray, SwapVector<uint8_t>, size_t, DedupeHashFunc> deduplicator("test", swap);
+ SwapVector<uint8_t>* array1;
{
ByteArray test1;
test1.push_back(10);
test1.push_back(20);
test1.push_back(30);
test1.push_back(45);
+
array1 = deduplicator.Add(self, test1);
- ASSERT_EQ(test1, *array1);
+ ASSERT_NE(array1, nullptr);
+ ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array1->begin()));
}
- ByteArray* array2;
+ SwapVector<uint8_t>* array2;
{
ByteArray test1;
test1.push_back(10);
@@ -56,10 +63,10 @@
test1.push_back(45);
array2 = deduplicator.Add(self, test1);
ASSERT_EQ(array2, array1);
- ASSERT_EQ(test1, *array2);
+ ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array2->begin()));
}
- ByteArray* array3;
+ SwapVector<uint8_t>* array3;
{
ByteArray test1;
test1.push_back(10);
@@ -67,8 +74,8 @@
test1.push_back(30);
test1.push_back(47);
array3 = deduplicator.Add(self, test1);
- ASSERT_NE(array3, &test1);
- ASSERT_EQ(test1, *array3);
+ ASSERT_NE(array3, nullptr);
+ ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array3->begin()));
}
}
diff --git a/compiler/utils/growable_array.h b/compiler/utils/growable_array.h
index fde65e7..6af4853 100644
--- a/compiler/utils/growable_array.h
+++ b/compiler/utils/growable_array.h
@@ -37,6 +37,17 @@
kArenaAllocGrowableArray));
}
+ GrowableArray(ArenaAllocator* arena, size_t init_length, T initial_data)
+ : arena_(arena),
+ num_allocated_(init_length),
+ num_used_(init_length) {
+ elem_list_ = static_cast<T*>(arena_->Alloc(sizeof(T) * init_length,
+ kArenaAllocGrowableArray));
+ for (size_t i = 0; i < init_length; ++i) {
+ elem_list_[i] = initial_data;
+ }
+ }
+
// Expand the list size to at least new length.
void Resize(size_t new_length) {
diff --git a/compiler/utils/managed_register.h b/compiler/utils/managed_register.h
index bfb2829..bb62bca 100644
--- a/compiler/utils/managed_register.h
+++ b/compiler/utils/managed_register.h
@@ -30,6 +30,9 @@
namespace mips {
class MipsManagedRegister;
}
+namespace mips64 {
+class Mips64ManagedRegister;
+}
namespace x86 {
class X86ManagedRegister;
@@ -54,6 +57,7 @@
arm::ArmManagedRegister AsArm() const;
arm64::Arm64ManagedRegister AsArm64() const;
mips::MipsManagedRegister AsMips() const;
+ mips64::Mips64ManagedRegister AsMips64() const;
x86::X86ManagedRegister AsX86() const;
x86_64::X86_64ManagedRegister AsX86_64() const;
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 8001dcd..b5437b0 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -332,7 +332,7 @@
}
void MipsAssembler::Jr(Register rs) {
- EmitR(0, rs, static_cast<Register>(0), static_cast<Register>(0), 0, 0x08);
+ EmitR(0, rs, static_cast<Register>(0), static_cast<Register>(0), 0, 0x09); // Jalr zero, rs
Nop();
}
@@ -420,7 +420,7 @@
}
void MipsAssembler::Move(Register rt, Register rs) {
- EmitI(0x8, rs, rt, 0);
+ EmitI(0x9, rs, rt, 0); // Addiu
}
void MipsAssembler::Clear(Register rt) {
@@ -447,11 +447,11 @@
}
void MipsAssembler::AddConstant(Register rt, Register rs, int32_t value) {
- Addi(rt, rs, value);
+ Addiu(rt, rs, value);
}
void MipsAssembler::LoadImmediate(Register rt, int32_t value) {
- Addi(rt, ZERO, value);
+ Addiu(rt, ZERO, value);
}
void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset,
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
new file mode 100644
index 0000000..233ae7d
--- /dev/null
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -0,0 +1,1036 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "assembler_mips64.h"
+
+#include "base/casts.h"
+#include "entrypoints/quick/quick_entrypoints.h"
+#include "memory_region.h"
+#include "thread.h"
+
+namespace art {
+namespace mips64 {
+
+void Mips64Assembler::Emit(int32_t value) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ buffer_.Emit<int32_t>(value);
+}
+
+void Mips64Assembler::EmitR(int opcode, GpuRegister rs, GpuRegister rt, GpuRegister rd,
+ int shamt, int funct) {
+ CHECK_NE(rs, kNoGpuRegister);
+ CHECK_NE(rt, kNoGpuRegister);
+ CHECK_NE(rd, kNoGpuRegister);
+ int32_t encoding = opcode << kOpcodeShift |
+ static_cast<int32_t>(rs) << kRsShift |
+ static_cast<int32_t>(rt) << kRtShift |
+ static_cast<int32_t>(rd) << kRdShift |
+ shamt << kShamtShift |
+ funct;
+ Emit(encoding);
+}
+
+void Mips64Assembler::EmitI(int opcode, GpuRegister rs, GpuRegister rt, uint16_t imm) {
+ CHECK_NE(rs, kNoGpuRegister);
+ CHECK_NE(rt, kNoGpuRegister);
+ int32_t encoding = opcode << kOpcodeShift |
+ static_cast<int32_t>(rs) << kRsShift |
+ static_cast<int32_t>(rt) << kRtShift |
+ imm;
+ Emit(encoding);
+}
+
+void Mips64Assembler::EmitJ(int opcode, int address) {
+ int32_t encoding = opcode << kOpcodeShift |
+ address;
+ Emit(encoding);
+}
+
+void Mips64Assembler::EmitFR(int opcode, int fmt, FpuRegister ft, FpuRegister fs, FpuRegister fd,
+int funct) {
+ CHECK_NE(ft, kNoFpuRegister);
+ CHECK_NE(fs, kNoFpuRegister);
+ CHECK_NE(fd, kNoFpuRegister);
+ int32_t encoding = opcode << kOpcodeShift |
+ fmt << kFmtShift |
+ static_cast<int32_t>(ft) << kFtShift |
+ static_cast<int32_t>(fs) << kFsShift |
+ static_cast<int32_t>(fd) << kFdShift |
+ funct;
+ Emit(encoding);
+}
+
+void Mips64Assembler::EmitFI(int opcode, int fmt, FpuRegister rt, uint16_t imm) {
+ CHECK_NE(rt, kNoFpuRegister);
+ int32_t encoding = opcode << kOpcodeShift |
+ fmt << kFmtShift |
+ static_cast<int32_t>(rt) << kRtShift |
+ imm;
+ Emit(encoding);
+}
+
+void Mips64Assembler::EmitBranch(GpuRegister rt, GpuRegister rs, Label* label, bool equal) {
+ int offset;
+ if (label->IsBound()) {
+ offset = label->Position() - buffer_.Size();
+ } else {
+ // Use the offset field of the branch instruction for linking the sites.
+ offset = label->position_;
+ label->LinkTo(buffer_.Size());
+ }
+ if (equal) {
+ Beq(rt, rs, (offset >> 2) & kBranchOffsetMask);
+ } else {
+ Bne(rt, rs, (offset >> 2) & kBranchOffsetMask);
+ }
+}
+
+void Mips64Assembler::EmitJump(Label* label, bool link) {
+ int offset;
+ if (label->IsBound()) {
+ offset = label->Position() - buffer_.Size();
+ } else {
+ // Use the offset field of the jump instruction for linking the sites.
+ offset = label->position_;
+ label->LinkTo(buffer_.Size());
+ }
+ if (link) {
+ Jal((offset >> 2) & kJumpOffsetMask);
+ } else {
+ J((offset >> 2) & kJumpOffsetMask);
+ }
+}
+
+int32_t Mips64Assembler::EncodeBranchOffset(int offset, int32_t inst, bool is_jump) {
+ CHECK_ALIGNED(offset, 4);
+ CHECK(IsInt(POPCOUNT(kBranchOffsetMask), offset)) << offset;
+
+ // Properly preserve only the bits supported in the instruction.
+ offset >>= 2;
+ if (is_jump) {
+ offset &= kJumpOffsetMask;
+ return (inst & ~kJumpOffsetMask) | offset;
+ } else {
+ offset &= kBranchOffsetMask;
+ return (inst & ~kBranchOffsetMask) | offset;
+ }
+}
+
+int Mips64Assembler::DecodeBranchOffset(int32_t inst, bool is_jump) {
+ // Sign-extend, then left-shift by 2.
+ if (is_jump) {
+ return (((inst & kJumpOffsetMask) << 6) >> 4);
+ } else {
+ return (((inst & kBranchOffsetMask) << 16) >> 14);
+ }
+}
+
+void Mips64Assembler::Bind(Label* label, bool is_jump) {
+ CHECK(!label->IsBound());
+ int bound_pc = buffer_.Size();
+ while (label->IsLinked()) {
+ int32_t position = label->Position();
+ int32_t next = buffer_.Load<int32_t>(position);
+ int32_t offset = is_jump ? bound_pc - position : bound_pc - position - 4;
+ int32_t encoded = Mips64Assembler::EncodeBranchOffset(offset, next, is_jump);
+ buffer_.Store<int32_t>(position, encoded);
+ label->position_ = Mips64Assembler::DecodeBranchOffset(next, is_jump);
+ }
+ label->BindTo(bound_pc);
+}
+
+void Mips64Assembler::Add(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+ EmitR(0, rs, rt, rd, 0, 0x20);
+}
+
+void Mips64Assembler::Addi(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x8, rs, rt, imm16);
+}
+
+void Mips64Assembler::Addu(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+ EmitR(0, rs, rt, rd, 0, 0x21);
+}
+
+void Mips64Assembler::Addiu(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x9, rs, rt, imm16);
+}
+
+void Mips64Assembler::Daddiu(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x19, rs, rt, imm16);
+}
+
+void Mips64Assembler::Sub(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+ EmitR(0, rs, rt, rd, 0, 0x22);
+}
+
+void Mips64Assembler::Subu(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+ EmitR(0, rs, rt, rd, 0, 0x23);
+}
+
+void Mips64Assembler::Mult(GpuRegister rs, GpuRegister rt) {
+ EmitR(0, rs, rt, static_cast<GpuRegister>(0), 0, 0x18);
+}
+
+void Mips64Assembler::Multu(GpuRegister rs, GpuRegister rt) {
+ EmitR(0, rs, rt, static_cast<GpuRegister>(0), 0, 0x19);
+}
+
+void Mips64Assembler::Div(GpuRegister rs, GpuRegister rt) {
+ EmitR(0, rs, rt, static_cast<GpuRegister>(0), 0, 0x1a);
+}
+
+void Mips64Assembler::Divu(GpuRegister rs, GpuRegister rt) {
+ EmitR(0, rs, rt, static_cast<GpuRegister>(0), 0, 0x1b);
+}
+
+void Mips64Assembler::And(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+ EmitR(0, rs, rt, rd, 0, 0x24);
+}
+
+void Mips64Assembler::Andi(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0xc, rs, rt, imm16);
+}
+
+void Mips64Assembler::Or(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+ EmitR(0, rs, rt, rd, 0, 0x25);
+}
+
+void Mips64Assembler::Ori(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0xd, rs, rt, imm16);
+}
+
+void Mips64Assembler::Xor(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+ EmitR(0, rs, rt, rd, 0, 0x26);
+}
+
+void Mips64Assembler::Xori(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0xe, rs, rt, imm16);
+}
+
+void Mips64Assembler::Nor(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+ EmitR(0, rs, rt, rd, 0, 0x27);
+}
+
+void Mips64Assembler::Sll(GpuRegister rd, GpuRegister rs, int shamt) {
+ EmitR(0, rs, static_cast<GpuRegister>(0), rd, shamt, 0x00);
+}
+
+void Mips64Assembler::Srl(GpuRegister rd, GpuRegister rs, int shamt) {
+ EmitR(0, rs, static_cast<GpuRegister>(0), rd, shamt, 0x02);
+}
+
+void Mips64Assembler::Sra(GpuRegister rd, GpuRegister rs, int shamt) {
+ EmitR(0, rs, static_cast<GpuRegister>(0), rd, shamt, 0x03);
+}
+
+void Mips64Assembler::Sllv(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+ EmitR(0, rs, rt, rd, 0, 0x04);
+}
+
+void Mips64Assembler::Srlv(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+ EmitR(0, rs, rt, rd, 0, 0x06);
+}
+
+void Mips64Assembler::Srav(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+ EmitR(0, rs, rt, rd, 0, 0x07);
+}
+
+void Mips64Assembler::Lb(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x20, rs, rt, imm16);
+}
+
+void Mips64Assembler::Lh(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x21, rs, rt, imm16);
+}
+
+void Mips64Assembler::Lw(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x23, rs, rt, imm16);
+}
+
+void Mips64Assembler::Ld(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x37, rs, rt, imm16);
+}
+
+void Mips64Assembler::Lbu(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x24, rs, rt, imm16);
+}
+
+void Mips64Assembler::Lhu(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x25, rs, rt, imm16);
+}
+
+void Mips64Assembler::Lui(GpuRegister rt, uint16_t imm16) {
+ EmitI(0xf, static_cast<GpuRegister>(0), rt, imm16);
+}
+
+void Mips64Assembler::Mfhi(GpuRegister rd) {
+ EmitR(0, static_cast<GpuRegister>(0), static_cast<GpuRegister>(0), rd, 0, 0x10);
+}
+
+void Mips64Assembler::Mflo(GpuRegister rd) {
+ EmitR(0, static_cast<GpuRegister>(0), static_cast<GpuRegister>(0), rd, 0, 0x12);
+}
+
+void Mips64Assembler::Sb(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x28, rs, rt, imm16);
+}
+
+void Mips64Assembler::Sh(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x29, rs, rt, imm16);
+}
+
+void Mips64Assembler::Sw(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x2b, rs, rt, imm16);
+}
+
+void Mips64Assembler::Sd(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x3f, rs, rt, imm16);
+}
+
+void Mips64Assembler::Slt(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+ EmitR(0, rs, rt, rd, 0, 0x2a);
+}
+
+void Mips64Assembler::Sltu(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+ EmitR(0, rs, rt, rd, 0, 0x2b);
+}
+
+void Mips64Assembler::Slti(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0xa, rs, rt, imm16);
+}
+
+void Mips64Assembler::Sltiu(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0xb, rs, rt, imm16);
+}
+
+void Mips64Assembler::Beq(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x4, rs, rt, imm16);
+ Nop();
+}
+
+void Mips64Assembler::Bne(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x5, rs, rt, imm16);
+ Nop();
+}
+
+void Mips64Assembler::J(uint32_t address) {
+ EmitJ(0x2, address);
+ Nop();
+}
+
+void Mips64Assembler::Jal(uint32_t address) {
+ EmitJ(0x2, address);
+ Nop();
+}
+
+void Mips64Assembler::Jr(GpuRegister rs) {
+ EmitR(0, rs, static_cast<GpuRegister>(0), static_cast<GpuRegister>(0), 0, 0x09); // Jalr zero, rs
+ Nop();
+}
+
+void Mips64Assembler::Jalr(GpuRegister rs) {
+ EmitR(0, rs, static_cast<GpuRegister>(0), RA, 0, 0x09);
+ Nop();
+}
+
+void Mips64Assembler::AddS(FpuRegister fd, FpuRegister fs, FpuRegister ft) {
+ EmitFR(0x11, 0x10, ft, fs, fd, 0x0);
+}
+
+void Mips64Assembler::SubS(FpuRegister fd, FpuRegister fs, FpuRegister ft) {
+ EmitFR(0x11, 0x10, ft, fs, fd, 0x1);
+}
+
+void Mips64Assembler::MulS(FpuRegister fd, FpuRegister fs, FpuRegister ft) {
+ EmitFR(0x11, 0x10, ft, fs, fd, 0x2);
+}
+
+void Mips64Assembler::DivS(FpuRegister fd, FpuRegister fs, FpuRegister ft) {
+ EmitFR(0x11, 0x10, ft, fs, fd, 0x3);
+}
+
+void Mips64Assembler::AddD(FpuRegister fd, FpuRegister fs, FpuRegister ft) {
+ EmitFR(0x11, 0x11, static_cast<FpuRegister>(ft), static_cast<FpuRegister>(fs),
+ static_cast<FpuRegister>(fd), 0x0);
+}
+
+void Mips64Assembler::SubD(FpuRegister fd, FpuRegister fs, FpuRegister ft) {
+ EmitFR(0x11, 0x11, static_cast<FpuRegister>(ft), static_cast<FpuRegister>(fs),
+ static_cast<FpuRegister>(fd), 0x1);
+}
+
+void Mips64Assembler::MulD(FpuRegister fd, FpuRegister fs, FpuRegister ft) {
+ EmitFR(0x11, 0x11, static_cast<FpuRegister>(ft), static_cast<FpuRegister>(fs),
+ static_cast<FpuRegister>(fd), 0x2);
+}
+
+void Mips64Assembler::DivD(FpuRegister fd, FpuRegister fs, FpuRegister ft) {
+ EmitFR(0x11, 0x11, static_cast<FpuRegister>(ft), static_cast<FpuRegister>(fs),
+ static_cast<FpuRegister>(fd), 0x3);
+}
+
+void Mips64Assembler::MovS(FpuRegister fd, FpuRegister fs) {
+ EmitFR(0x11, 0x10, static_cast<FpuRegister>(0), fs, fd, 0x6);
+}
+
+void Mips64Assembler::MovD(FpuRegister fd, FpuRegister fs) {
+ EmitFR(0x11, 0x11, static_cast<FpuRegister>(0), static_cast<FpuRegister>(fs),
+ static_cast<FpuRegister>(fd), 0x6);
+}
+
+void Mips64Assembler::Mfc1(GpuRegister rt, FpuRegister fs) {
+ EmitFR(0x11, 0x00, static_cast<FpuRegister>(rt), fs, static_cast<FpuRegister>(0), 0x0);
+}
+
+void Mips64Assembler::Mtc1(FpuRegister ft, GpuRegister rs) {
+ EmitFR(0x11, 0x04, ft, static_cast<FpuRegister>(rs), static_cast<FpuRegister>(0), 0x0);
+}
+
+void Mips64Assembler::Lwc1(FpuRegister ft, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x31, rs, static_cast<GpuRegister>(ft), imm16);
+}
+
+void Mips64Assembler::Ldc1(FpuRegister ft, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x35, rs, static_cast<GpuRegister>(ft), imm16);
+}
+
+void Mips64Assembler::Swc1(FpuRegister ft, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x39, rs, static_cast<GpuRegister>(ft), imm16);
+}
+
+void Mips64Assembler::Sdc1(FpuRegister ft, GpuRegister rs, uint16_t imm16) {
+ EmitI(0x3d, rs, static_cast<GpuRegister>(ft), imm16);
+}
+
+void Mips64Assembler::Break() {
+ EmitR(0, static_cast<GpuRegister>(0), static_cast<GpuRegister>(0),
+ static_cast<GpuRegister>(0), 0, 0xD);
+}
+
+void Mips64Assembler::Nop() {
+ EmitR(0x0, static_cast<GpuRegister>(0), static_cast<GpuRegister>(0),
+ static_cast<GpuRegister>(0), 0, 0x0);
+}
+
+void Mips64Assembler::Move(GpuRegister rt, GpuRegister rs) {
+ EmitI(0x19, rs, rt, 0); // Daddiu
+}
+
+void Mips64Assembler::Clear(GpuRegister rt) {
+ EmitR(0, static_cast<GpuRegister>(0), static_cast<GpuRegister>(0), rt, 0, 0x20);
+}
+
+void Mips64Assembler::Not(GpuRegister rt, GpuRegister rs) {
+ EmitR(0, static_cast<GpuRegister>(0), rs, rt, 0, 0x27);
+}
+
+void Mips64Assembler::Mul(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+ Mult(rs, rt);
+ Mflo(rd);
+}
+
+void Mips64Assembler::Div(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+ Div(rs, rt);
+ Mflo(rd);
+}
+
+void Mips64Assembler::Rem(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+ Div(rs, rt);
+ Mfhi(rd);
+}
+
+void Mips64Assembler::AddConstant64(GpuRegister rt, GpuRegister rs, int32_t value) {
+ CHECK((value >= -32768) && (value <= 32766));
+ Daddiu(rt, rs, value);
+}
+
+void Mips64Assembler::LoadImmediate64(GpuRegister rt, int32_t value) {
+ CHECK((value >= -32768) && (value <= 32766));
+ Daddiu(rt, ZERO, value);
+}
+
+void Mips64Assembler::LoadFromOffset(LoadOperandType type, GpuRegister reg, GpuRegister base,
+ int32_t offset) {
+ switch (type) {
+ case kLoadSignedByte:
+ Lb(reg, base, offset);
+ break;
+ case kLoadUnsignedByte:
+ Lbu(reg, base, offset);
+ break;
+ case kLoadSignedHalfword:
+ Lh(reg, base, offset);
+ break;
+ case kLoadUnsignedHalfword:
+ Lhu(reg, base, offset);
+ break;
+ case kLoadWord:
+ Lw(reg, base, offset);
+ break;
+ case kLoadDoubleword:
+ // TODO: alignment issues ???
+ Ld(reg, base, offset);
+ break;
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ }
+}
+
+void Mips64Assembler::LoadFpuFromOffset(LoadOperandType type, FpuRegister reg, GpuRegister base,
+ int32_t offset) {
+ CHECK((offset >= -32768) && (offset <= 32766));
+ switch (type) {
+ case kLoadWord:
+ Lwc1(reg, base, offset);
+ break;
+ case kLoadDoubleword:
+ // TODO: alignment issues ???
+ Ldc1(reg, base, offset);
+ break;
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ }
+}
+
+void Mips64Assembler::EmitLoad(ManagedRegister m_dst, GpuRegister src_register, int32_t src_offset,
+ size_t size) {
+ Mips64ManagedRegister dst = m_dst.AsMips64();
+ if (dst.IsNoRegister()) {
+ CHECK_EQ(0u, size) << dst;
+ } else if (dst.IsGpuRegister()) {
+ if (size == 4) {
+ CHECK_EQ(4u, size) << dst;
+ LoadFromOffset(kLoadWord, dst.AsGpuRegister(), src_register, src_offset);
+ } else if (size == 8) {
+ CHECK_EQ(8u, size) << dst;
+ LoadFromOffset(kLoadDoubleword, dst.AsGpuRegister(), src_register, src_offset);
+ } else {
+ UNIMPLEMENTED(FATAL) << "We only support Load() of size 4 and 8";
+ }
+ } else if (dst.IsFpuRegister()) {
+ if (size == 4) {
+ CHECK_EQ(4u, size) << dst;
+ LoadFpuFromOffset(kLoadWord, dst.AsFpuRegister(), src_register, src_offset);
+ } else if (size == 8) {
+ CHECK_EQ(8u, size) << dst;
+ LoadFpuFromOffset(kLoadDoubleword, dst.AsFpuRegister(), src_register, src_offset);
+ } else {
+ UNIMPLEMENTED(FATAL) << "We only support Load() of size 4 and 8";
+ }
+ }
+}
+
+void Mips64Assembler::StoreToOffset(StoreOperandType type, GpuRegister reg, GpuRegister base,
+ int32_t offset) {
+ switch (type) {
+ case kStoreByte:
+ Sb(reg, base, offset);
+ break;
+ case kStoreHalfword:
+ Sh(reg, base, offset);
+ break;
+ case kStoreWord:
+ Sw(reg, base, offset);
+ break;
+ case kStoreDoubleword:
+ // TODO: alignment issues ???
+ Sd(reg, base, offset);
+ break;
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ }
+}
+
+void Mips64Assembler::StoreFpuToOffset(StoreOperandType type, FpuRegister reg, GpuRegister base,
+ int32_t offset) {
+ switch (type) {
+ case kStoreWord:
+ Swc1(reg, base, offset);
+ break;
+ case kStoreDoubleword:
+ Sdc1(reg, base, offset);
+ break;
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ }
+}
+
+constexpr size_t kFramePointerSize = 8;
+
+void Mips64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg,
+ const std::vector<ManagedRegister>& callee_save_regs,
+ const ManagedRegisterEntrySpills& entry_spills) {
+ CHECK_ALIGNED(frame_size, kStackAlignment);
+
+ // Increase frame to required size.
+ IncreaseFrameSize(frame_size);
+
+ // Push callee saves and return address
+ int stack_offset = frame_size - kFramePointerSize;
+ StoreToOffset(kStoreDoubleword, RA, SP, stack_offset);
+ for (int i = callee_save_regs.size() - 1; i >= 0; --i) {
+ stack_offset -= kFramePointerSize;
+ GpuRegister reg = callee_save_regs.at(i).AsMips64().AsGpuRegister();
+ StoreToOffset(kStoreDoubleword, reg, SP, stack_offset);
+ }
+
+ // Write out Method*.
+ StoreToOffset(kStoreWord, method_reg.AsMips64().AsGpuRegister(), SP, 0);
+
+ // Write out entry spills.
+ int32_t offset = frame_size + sizeof(StackReference<mirror::ArtMethod>);
+ for (size_t i = 0; i < entry_spills.size(); ++i) {
+ Mips64ManagedRegister reg = entry_spills.at(i).AsMips64();
+ ManagedRegisterSpill spill = entry_spills.at(i);
+ int32_t size = spill.getSize();
+ if (reg.IsNoRegister()) {
+ // only increment stack offset.
+ offset += size;
+ } else if (reg.IsFpuRegister()) {
+ StoreFpuToOffset((size == 4) ? kStoreWord : kStoreDoubleword, reg.AsFpuRegister(), SP, offset);
+ offset += size;
+ } else if (reg.IsGpuRegister()) {
+ StoreToOffset((size == 4) ? kStoreWord : kStoreDoubleword, reg.AsGpuRegister(), SP, offset);
+ offset += size;
+ }
+ }
+}
+
+void Mips64Assembler::RemoveFrame(size_t frame_size,
+ const std::vector<ManagedRegister>& callee_save_regs) {
+ CHECK_ALIGNED(frame_size, kStackAlignment);
+
+ // Pop callee saves and return address
+ int stack_offset = frame_size - (callee_save_regs.size() * kFramePointerSize) - kFramePointerSize;
+ for (size_t i = 0; i < callee_save_regs.size(); ++i) {
+ GpuRegister reg = callee_save_regs.at(i).AsMips64().AsGpuRegister();
+ LoadFromOffset(kLoadDoubleword, reg, SP, stack_offset);
+ stack_offset += kFramePointerSize;
+ }
+ LoadFromOffset(kLoadDoubleword, RA, SP, stack_offset);
+
+ // Decrease frame to required size.
+ DecreaseFrameSize(frame_size);
+
+ // Then jump to the return address.
+ Jr(RA);
+}
+
+void Mips64Assembler::IncreaseFrameSize(size_t adjust) {
+ CHECK_ALIGNED(adjust, kStackAlignment);
+ AddConstant64(SP, SP, -adjust);
+}
+
+void Mips64Assembler::DecreaseFrameSize(size_t adjust) {
+ CHECK_ALIGNED(adjust, kStackAlignment);
+ AddConstant64(SP, SP, adjust);
+}
+
+void Mips64Assembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) {
+ Mips64ManagedRegister src = msrc.AsMips64();
+ if (src.IsNoRegister()) {
+ CHECK_EQ(0u, size);
+ } else if (src.IsGpuRegister()) {
+ CHECK(size == 4 || size == 8) << size;
+ if (size == 8) {
+ StoreToOffset(kStoreDoubleword, src.AsGpuRegister(), SP, dest.Int32Value());
+ } else if (size == 4) {
+ StoreToOffset(kStoreWord, src.AsGpuRegister(), SP, dest.Int32Value());
+ } else {
+ UNIMPLEMENTED(FATAL) << "We only support Store() of size 4 and 8";
+ }
+ } else if (src.IsFpuRegister()) {
+ CHECK(size == 4 || size == 8) << size;
+ if (size == 8) {
+ StoreFpuToOffset(kStoreDoubleword, src.AsFpuRegister(), SP, dest.Int32Value());
+ } else if (size == 4) {
+ StoreFpuToOffset(kStoreWord, src.AsFpuRegister(), SP, dest.Int32Value());
+ } else {
+ UNIMPLEMENTED(FATAL) << "We only support Store() of size 4 and 8";
+ }
+ }
+}
+
+void Mips64Assembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
+ Mips64ManagedRegister src = msrc.AsMips64();
+ CHECK(src.IsGpuRegister());
+ StoreToOffset(kStoreWord, src.AsGpuRegister(), SP, dest.Int32Value());
+}
+
+void Mips64Assembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
+ Mips64ManagedRegister src = msrc.AsMips64();
+ CHECK(src.IsGpuRegister());
+ StoreToOffset(kStoreDoubleword, src.AsGpuRegister(), SP, dest.Int32Value());
+}
+
+void Mips64Assembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm,
+ ManagedRegister mscratch) {
+ Mips64ManagedRegister scratch = mscratch.AsMips64();
+ CHECK(scratch.IsGpuRegister()) << scratch;
+ LoadImmediate64(scratch.AsGpuRegister(), imm);
+ StoreToOffset(kStoreWord, scratch.AsGpuRegister(), SP, dest.Int32Value());
+}
+
+void Mips64Assembler::StoreImmediateToThread64(ThreadOffset<8> dest, uint32_t imm,
+ ManagedRegister mscratch) {
+ Mips64ManagedRegister scratch = mscratch.AsMips64();
+ CHECK(scratch.IsGpuRegister()) << scratch;
+ LoadImmediate64(scratch.AsGpuRegister(), imm);
+ StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), S1, dest.Int32Value());
+}
+
+void Mips64Assembler::StoreStackOffsetToThread64(ThreadOffset<8> thr_offs,
+ FrameOffset fr_offs,
+ ManagedRegister mscratch) {
+ Mips64ManagedRegister scratch = mscratch.AsMips64();
+ CHECK(scratch.IsGpuRegister()) << scratch;
+ AddConstant64(scratch.AsGpuRegister(), SP, fr_offs.Int32Value());
+ StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), S1, thr_offs.Int32Value());
+}
+
+void Mips64Assembler::StoreStackPointerToThread64(ThreadOffset<8> thr_offs) {
+ StoreToOffset(kStoreDoubleword, SP, S1, thr_offs.Int32Value());
+}
+
+void Mips64Assembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc,
+ FrameOffset in_off, ManagedRegister mscratch) {
+ Mips64ManagedRegister src = msrc.AsMips64();
+ Mips64ManagedRegister scratch = mscratch.AsMips64();
+ StoreToOffset(kStoreDoubleword, src.AsGpuRegister(), SP, dest.Int32Value());
+ LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(), SP, in_off.Int32Value());
+ StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), SP, dest.Int32Value() + 8);
+}
+
+void Mips64Assembler::Load(ManagedRegister mdest, FrameOffset src, size_t size) {
+ return EmitLoad(mdest, SP, src.Int32Value(), size);
+}
+
+void Mips64Assembler::LoadFromThread64(ManagedRegister mdest, ThreadOffset<8> src, size_t size) {
+ return EmitLoad(mdest, S1, src.Int32Value(), size);
+}
+
+void Mips64Assembler::LoadRef(ManagedRegister mdest, FrameOffset src) {
+ Mips64ManagedRegister dest = mdest.AsMips64();
+ CHECK(dest.IsGpuRegister());
+ LoadFromOffset(kLoadWord, dest.AsGpuRegister(), SP, src.Int32Value());
+}
+
+void Mips64Assembler::LoadRef(ManagedRegister mdest, ManagedRegister base,
+ MemberOffset offs) {
+ Mips64ManagedRegister dest = mdest.AsMips64();
+ CHECK(dest.IsGpuRegister() && dest.IsGpuRegister());
+ LoadFromOffset(kLoadWord, dest.AsGpuRegister(),
+ base.AsMips64().AsGpuRegister(), offs.Int32Value());
+ if (kPoisonHeapReferences) {
+ Subu(dest.AsGpuRegister(), ZERO, dest.AsGpuRegister());
+ }
+}
+
+void Mips64Assembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base,
+ Offset offs) {
+ Mips64ManagedRegister dest = mdest.AsMips64();
+ CHECK(dest.IsGpuRegister() && dest.IsGpuRegister()) << dest;
+ LoadFromOffset(kLoadDoubleword, dest.AsGpuRegister(),
+ base.AsMips64().AsGpuRegister(), offs.Int32Value());
+}
+
+void Mips64Assembler::LoadRawPtrFromThread64(ManagedRegister mdest,
+ ThreadOffset<8> offs) {
+ Mips64ManagedRegister dest = mdest.AsMips64();
+ CHECK(dest.IsGpuRegister());
+ LoadFromOffset(kLoadDoubleword, dest.AsGpuRegister(), S1, offs.Int32Value());
+}
+
+void Mips64Assembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
+ UNIMPLEMENTED(FATAL) << "no sign extension necessary for mips";
+}
+
+void Mips64Assembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
+ UNIMPLEMENTED(FATAL) << "no zero extension necessary for mips";
+}
+
+void Mips64Assembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) {
+ Mips64ManagedRegister dest = mdest.AsMips64();
+ Mips64ManagedRegister src = msrc.AsMips64();
+ if (!dest.Equals(src)) {
+ if (dest.IsGpuRegister()) {
+ CHECK(src.IsGpuRegister()) << src;
+ Move(dest.AsGpuRegister(), src.AsGpuRegister());
+ } else if (dest.IsFpuRegister()) {
+ CHECK(src.IsFpuRegister()) << src;
+ if (size == 4) {
+ MovS(dest.AsFpuRegister(), src.AsFpuRegister());
+ } else if (size == 8) {
+ MovD(dest.AsFpuRegister(), src.AsFpuRegister());
+ } else {
+ UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
+ }
+ }
+ }
+}
+
+void Mips64Assembler::CopyRef(FrameOffset dest, FrameOffset src,
+ ManagedRegister mscratch) {
+ Mips64ManagedRegister scratch = mscratch.AsMips64();
+ CHECK(scratch.IsGpuRegister()) << scratch;
+ LoadFromOffset(kLoadWord, scratch.AsGpuRegister(), SP, src.Int32Value());
+ StoreToOffset(kStoreWord, scratch.AsGpuRegister(), SP, dest.Int32Value());
+}
+
+void Mips64Assembler::CopyRawPtrFromThread64(FrameOffset fr_offs,
+ ThreadOffset<8> thr_offs,
+ ManagedRegister mscratch) {
+ Mips64ManagedRegister scratch = mscratch.AsMips64();
+ CHECK(scratch.IsGpuRegister()) << scratch;
+ LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(), S1, thr_offs.Int32Value());
+ StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), SP, fr_offs.Int32Value());
+}
+
+void Mips64Assembler::CopyRawPtrToThread64(ThreadOffset<8> thr_offs,
+ FrameOffset fr_offs,
+ ManagedRegister mscratch) {
+ Mips64ManagedRegister scratch = mscratch.AsMips64();
+ CHECK(scratch.IsGpuRegister()) << scratch;
+ LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(),
+ SP, fr_offs.Int32Value());
+ StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(),
+ S1, thr_offs.Int32Value());
+}
+
+void Mips64Assembler::Copy(FrameOffset dest, FrameOffset src,
+ ManagedRegister mscratch, size_t size) {
+ Mips64ManagedRegister scratch = mscratch.AsMips64();
+ CHECK(scratch.IsGpuRegister()) << scratch;
+ CHECK(size == 4 || size == 8) << size;
+ if (size == 4) {
+ LoadFromOffset(kLoadWord, scratch.AsGpuRegister(), SP, src.Int32Value());
+ StoreToOffset(kStoreWord, scratch.AsGpuRegister(), SP, dest.Int32Value());
+ } else if (size == 8) {
+ LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(), SP, src.Int32Value());
+ StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), SP, dest.Int32Value());
+ } else {
+ UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
+ }
+}
+
+void Mips64Assembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset,
+ ManagedRegister mscratch, size_t size) {
+ GpuRegister scratch = mscratch.AsMips64().AsGpuRegister();
+ CHECK(size == 4 || size == 8) << size;
+ if (size == 4) {
+ LoadFromOffset(kLoadWord, scratch, src_base.AsMips64().AsGpuRegister(),
+ src_offset.Int32Value());
+ StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value());
+ } else if (size == 8) {
+ LoadFromOffset(kLoadDoubleword, scratch, src_base.AsMips64().AsGpuRegister(),
+ src_offset.Int32Value());
+ StoreToOffset(kStoreDoubleword, scratch, SP, dest.Int32Value());
+ } else {
+ UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
+ }
+}
+
+void Mips64Assembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src,
+ ManagedRegister mscratch, size_t size) {
+ GpuRegister scratch = mscratch.AsMips64().AsGpuRegister();
+ CHECK(size == 4 || size == 8) << size;
+ if (size == 4) {
+ LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value());
+ StoreToOffset(kStoreWord, scratch, dest_base.AsMips64().AsGpuRegister(),
+ dest_offset.Int32Value());
+ } else if (size == 8) {
+ LoadFromOffset(kLoadDoubleword, scratch, SP, src.Int32Value());
+ StoreToOffset(kStoreDoubleword, scratch, dest_base.AsMips64().AsGpuRegister(),
+ dest_offset.Int32Value());
+ } else {
+ UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
+ }
+}
+
+void Mips64Assembler::Copy(FrameOffset /*dest*/, FrameOffset /*src_base*/, Offset /*src_offset*/,
+ ManagedRegister /*mscratch*/, size_t /*size*/) {
+ UNIMPLEMENTED(FATAL) << "no mips64 implementation";
+}
+
+void Mips64Assembler::Copy(ManagedRegister dest, Offset dest_offset,
+ ManagedRegister src, Offset src_offset,
+ ManagedRegister mscratch, size_t size) {
+ GpuRegister scratch = mscratch.AsMips64().AsGpuRegister();
+ CHECK(size == 4 || size == 8) << size;
+ if (size == 4) {
+ LoadFromOffset(kLoadWord, scratch, src.AsMips64().AsGpuRegister(), src_offset.Int32Value());
+ StoreToOffset(kStoreWord, scratch, dest.AsMips64().AsGpuRegister(), dest_offset.Int32Value());
+ } else if (size == 8) {
+ LoadFromOffset(kLoadDoubleword, scratch, src.AsMips64().AsGpuRegister(),
+ src_offset.Int32Value());
+ StoreToOffset(kStoreDoubleword, scratch, dest.AsMips64().AsGpuRegister(),
+ dest_offset.Int32Value());
+ } else {
+ UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
+ }
+}
+
+void Mips64Assembler::Copy(FrameOffset /*dest*/, Offset /*dest_offset*/, FrameOffset /*src*/, Offset
+/*src_offset*/,
+ ManagedRegister /*mscratch*/, size_t /*size*/) {
+ UNIMPLEMENTED(FATAL) << "no mips64 implementation";
+}
+
+void Mips64Assembler::MemoryBarrier(ManagedRegister) {
+ UNIMPLEMENTED(FATAL) << "no mips64 implementation";
+}
+
+void Mips64Assembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
+ FrameOffset handle_scope_offset,
+ ManagedRegister min_reg, bool null_allowed) {
+ Mips64ManagedRegister out_reg = mout_reg.AsMips64();
+ Mips64ManagedRegister in_reg = min_reg.AsMips64();
+ CHECK(in_reg.IsNoRegister() || in_reg.IsGpuRegister()) << in_reg;
+ CHECK(out_reg.IsGpuRegister()) << out_reg;
+ if (null_allowed) {
+ Label null_arg;
+ // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
+ // the address in the handle scope holding the reference.
+ // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset)
+ if (in_reg.IsNoRegister()) {
+ LoadFromOffset(kLoadWord, out_reg.AsGpuRegister(),
+ SP, handle_scope_offset.Int32Value());
+ in_reg = out_reg;
+ }
+ if (!out_reg.Equals(in_reg)) {
+ LoadImmediate64(out_reg.AsGpuRegister(), 0);
+ }
+ EmitBranch(in_reg.AsGpuRegister(), ZERO, &null_arg, true);
+ AddConstant64(out_reg.AsGpuRegister(), SP, handle_scope_offset.Int32Value());
+ Bind(&null_arg, false);
+ } else {
+ AddConstant64(out_reg.AsGpuRegister(), SP, handle_scope_offset.Int32Value());
+ }
+}
+
+void Mips64Assembler::CreateHandleScopeEntry(FrameOffset out_off,
+ FrameOffset handle_scope_offset,
+ ManagedRegister mscratch,
+ bool null_allowed) {
+ Mips64ManagedRegister scratch = mscratch.AsMips64();
+ CHECK(scratch.IsGpuRegister()) << scratch;
+ if (null_allowed) {
+ Label null_arg;
+ LoadFromOffset(kLoadWord, scratch.AsGpuRegister(), SP,
+ handle_scope_offset.Int32Value());
+ // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
+ // the address in the handle scope holding the reference.
+ // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset)
+ EmitBranch(scratch.AsGpuRegister(), ZERO, &null_arg, true);
+ AddConstant64(scratch.AsGpuRegister(), SP, handle_scope_offset.Int32Value());
+ Bind(&null_arg, false);
+ } else {
+ AddConstant64(scratch.AsGpuRegister(), SP, handle_scope_offset.Int32Value());
+ }
+ StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), SP, out_off.Int32Value());
+}
+
+// Given a handle scope entry, load the associated reference.
+void Mips64Assembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg,
+ ManagedRegister min_reg) {
+ Mips64ManagedRegister out_reg = mout_reg.AsMips64();
+ Mips64ManagedRegister in_reg = min_reg.AsMips64();
+ CHECK(out_reg.IsGpuRegister()) << out_reg;
+ CHECK(in_reg.IsGpuRegister()) << in_reg;
+ Label null_arg;
+ if (!out_reg.Equals(in_reg)) {
+ LoadImmediate64(out_reg.AsGpuRegister(), 0);
+ }
+ EmitBranch(in_reg.AsGpuRegister(), ZERO, &null_arg, true);
+ LoadFromOffset(kLoadDoubleword, out_reg.AsGpuRegister(),
+ in_reg.AsGpuRegister(), 0);
+ Bind(&null_arg, false);
+}
+
+void Mips64Assembler::VerifyObject(ManagedRegister /*src*/, bool /*could_be_null*/) {
+ // TODO: not validating references
+}
+
+void Mips64Assembler::VerifyObject(FrameOffset /*src*/, bool /*could_be_null*/) {
+ // TODO: not validating references
+}
+
+void Mips64Assembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister mscratch) {
+ Mips64ManagedRegister base = mbase.AsMips64();
+ Mips64ManagedRegister scratch = mscratch.AsMips64();
+ CHECK(base.IsGpuRegister()) << base;
+ CHECK(scratch.IsGpuRegister()) << scratch;
+ LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(),
+ base.AsGpuRegister(), offset.Int32Value());
+ Jalr(scratch.AsGpuRegister());
+ // TODO: place reference map on call
+}
+
+void Mips64Assembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
+ Mips64ManagedRegister scratch = mscratch.AsMips64();
+ CHECK(scratch.IsGpuRegister()) << scratch;
+ // Call *(*(SP + base) + offset)
+ LoadFromOffset(kLoadWord, scratch.AsGpuRegister(),
+ SP, base.Int32Value());
+ LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(),
+ scratch.AsGpuRegister(), offset.Int32Value());
+ Jalr(scratch.AsGpuRegister());
+ // TODO: place reference map on call
+}
+
+void Mips64Assembler::CallFromThread64(ThreadOffset<8> /*offset*/, ManagedRegister /*mscratch*/) {
+ UNIMPLEMENTED(FATAL) << "no mips64 implementation";
+}
+
+void Mips64Assembler::GetCurrentThread(ManagedRegister tr) {
+ Move(tr.AsMips64().AsGpuRegister(), S1);
+}
+
+void Mips64Assembler::GetCurrentThread(FrameOffset offset,
+ ManagedRegister /*mscratch*/) {
+ StoreToOffset(kStoreDoubleword, S1, SP, offset.Int32Value());
+}
+
+void Mips64Assembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) {
+ Mips64ManagedRegister scratch = mscratch.AsMips64();
+ Mips64ExceptionSlowPath* slow = new Mips64ExceptionSlowPath(scratch, stack_adjust);
+ buffer_.EnqueueSlowPath(slow);
+ LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(),
+ S1, Thread::ExceptionOffset<8>().Int32Value());
+ EmitBranch(scratch.AsGpuRegister(), ZERO, slow->Entry(), false);
+}
+
+void Mips64ExceptionSlowPath::Emit(Assembler* sasm) {
+ Mips64Assembler* sp_asm = down_cast<Mips64Assembler*>(sasm);
+#define __ sp_asm->
+ __ Bind(&entry_, false);
+ if (stack_adjust_ != 0) { // Fix up the frame.
+ __ DecreaseFrameSize(stack_adjust_);
+ }
+ // Pass exception object as argument
+ // Don't care about preserving A0 as this call won't return
+ __ Move(A0, scratch_.AsGpuRegister());
+ // Set up call to Thread::Current()->pDeliverException
+ __ LoadFromOffset(kLoadDoubleword, T9, S1,
+ QUICK_ENTRYPOINT_OFFSET(4, pDeliverException).Int32Value());
+ __ Jr(T9);
+ // Call never returns
+ __ Break();
+#undef __
+}
+
+} // namespace mips64
+} // namespace art
diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h
new file mode 100644
index 0000000..36e74d7
--- /dev/null
+++ b/compiler/utils/mips64/assembler_mips64.h
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_UTILS_MIPS64_ASSEMBLER_MIPS64_H_
+#define ART_COMPILER_UTILS_MIPS64_ASSEMBLER_MIPS64_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "constants_mips64.h"
+#include "globals.h"
+#include "managed_register_mips64.h"
+#include "utils/assembler.h"
+#include "offsets.h"
+#include "utils.h"
+
+namespace art {
+namespace mips64 {
+
+enum LoadOperandType {
+ kLoadSignedByte,
+ kLoadUnsignedByte,
+ kLoadSignedHalfword,
+ kLoadUnsignedHalfword,
+ kLoadWord,
+ kLoadDoubleword
+};
+
+enum StoreOperandType {
+ kStoreByte,
+ kStoreHalfword,
+ kStoreWord,
+ kStoreDoubleword
+};
+
+class Mips64Assembler FINAL : public Assembler {
+ public:
+ Mips64Assembler() {}
+ virtual ~Mips64Assembler() {}
+
+ // Emit Machine Instructions.
+ void Add(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+ void Addi(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void Addu(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+ void Addiu(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void Daddiu(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void Sub(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+ void Subu(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+ void Mult(GpuRegister rs, GpuRegister rt);
+ void Multu(GpuRegister rs, GpuRegister rt);
+ void Div(GpuRegister rs, GpuRegister rt);
+ void Divu(GpuRegister rs, GpuRegister rt);
+
+ void And(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+ void Andi(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void Or(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+ void Ori(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void Xor(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+ void Xori(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void Nor(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+
+ void Sll(GpuRegister rd, GpuRegister rs, int shamt);
+ void Srl(GpuRegister rd, GpuRegister rs, int shamt);
+ void Sra(GpuRegister rd, GpuRegister rs, int shamt);
+ void Sllv(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+ void Srlv(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+ void Srav(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+
+ void Lb(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void Lh(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void Lw(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void Ld(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void Lbu(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void Lhu(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void Lui(GpuRegister rt, uint16_t imm16);
+ void Mfhi(GpuRegister rd);
+ void Mflo(GpuRegister rd);
+
+ void Sb(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void Sh(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void Sw(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void Sd(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+
+ void Slt(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+ void Sltu(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+ void Slti(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void Sltiu(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+
+ void Beq(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void Bne(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+ void J(uint32_t address);
+ void Jal(uint32_t address);
+ void Jr(GpuRegister rs);
+ void Jalr(GpuRegister rs);
+
+ void AddS(FpuRegister fd, FpuRegister fs, FpuRegister ft);
+ void SubS(FpuRegister fd, FpuRegister fs, FpuRegister ft);
+ void MulS(FpuRegister fd, FpuRegister fs, FpuRegister ft);
+ void DivS(FpuRegister fd, FpuRegister fs, FpuRegister ft);
+ void AddD(FpuRegister fd, FpuRegister fs, FpuRegister ft);
+ void SubD(FpuRegister fd, FpuRegister fs, FpuRegister ft);
+ void MulD(FpuRegister fd, FpuRegister fs, FpuRegister ft);
+ void DivD(FpuRegister fd, FpuRegister fs, FpuRegister ft);
+ void MovS(FpuRegister fd, FpuRegister fs);
+ void MovD(FpuRegister fd, FpuRegister fs);
+
+ void Mfc1(GpuRegister rt, FpuRegister fs);
+ void Mtc1(FpuRegister ft, GpuRegister rs);
+ void Lwc1(FpuRegister ft, GpuRegister rs, uint16_t imm16);
+ void Ldc1(FpuRegister ft, GpuRegister rs, uint16_t imm16);
+ void Swc1(FpuRegister ft, GpuRegister rs, uint16_t imm16);
+ void Sdc1(FpuRegister ft, GpuRegister rs, uint16_t imm16);
+
+ void Break();
+ void Nop();
+ void Move(GpuRegister rt, GpuRegister rs);
+ void Clear(GpuRegister rt);
+ void Not(GpuRegister rt, GpuRegister rs);
+ void Mul(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+ void Div(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+ void Rem(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+
+ void AddConstant64(GpuRegister rt, GpuRegister rs, int32_t value);
+ void LoadImmediate64(GpuRegister rt, int32_t value);
+
+ void EmitLoad(ManagedRegister m_dst, GpuRegister src_register, int32_t src_offset, size_t size);
+ void LoadFromOffset(LoadOperandType type, GpuRegister reg, GpuRegister base, int32_t offset);
+ void LoadFpuFromOffset(LoadOperandType type, FpuRegister reg, GpuRegister base, int32_t offset);
+ void StoreToOffset(StoreOperandType type, GpuRegister reg, GpuRegister base, int32_t offset);
+ void StoreFpuToOffset(StoreOperandType type, FpuRegister reg, GpuRegister base, int32_t offset);
+
+ // Emit data (e.g. encoded instruction or immediate) to the instruction stream.
+ void Emit(int32_t value);
+ void EmitBranch(GpuRegister rt, GpuRegister rs, Label* label, bool equal);
+ void EmitJump(Label* label, bool link);
+ void Bind(Label* label, bool is_jump);
+
+ //
+ // Overridden common assembler high-level functionality
+ //
+
+ // Emit code that will create an activation on the stack
+ void BuildFrame(size_t frame_size, ManagedRegister method_reg,
+ const std::vector<ManagedRegister>& callee_save_regs,
+ const ManagedRegisterEntrySpills& entry_spills) OVERRIDE;
+
+ // Emit code that will remove an activation from the stack
+ void RemoveFrame(size_t frame_size,
+ const std::vector<ManagedRegister>& callee_save_regs) OVERRIDE;
+
+ void IncreaseFrameSize(size_t adjust) OVERRIDE;
+ void DecreaseFrameSize(size_t adjust) OVERRIDE;
+
+ // Store routines
+ void Store(FrameOffset offs, ManagedRegister msrc, size_t size) OVERRIDE;
+ void StoreRef(FrameOffset dest, ManagedRegister msrc) OVERRIDE;
+ void StoreRawPtr(FrameOffset dest, ManagedRegister msrc) OVERRIDE;
+
+ void StoreImmediateToFrame(FrameOffset dest, uint32_t imm, ManagedRegister mscratch) OVERRIDE;
+
+ void StoreImmediateToThread64(ThreadOffset<8> dest, uint32_t imm,
+ ManagedRegister mscratch) OVERRIDE;
+
+ void StoreStackOffsetToThread64(ThreadOffset<8> thr_offs, FrameOffset fr_offs,
+ ManagedRegister mscratch) OVERRIDE;
+
+ void StoreStackPointerToThread64(ThreadOffset<8> thr_offs) OVERRIDE;
+
+ void StoreSpanning(FrameOffset dest, ManagedRegister msrc, FrameOffset in_off,
+ ManagedRegister mscratch) OVERRIDE;
+
+ // Load routines
+ void Load(ManagedRegister mdest, FrameOffset src, size_t size) OVERRIDE;
+
+ void LoadFromThread64(ManagedRegister mdest, ThreadOffset<8> src, size_t size) OVERRIDE;
+
+ void LoadRef(ManagedRegister dest, FrameOffset src) OVERRIDE;
+
+ void LoadRef(ManagedRegister mdest, ManagedRegister base, MemberOffset offs) OVERRIDE;
+
+ void LoadRawPtr(ManagedRegister mdest, ManagedRegister base, Offset offs) OVERRIDE;
+
+ void LoadRawPtrFromThread64(ManagedRegister mdest, ThreadOffset<8> offs) OVERRIDE;
+
+ // Copying routines
+ void Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) OVERRIDE;
+
+ void CopyRawPtrFromThread64(FrameOffset fr_offs, ThreadOffset<8> thr_offs,
+ ManagedRegister mscratch) OVERRIDE;
+
+ void CopyRawPtrToThread64(ThreadOffset<8> thr_offs, FrameOffset fr_offs,
+ ManagedRegister mscratch) OVERRIDE;
+
+ void CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) OVERRIDE;
+
+ void Copy(FrameOffset dest, FrameOffset src, ManagedRegister mscratch, size_t size) OVERRIDE;
+
+ void Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset, ManagedRegister mscratch,
+ size_t size) OVERRIDE;
+
+ void Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src,
+ ManagedRegister mscratch, size_t size) OVERRIDE;
+
+ void Copy(FrameOffset dest, FrameOffset src_base, Offset src_offset, ManagedRegister mscratch,
+ size_t size) OVERRIDE;
+
+ void Copy(ManagedRegister dest, Offset dest_offset, ManagedRegister src, Offset src_offset,
+ ManagedRegister mscratch, size_t size) OVERRIDE;
+
+ void Copy(FrameOffset dest, Offset dest_offset, FrameOffset src, Offset src_offset,
+ ManagedRegister mscratch, size_t size) OVERRIDE;
+
+ void MemoryBarrier(ManagedRegister) OVERRIDE;
+
+ // Sign extension
+ void SignExtend(ManagedRegister mreg, size_t size) OVERRIDE;
+
+ // Zero extension
+ void ZeroExtend(ManagedRegister mreg, size_t size) OVERRIDE;
+
+ // Exploit fast access in managed code to Thread::Current()
+ void GetCurrentThread(ManagedRegister tr) OVERRIDE;
+ void GetCurrentThread(FrameOffset dest_offset, ManagedRegister mscratch) OVERRIDE;
+
+ // Set up out_reg to hold a Object** into the handle scope, or to be NULL if the
+ // value is null and null_allowed. in_reg holds a possibly stale reference
+ // that can be used to avoid loading the handle scope entry to see if the value is
+ // NULL.
+ void CreateHandleScopeEntry(ManagedRegister out_reg, FrameOffset handlescope_offset,
+ ManagedRegister in_reg, bool null_allowed) OVERRIDE;
+
+ // Set up out_off to hold a Object** into the handle scope, or to be NULL if the
+ // value is null and null_allowed.
+ void CreateHandleScopeEntry(FrameOffset out_off, FrameOffset handlescope_offset, ManagedRegister
+ mscratch, bool null_allowed) OVERRIDE;
+
+ // src holds a handle scope entry (Object**) load this into dst
+ void LoadReferenceFromHandleScope(ManagedRegister dst, ManagedRegister src) OVERRIDE;
+
+ // Heap::VerifyObject on src. In some cases (such as a reference to this) we
+ // know that src may not be null.
+ void VerifyObject(ManagedRegister src, bool could_be_null) OVERRIDE;
+ void VerifyObject(FrameOffset src, bool could_be_null) OVERRIDE;
+
+ // Call to address held at [base+offset]
+ void Call(ManagedRegister base, Offset offset, ManagedRegister mscratch) OVERRIDE;
+ void Call(FrameOffset base, Offset offset, ManagedRegister mscratch) OVERRIDE;
+ void CallFromThread64(ThreadOffset<8> offset, ManagedRegister mscratch) OVERRIDE;
+
+ // Generate code to check if Thread::Current()->exception_ is non-null
+ // and branch to a ExceptionSlowPath if it is.
+ void ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) OVERRIDE;
+
+ private:
+ void EmitR(int opcode, GpuRegister rs, GpuRegister rt, GpuRegister rd, int shamt, int funct);
+ void EmitI(int opcode, GpuRegister rs, GpuRegister rt, uint16_t imm);
+ void EmitJ(int opcode, int address);
+ void EmitFR(int opcode, int fmt, FpuRegister ft, FpuRegister fs, FpuRegister fd, int funct);
+ void EmitFI(int opcode, int fmt, FpuRegister rt, uint16_t imm);
+
+ int32_t EncodeBranchOffset(int offset, int32_t inst, bool is_jump);
+ int DecodeBranchOffset(int32_t inst, bool is_jump);
+
+ DISALLOW_COPY_AND_ASSIGN(Mips64Assembler);
+};
+
+// Slowpath entered when Thread::Current()->_exception is non-null
+class Mips64ExceptionSlowPath FINAL : public SlowPath {
+ public:
+ explicit Mips64ExceptionSlowPath(Mips64ManagedRegister scratch, size_t stack_adjust)
+ : scratch_(scratch), stack_adjust_(stack_adjust) {}
+ virtual void Emit(Assembler *sp_asm) OVERRIDE;
+ private:
+ const Mips64ManagedRegister scratch_;
+ const size_t stack_adjust_;
+};
+
+} // namespace mips64
+} // namespace art
+
+#endif // ART_COMPILER_UTILS_MIPS64_ASSEMBLER_MIPS64_H_
diff --git a/compiler/utils/mips64/constants_mips64.h b/compiler/utils/mips64/constants_mips64.h
new file mode 100644
index 0000000..8b7697c
--- /dev/null
+++ b/compiler/utils/mips64/constants_mips64.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_UTILS_MIPS64_CONSTANTS_MIPS64_H_
+#define ART_COMPILER_UTILS_MIPS64_CONSTANTS_MIPS64_H_
+
+#include <iosfwd>
+
+#include "arch/mips64/registers_mips64.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "globals.h"
+
+namespace art {
+namespace mips64 {
+
+// Constants used for the decoding or encoding of the individual fields of instructions.
+enum InstructionFields {
+ kOpcodeShift = 26,
+ kOpcodeBits = 6,
+ kRsShift = 21,
+ kRsBits = 5,
+ kRtShift = 16,
+ kRtBits = 5,
+ kRdShift = 11,
+ kRdBits = 5,
+ kShamtShift = 6,
+ kShamtBits = 5,
+ kFunctShift = 0,
+ kFunctBits = 6,
+
+ kFmtShift = 21,
+ kFmtBits = 5,
+ kFtShift = 16,
+ kFtBits = 5,
+ kFsShift = 11,
+ kFsBits = 5,
+ kFdShift = 6,
+ kFdBits = 5,
+
+ kBranchOffsetMask = 0x0000ffff,
+ kJumpOffsetMask = 0x03ffffff,
+};
+
+enum ScaleFactor {
+ TIMES_1 = 0,
+ TIMES_2 = 1,
+ TIMES_4 = 2,
+ TIMES_8 = 3
+};
+
+class Instr {
+ public:
+ static const uint32_t kBreakPointInstruction = 0x0000000D;
+
+ bool IsBreakPoint() {
+ return ((*reinterpret_cast<const uint32_t*>(this)) & 0xFC0000CF) == kBreakPointInstruction;
+ }
+
+ // Instructions are read out of a code stream. The only way to get a
+ // reference to an instruction is to convert a pointer. There is no way
+ // to allocate or create instances of class Instr.
+ // Use the At(pc) function to create references to Instr.
+ static Instr* At(uintptr_t pc) { return reinterpret_cast<Instr*>(pc); }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Instr);
+};
+
+} // namespace mips64
+} // namespace art
+
+#endif // ART_COMPILER_UTILS_MIPS64_CONSTANTS_MIPS64_H_
diff --git a/compiler/utils/mips64/managed_register_mips64.cc b/compiler/utils/mips64/managed_register_mips64.cc
new file mode 100644
index 0000000..dea396e
--- /dev/null
+++ b/compiler/utils/mips64/managed_register_mips64.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "managed_register_mips64.h"
+
+#include "globals.h"
+
+namespace art {
+namespace mips64 {
+
+bool Mips64ManagedRegister::Overlaps(const Mips64ManagedRegister& other) const {
+ if (IsNoRegister() || other.IsNoRegister()) return false;
+ CHECK(IsValidManagedRegister());
+ CHECK(other.IsValidManagedRegister());
+ if (Equals(other)) return true;
+ return false;
+}
+
+void Mips64ManagedRegister::Print(std::ostream& os) const {
+ if (!IsValidManagedRegister()) {
+ os << "No Register";
+ } else if (IsGpuRegister()) {
+ os << "GPU: " << static_cast<int>(AsGpuRegister());
+ } else if (IsFpuRegister()) {
+ os << "FpuRegister: " << static_cast<int>(AsFpuRegister());
+ } else {
+ os << "??: " << RegId();
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, const Mips64ManagedRegister& reg) {
+ reg.Print(os);
+ return os;
+}
+
+} // namespace mips64
+} // namespace art
diff --git a/compiler/utils/mips64/managed_register_mips64.h b/compiler/utils/mips64/managed_register_mips64.h
new file mode 100644
index 0000000..924a928
--- /dev/null
+++ b/compiler/utils/mips64/managed_register_mips64.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_UTILS_MIPS64_MANAGED_REGISTER_MIPS64_H_
+#define ART_COMPILER_UTILS_MIPS64_MANAGED_REGISTER_MIPS64_H_
+
+#include "constants_mips64.h"
+#include "utils/managed_register.h"
+
+namespace art {
+namespace mips64 {
+
+const int kNumberOfGpuRegIds = kNumberOfGpuRegisters;
+const int kNumberOfGpuAllocIds = kNumberOfGpuRegisters;
+
+const int kNumberOfFpuRegIds = kNumberOfFpuRegisters;
+const int kNumberOfFpuAllocIds = kNumberOfFpuRegisters;
+
+const int kNumberOfRegIds = kNumberOfGpuRegIds + kNumberOfFpuRegIds;
+const int kNumberOfAllocIds = kNumberOfGpuAllocIds + kNumberOfFpuAllocIds;
+
+// An instance of class 'ManagedRegister' represents a single GPU register (enum
+// Register) or a double precision FP register (enum FpuRegister)
+// 'ManagedRegister::NoRegister()' provides an invalid register.
+// There is a one-to-one mapping between ManagedRegister and register id.
+class Mips64ManagedRegister : public ManagedRegister {
+ public:
+ GpuRegister AsGpuRegister() const {
+ CHECK(IsGpuRegister());
+ return static_cast<GpuRegister>(id_);
+ }
+
+ FpuRegister AsFpuRegister() const {
+ CHECK(IsFpuRegister());
+ return static_cast<FpuRegister>(id_ - kNumberOfGpuRegIds);
+ }
+
+ bool IsGpuRegister() const {
+ CHECK(IsValidManagedRegister());
+ return (0 <= id_) && (id_ < kNumberOfGpuRegIds);
+ }
+
+ bool IsFpuRegister() const {
+ CHECK(IsValidManagedRegister());
+ const int test = id_ - kNumberOfGpuRegIds;
+ return (0 <= test) && (test < kNumberOfFpuRegIds);
+ }
+
+ void Print(std::ostream& os) const;
+
+ // Returns true if the two managed-registers ('this' and 'other') overlap.
+ // Either managed-register may be the NoRegister. If both are the NoRegister
+ // then false is returned.
+ bool Overlaps(const Mips64ManagedRegister& other) const;
+
+ static Mips64ManagedRegister FromGpuRegister(GpuRegister r) {
+ CHECK_NE(r, kNoGpuRegister);
+ return FromRegId(r);
+ }
+
+ static Mips64ManagedRegister FromFpuRegister(FpuRegister r) {
+ CHECK_NE(r, kNoFpuRegister);
+ return FromRegId(r + kNumberOfGpuRegIds);
+ }
+
+ private:
+ bool IsValidManagedRegister() const {
+ return (0 <= id_) && (id_ < kNumberOfRegIds);
+ }
+
+ int RegId() const {
+ CHECK(!IsNoRegister());
+ return id_;
+ }
+
+ int AllocId() const {
+ CHECK(IsValidManagedRegister());
+ CHECK_LT(id_, kNumberOfAllocIds);
+ return id_;
+ }
+
+ int AllocIdLow() const;
+ int AllocIdHigh() const;
+
+ friend class ManagedRegister;
+
+ explicit Mips64ManagedRegister(int reg_id) : ManagedRegister(reg_id) {}
+
+ static Mips64ManagedRegister FromRegId(int reg_id) {
+ Mips64ManagedRegister reg(reg_id);
+ CHECK(reg.IsValidManagedRegister());
+ return reg;
+ }
+};
+
+std::ostream& operator<<(std::ostream& os, const Mips64ManagedRegister& reg);
+
+} // namespace mips64
+
+inline mips64::Mips64ManagedRegister ManagedRegister::AsMips64() const {
+ mips64::Mips64ManagedRegister reg(id_);
+ CHECK(reg.IsNoRegister() || reg.IsValidManagedRegister());
+ return reg;
+}
+
+} // namespace art
+
+#endif // ART_COMPILER_UTILS_MIPS64_MANAGED_REGISTER_MIPS64_H_
diff --git a/compiler/utils/scoped_hashtable.h b/compiler/utils/scoped_hashtable.h
deleted file mode 100644
index bf8dd1f..0000000
--- a/compiler/utils/scoped_hashtable.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
-
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stddef.h>
-#include <map>
-#include <list>
-
-#ifndef ART_COMPILER_UTILS_SCOPED_HASHTABLE_H_
-#define ART_COMPILER_UTILS_SCOPED_HASHTABLE_H_
-
-namespace utils {
-template <typename K, typename V>
-class ScopedHashtable {
- public:
- explicit ScopedHashtable():scopes() {
- }
-
- void OpenScope() {
- scopes.push_front(std::map<K, V>());
- }
-
- // Lookups entry K starting from the current (topmost) scope
- // and returns its value if found or NULL.
- V Lookup(K k) const {
- for (typename std::list<std::map<K, V>>::const_iterator scopes_it = scopes.begin();
- scopes_it != scopes.end(); scopes_it++) {
- typename std::map<K, V>::const_iterator result_it = (*scopes_it).find(k);
- if (result_it != (*scopes_it).end()) {
- return (*result_it).second;
- }
- }
- return NULL;
- }
-
- // Adds a new entry in the current (topmost) scope.
- void Add(K k, V v) {
- scopes.front().erase(k);
- scopes.front().insert(std::pair< K, V >(k, v));
- }
-
- // Removes the topmost scope.
- bool CloseScope() {
- // Added check to uniformly handle undefined behavior
- // when removing scope and the list of scopes is empty.
- if (scopes.size() > 0) {
- scopes.pop_front();
- return true;
- }
- return false;
- }
-
- private:
- std::list<std::map<K, V>> scopes;
-};
-} // namespace utils
-
-#endif // ART_COMPILER_UTILS_SCOPED_HASHTABLE_H_
diff --git a/compiler/utils/scoped_hashtable_test.cc b/compiler/utils/scoped_hashtable_test.cc
deleted file mode 100644
index 1c843eb..0000000
--- a/compiler/utils/scoped_hashtable_test.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "scoped_hashtable.h"
-
-#include "common_runtime_test.h"
-
-using utils::ScopedHashtable;
-
-namespace art {
-
-class Value {
- public:
- explicit Value(int v):value_(v) {}
- int value_;
-};
-
-class ScopedHashtableTest : public testing::Test {};
-
-TEST_F(ScopedHashtableTest, Basics) {
- ScopedHashtable<int, Value*> sht;
- // Check table is empty when no scope is open.
- EXPECT_TRUE(NULL == sht.Lookup(1));
-
- // Check table is empty when scope open.
- sht.OpenScope();
- EXPECT_TRUE(NULL == sht.Lookup(1));
- // Check table is empty after closing scope.
- EXPECT_EQ(sht.CloseScope(), true);
- // Check closing scope on empty table is no-op.
- EXPECT_EQ(sht.CloseScope(), false);
- // Check that find in current scope works.
- sht.OpenScope();
- sht.Add(1, new Value(1));
- EXPECT_EQ(sht.Lookup(1)->value_, 1);
- // Check that updating values in current scope works.
- sht.Add(1, new Value(2));
- EXPECT_EQ(sht.Lookup(1)->value_, 2);
- // Check that find works in previous scope.
- sht.OpenScope();
- EXPECT_EQ(sht.Lookup(1)->value_, 2);
- // Check that shadowing scopes works.
- sht.Add(1, new Value(3));
- EXPECT_EQ(sht.Lookup(1)->value_, 3);
- // Check that having multiple keys work correctly.
- sht.Add(2, new Value(4));
- EXPECT_EQ(sht.Lookup(1)->value_, 3);
- EXPECT_EQ(sht.Lookup(2)->value_, 4);
- // Check that scope removal works corectly.
- sht.CloseScope();
- EXPECT_EQ(sht.Lookup(1)->value_, 2);
- EXPECT_TRUE(NULL == sht.Lookup(2));
-}
-
-} // namespace art
diff --git a/compiler/utils/swap_space.cc b/compiler/utils/swap_space.cc
new file mode 100644
index 0000000..325ee4f
--- /dev/null
+++ b/compiler/utils/swap_space.cc
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "swap_space.h"
+
+#include <algorithm>
+#include <numeric>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "thread-inl.h"
+
+namespace art {
+
+// The chunk size by which the swap file is increased and mapped.
+static constexpr size_t kMininumMapSize = 16 * MB;
+
+static constexpr bool kCheckFreeMaps = false;
+
+template <typename FreeBySizeSet>
+static void DumpFreeMap(const FreeBySizeSet& free_by_size) {
+ size_t last_size = static_cast<size_t>(-1);
+ for (const auto& entry : free_by_size) {
+ if (last_size != entry.first) {
+ last_size = entry.first;
+ LOG(INFO) << "Size " << last_size;
+ }
+ LOG(INFO) << " 0x" << std::hex << entry.second->Start()
+ << " size=" << std::dec << entry.second->size;
+ }
+}
+
+template <typename FreeByStartSet, typename FreeBySizeSet>
+static void RemoveChunk(FreeByStartSet* free_by_start,
+ FreeBySizeSet* free_by_size,
+ typename FreeBySizeSet::const_iterator free_by_size_pos) {
+ auto free_by_start_pos = free_by_size_pos->second;
+ free_by_size->erase(free_by_size_pos);
+ free_by_start->erase(free_by_start_pos);
+}
+
+template <typename FreeByStartSet, typename FreeBySizeSet>
+static void InsertChunk(FreeByStartSet* free_by_start,
+ FreeBySizeSet* free_by_size,
+ const SpaceChunk& chunk) {
+ DCHECK_NE(chunk.size, 0u);
+ auto insert_result = free_by_start->insert(chunk);
+ DCHECK(insert_result.second);
+ free_by_size->emplace(chunk.size, insert_result.first);
+}
+
+SwapSpace::SwapSpace(int fd, size_t initial_size)
+ : fd_(fd),
+ size_(0),
+ lock_("SwapSpace lock", static_cast<LockLevel>(LockLevel::kDefaultMutexLevel - 1)) {
+ // Assume that the file is unlinked.
+
+ InsertChunk(&free_by_start_, &free_by_size_, NewFileChunk(initial_size));
+}
+
+SwapSpace::~SwapSpace() {
+ // All arenas are backed by the same file. Just close the descriptor.
+ close(fd_);
+}
+
+template <typename FreeByStartSet, typename FreeBySizeSet>
+static size_t CollectFree(const FreeByStartSet& free_by_start, const FreeBySizeSet& free_by_size) {
+ if (free_by_start.size() != free_by_size.size()) {
+ LOG(FATAL) << "Size: " << free_by_start.size() << " vs " << free_by_size.size();
+ }
+
+ // Calculate over free_by_size.
+ size_t sum1 = 0;
+ for (const auto& entry : free_by_size) {
+ sum1 += entry.second->size;
+ }
+
+ // Calculate over free_by_start.
+ size_t sum2 = 0;
+ for (const auto& entry : free_by_start) {
+ sum2 += entry.size;
+ }
+
+ if (sum1 != sum2) {
+ LOG(FATAL) << "Sum: " << sum1 << " vs " << sum2;
+ }
+ return sum1;
+}
+
+void* SwapSpace::Alloc(size_t size) {
+ MutexLock lock(Thread::Current(), lock_);
+ size = RoundUp(size, 8U);
+
+ // Check the free list for something that fits.
+ // TODO: Smarter implementation. Global biggest chunk, ...
+ SpaceChunk old_chunk;
+ auto it = free_by_start_.empty()
+ ? free_by_size_.end()
+ : free_by_size_.lower_bound(FreeBySizeEntry { size, free_by_start_.begin() });
+ if (it != free_by_size_.end()) {
+ old_chunk = *it->second;
+ RemoveChunk(&free_by_start_, &free_by_size_, it);
+ } else {
+ // Not a big enough free chunk, need to increase file size.
+ old_chunk = NewFileChunk(size);
+ }
+
+ void* ret = old_chunk.ptr;
+
+ if (old_chunk.size != size) {
+ // Insert the remainder.
+ SpaceChunk new_chunk = { old_chunk.ptr + size, old_chunk.size - size };
+ InsertChunk(&free_by_start_, &free_by_size_, new_chunk);
+ }
+
+ return ret;
+}
+
+SpaceChunk SwapSpace::NewFileChunk(size_t min_size) {
+#if !defined(__APPLE__)
+ size_t next_part = std::max(RoundUp(min_size, kPageSize), RoundUp(kMininumMapSize, kPageSize));
+ int result = TEMP_FAILURE_RETRY(ftruncate64(fd_, size_ + next_part));
+ if (result != 0) {
+ PLOG(FATAL) << "Unable to increase swap file.";
+ }
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(
+ mmap(nullptr, next_part, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, size_));
+ if (ptr == MAP_FAILED) {
+ LOG(ERROR) << "Unable to mmap new swap file chunk.";
+ LOG(ERROR) << "Current size: " << size_ << " requested: " << next_part << "/" << min_size;
+ LOG(ERROR) << "Free list:";
+ MutexLock lock(Thread::Current(), lock_);
+ DumpFreeMap(free_by_size_);
+ LOG(ERROR) << "In free list: " << CollectFree(free_by_start_, free_by_size_);
+ LOG(FATAL) << "Aborting...";
+ }
+ size_ += next_part;
+ SpaceChunk new_chunk = {ptr, next_part};
+ maps_.push_back(new_chunk);
+ return new_chunk;
+#else
+ UNUSED(min_size, kMininumMapSize);
+ LOG(FATAL) << "No swap file support on the Mac.";
+ UNREACHABLE();
+#endif
+}
+
+// TODO: Full coalescing.
+void SwapSpace::Free(void* ptrV, size_t size) {
+ MutexLock lock(Thread::Current(), lock_);
+ size = RoundUp(size, 8U);
+
+ size_t free_before = 0;
+ if (kCheckFreeMaps) {
+ free_before = CollectFree(free_by_start_, free_by_size_);
+ }
+
+ SpaceChunk chunk = { reinterpret_cast<uint8_t*>(ptrV), size };
+ auto it = free_by_start_.lower_bound(chunk);
+ if (it != free_by_start_.begin()) {
+ auto prev = it;
+ --prev;
+ CHECK_LE(prev->End(), chunk.Start());
+ if (prev->End() == chunk.Start()) {
+ // Merge *prev with this chunk.
+ chunk.size += prev->size;
+ chunk.ptr -= prev->size;
+ auto erase_pos = free_by_size_.find(FreeBySizeEntry { prev->size, prev });
+ DCHECK(erase_pos != free_by_size_.end());
+ RemoveChunk(&free_by_start_, &free_by_size_, erase_pos);
+ // "prev" is invalidated but "it" remains valid.
+ }
+ }
+ if (it != free_by_start_.end()) {
+ CHECK_LE(chunk.End(), it->Start());
+ if (chunk.End() == it->Start()) {
+ // Merge *it with this chunk.
+ chunk.size += it->size;
+ auto erase_pos = free_by_size_.find(FreeBySizeEntry { it->size, it });
+ DCHECK(erase_pos != free_by_size_.end());
+ RemoveChunk(&free_by_start_, &free_by_size_, erase_pos);
+ // "it" is invalidated but we don't need it anymore.
+ }
+ }
+ InsertChunk(&free_by_start_, &free_by_size_, chunk);
+
+ if (kCheckFreeMaps) {
+ size_t free_after = CollectFree(free_by_start_, free_by_size_);
+
+ if (free_after != free_before + size) {
+ DumpFreeMap(free_by_size_);
+ CHECK_EQ(free_after, free_before + size) << "Should be " << size << " difference from " << free_before;
+ }
+ }
+}
+
+} // namespace art
diff --git a/compiler/utils/swap_space.h b/compiler/utils/swap_space.h
new file mode 100644
index 0000000..2d0d77a
--- /dev/null
+++ b/compiler/utils/swap_space.h
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_UTILS_SWAP_SPACE_H_
+#define ART_COMPILER_UTILS_SWAP_SPACE_H_
+
+#include <cstdlib>
+#include <list>
+#include <set>
+#include <stdint.h>
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "mem_map.h"
+#include "utils.h"
+#include "utils/debug_stack.h"
+
+namespace art {
+
+// Chunk of space.
+struct SpaceChunk {
+ uint8_t* ptr;
+ size_t size;
+
+ uintptr_t Start() const {
+ return reinterpret_cast<uintptr_t>(ptr);
+ }
+ uintptr_t End() const {
+ return reinterpret_cast<uintptr_t>(ptr) + size;
+ }
+};
+
+inline bool operator==(const SpaceChunk& lhs, const SpaceChunk& rhs) {
+ return (lhs.size == rhs.size) && (lhs.ptr == rhs.ptr);
+}
+
+class SortChunkByPtr {
+ public:
+ bool operator()(const SpaceChunk& a, const SpaceChunk& b) const {
+ return reinterpret_cast<uintptr_t>(a.ptr) < reinterpret_cast<uintptr_t>(b.ptr);
+ }
+};
+
+// An arena pool that creates arenas backed by an mmaped file.
+class SwapSpace {
+ public:
+ SwapSpace(int fd, size_t initial_size);
+ ~SwapSpace();
+ void* Alloc(size_t size) LOCKS_EXCLUDED(lock_);
+ void Free(void* ptr, size_t size) LOCKS_EXCLUDED(lock_);
+
+ size_t GetSize() {
+ return size_;
+ }
+
+ private:
+ SpaceChunk NewFileChunk(size_t min_size);
+
+ int fd_;
+ size_t size_;
+ std::list<SpaceChunk> maps_;
+
+ // NOTE: Boost.Bimap would be useful for the two following members.
+
+ // Map start of a free chunk to its size.
+ typedef std::set<SpaceChunk, SortChunkByPtr> FreeByStartSet;
+ FreeByStartSet free_by_start_ GUARDED_BY(lock_);
+
+ // Map size to an iterator to free_by_start_'s entry.
+ typedef std::pair<size_t, FreeByStartSet::const_iterator> FreeBySizeEntry;
+ struct FreeBySizeComparator {
+ bool operator()(const FreeBySizeEntry& lhs, const FreeBySizeEntry& rhs) {
+ if (lhs.first != rhs.first) {
+ return lhs.first < rhs.first;
+ } else {
+ return lhs.second->Start() < rhs.second->Start();
+ }
+ }
+ };
+ typedef std::set<FreeBySizeEntry, FreeBySizeComparator> FreeBySizeSet;
+ FreeBySizeSet free_by_size_ GUARDED_BY(lock_);
+
+ mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ DISALLOW_COPY_AND_ASSIGN(SwapSpace);
+};
+
+template <typename T> class SwapAllocator;
+
+template <>
+class SwapAllocator<void> {
+ public:
+ typedef void value_type;
+ typedef void* pointer;
+ typedef const void* const_pointer;
+
+ template <typename U>
+ struct rebind {
+ typedef SwapAllocator<U> other;
+ };
+
+ explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {}
+
+ template <typename U>
+ SwapAllocator(const SwapAllocator<U>& other) : swap_space_(other.swap_space_) {}
+
+ SwapAllocator(const SwapAllocator& other) = default;
+ SwapAllocator& operator=(const SwapAllocator& other) = default;
+ ~SwapAllocator() = default;
+
+ private:
+ SwapSpace* swap_space_;
+
+ template <typename U>
+ friend class SwapAllocator;
+};
+
+template <typename T>
+class SwapAllocator {
+ public:
+ typedef T value_type;
+ typedef T* pointer;
+ typedef T& reference;
+ typedef const T* const_pointer;
+ typedef const T& const_reference;
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+
+ template <typename U>
+ struct rebind {
+ typedef SwapAllocator<U> other;
+ };
+
+ explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {}
+
+ template <typename U>
+ SwapAllocator(const SwapAllocator<U>& other) : swap_space_(other.swap_space_) {}
+
+ SwapAllocator(const SwapAllocator& other) = default;
+ SwapAllocator& operator=(const SwapAllocator& other) = default;
+ ~SwapAllocator() = default;
+
+ size_type max_size() const {
+ return static_cast<size_type>(-1) / sizeof(T);
+ }
+
+ pointer address(reference x) const { return &x; }
+ const_pointer address(const_reference x) const { return &x; }
+
+ pointer allocate(size_type n, SwapAllocator<void>::pointer hint ATTRIBUTE_UNUSED = nullptr) {
+ DCHECK_LE(n, max_size());
+ if (swap_space_ == nullptr) {
+ return reinterpret_cast<T*>(malloc(n * sizeof(T)));
+ } else {
+ return reinterpret_cast<T*>(swap_space_->Alloc(n * sizeof(T)));
+ }
+ }
+ void deallocate(pointer p, size_type n) {
+ if (swap_space_ == nullptr) {
+ free(p);
+ } else {
+ swap_space_->Free(p, n * sizeof(T));
+ }
+ }
+
+ void construct(pointer p, const_reference val) {
+ new (static_cast<void*>(p)) value_type(val);
+ }
+ template <class U, class... Args>
+ void construct(U* p, Args&&... args) {
+ ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
+ }
+ void destroy(pointer p) {
+ p->~value_type();
+ }
+
+ inline bool operator==(SwapAllocator const& other) {
+ return swap_space_ == other.swap_space_;
+ }
+ inline bool operator!=(SwapAllocator const& other) {
+ return !operator==(other);
+ }
+
+ private:
+ SwapSpace* swap_space_;
+
+ template <typename U>
+ friend class SwapAllocator;
+};
+
+template <typename T>
+using SwapVector = std::vector<T, SwapAllocator<T>>;
+template <typename T, typename Comparator>
+using SwapSet = std::set<T, Comparator, SwapAllocator<T>>;
+
+} // namespace art
+
+#endif // ART_COMPILER_UTILS_SWAP_SPACE_H_
diff --git a/compiler/utils/swap_space_test.cc b/compiler/utils/swap_space_test.cc
new file mode 100644
index 0000000..bf50ac3
--- /dev/null
+++ b/compiler/utils/swap_space_test.cc
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/swap_space.h"
+
+#include <cstdio>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "gtest/gtest.h"
+
+#include "base/unix_file/fd_file.h"
+#include "common_runtime_test.h"
+#include "os.h"
+
+namespace art {
+
+class SwapSpaceTest : public CommonRuntimeTest {
+};
+
+static void SwapTest(bool use_file) {
+ ScratchFile scratch;
+ int fd = scratch.GetFd();
+ unlink(scratch.GetFilename().c_str());
+
+ SwapSpace pool(fd, 1 * MB);
+ SwapAllocator<void> alloc(use_file ? &pool : nullptr);
+
+ SwapVector<int32_t> v(alloc);
+ v.reserve(1000000);
+ for (int32_t i = 0; i < 1000000; ++i) {
+ v.push_back(i);
+ EXPECT_EQ(i, v[i]);
+ }
+
+ SwapVector<int32_t> v2(alloc);
+ v2.reserve(1000000);
+ for (int32_t i = 0; i < 1000000; ++i) {
+ v2.push_back(i);
+ EXPECT_EQ(i, v2[i]);
+ }
+
+ SwapVector<int32_t> v3(alloc);
+ v3.reserve(500000);
+ for (int32_t i = 0; i < 1000000; ++i) {
+ v3.push_back(i);
+ EXPECT_EQ(i, v2[i]);
+ }
+
+ // Verify contents.
+ for (int32_t i = 0; i < 1000000; ++i) {
+ EXPECT_EQ(i, v[i]);
+ EXPECT_EQ(i, v2[i]);
+ EXPECT_EQ(i, v3[i]);
+ }
+
+ scratch.Close();
+}
+
+TEST_F(SwapSpaceTest, Memory) {
+ SwapTest(false);
+}
+
+TEST_F(SwapSpaceTest, Swap) {
+ SwapTest(true);
+}
+
+} // namespace art
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index f0353f6..03744e4 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -51,7 +51,8 @@
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xE8);
static const int kSize = 5;
- EmitLabel(label, kSize);
+ // Offset by one because we already have emitted the opcode.
+ EmitLabel(label, kSize - 1);
}
@@ -409,6 +410,13 @@
}
+void X86Assembler::fsts(const Address& dst) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xD9);
+ EmitOperand(2, dst);
+}
+
+
void X86Assembler::fstps(const Address& dst) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xD9);
@@ -443,6 +451,27 @@
}
+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);
@@ -698,6 +727,13 @@
}
+void X86Assembler::fstl(const Address& dst) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xDD);
+ EmitOperand(2, dst);
+}
+
+
void X86Assembler::fstpl(const Address& dst) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xDD);
@@ -705,6 +741,14 @@
}
+void X86Assembler::fstsw() {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x9B);
+ EmitUint8(0xDF);
+ EmitUint8(0xE0);
+}
+
+
void X86Assembler::fnstcw(const Address& dst) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xD9);
@@ -776,6 +820,20 @@
}
+void X86Assembler::fucompp() {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xDA);
+ EmitUint8(0xE9);
+}
+
+
+void X86Assembler::fprem() {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xD9);
+ EmitUint8(0xF8);
+}
+
+
void X86Assembler::xchgl(Register dst, Register src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x87);
@@ -1351,38 +1409,6 @@
}
-void X86Assembler::FloatNegate(XmmRegister f) {
- static const struct {
- uint32_t a;
- uint32_t b;
- uint32_t c;
- uint32_t d;
- } float_negate_constant __attribute__((aligned(16))) =
- { 0x80000000, 0x00000000, 0x80000000, 0x00000000 };
- xorps(f, Address::Absolute(reinterpret_cast<uintptr_t>(&float_negate_constant)));
-}
-
-
-void X86Assembler::DoubleNegate(XmmRegister d) {
- static const struct {
- uint64_t a;
- uint64_t b;
- } double_negate_constant __attribute__((aligned(16))) =
- {0x8000000000000000LL, 0x8000000000000000LL};
- xorpd(d, Address::Absolute(reinterpret_cast<uintptr_t>(&double_negate_constant)));
-}
-
-
-void X86Assembler::DoubleAbs(XmmRegister reg) {
- static const struct {
- uint64_t a;
- uint64_t b;
- } double_abs_constant __attribute__((aligned(16))) =
- {0x7FFFFFFFFFFFFFFFLL, 0x7FFFFFFFFFFFFFFFLL};
- andpd(reg, Address::Absolute(reinterpret_cast<uintptr_t>(&double_abs_constant)));
-}
-
-
void X86Assembler::Align(int alignment, int offset) {
CHECK(IsPowerOfTwo(alignment));
// Emit nop instruction until the real position is aligned.
@@ -1512,8 +1538,12 @@
uint32_t reg_offset = 1;
CHECK_ALIGNED(frame_size, kStackAlignment);
+ int gpr_count = 0;
for (int i = spill_regs.size() - 1; i >= 0; --i) {
- pushl(spill_regs.at(i).AsX86().AsCpuRegister());
+ x86::X86ManagedRegister spill = spill_regs.at(i).AsX86();
+ DCHECK(spill.IsCpuRegister());
+ pushl(spill.AsCpuRegister());
+ gpr_count++;
// DW_CFA_advance_loc
DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
@@ -1527,7 +1557,7 @@
}
// return address then method on stack
- int32_t adjust = frame_size - (spill_regs.size() * kFramePointerSize) -
+ int32_t adjust = frame_size - (gpr_count * kFramePointerSize) -
sizeof(StackReference<mirror::ArtMethod>) /*method*/ -
kFramePointerSize /*return address*/;
addl(ESP, Immediate(-adjust));
@@ -1547,9 +1577,18 @@
DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
for (size_t i = 0; i < entry_spills.size(); ++i) {
- movl(Address(ESP, frame_size + sizeof(StackReference<mirror::ArtMethod>) +
- (i * kFramePointerSize)),
- entry_spills.at(i).AsX86().AsCpuRegister());
+ ManagedRegisterSpill spill = entry_spills.at(i);
+ if (spill.AsX86().IsCpuRegister()) {
+ movl(Address(ESP, frame_size + spill.getSpillOffset()), spill.AsX86().AsCpuRegister());
+ } else {
+ DCHECK(spill.AsX86().IsXmmRegister());
+ if (spill.getSize() == 8) {
+ movsd(Address(ESP, frame_size + spill.getSpillOffset()), spill.AsX86().AsXmmRegister());
+ } else {
+ CHECK_EQ(spill.getSize(), 4);
+ movss(Address(ESP, frame_size + spill.getSpillOffset()), spill.AsX86().AsXmmRegister());
+ }
+ }
}
}
@@ -1559,7 +1598,9 @@
addl(ESP, Immediate(frame_size - (spill_regs.size() * kFramePointerSize) -
sizeof(StackReference<mirror::ArtMethod>)));
for (size_t i = 0; i < spill_regs.size(); ++i) {
- popl(spill_regs.at(i).AsX86().AsCpuRegister());
+ x86::X86ManagedRegister spill = spill_regs.at(i).AsX86();
+ DCHECK(spill.IsCpuRegister());
+ popl(spill.AsCpuRegister());
}
ret();
}
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index 9fecf1e..3a44ace 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -274,6 +274,9 @@
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 addsd(XmmRegister dst, XmmRegister src);
void addsd(XmmRegister dst, const Address& src);
void subsd(XmmRegister dst, XmmRegister src);
@@ -314,9 +317,15 @@
void flds(const Address& src);
void fstps(const Address& dst);
+ void fsts(const Address& dst);
void fldl(const Address& src);
void fstpl(const Address& dst);
+ void fstl(const Address& dst);
+
+ void fstsw();
+
+ void fucompp();
void fnstcw(const Address& dst);
void fldcw(const Address& src);
@@ -331,6 +340,7 @@
void fsin();
void fcos();
void fptan();
+ void fprem();
void xchgl(Register dst, Register src);
void xchgl(Register reg, const Address& address);
@@ -444,11 +454,6 @@
void LoadLongConstant(XmmRegister dst, int64_t value);
void LoadDoubleConstant(XmmRegister dst, double value);
- void DoubleNegate(XmmRegister d);
- void FloatNegate(XmmRegister f);
-
- void DoubleAbs(XmmRegister reg);
-
void LockCmpxchgl(const Address& address, Register reg) {
lock()->cmpxchgl(address, reg);
}
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index d901673..fccb510 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -105,6 +105,18 @@
DriverStr(expected, "movl");
}
+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 =
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 474d8a9..556fa9b 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -57,7 +57,8 @@
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xE8);
static const int kSize = 5;
- EmitLabel(label, kSize);
+ // Offset by one because we already have emitted the opcode.
+ EmitLabel(label, kSize - 1);
}
void X86_64Assembler::pushq(CpuRegister reg) {
@@ -184,6 +185,20 @@
EmitImmediate(imm);
}
+
+void X86_64Assembler::cmov(Condition c, CpuRegister dst, CpuRegister src) {
+ cmov(c, dst, src, true);
+}
+
+void X86_64Assembler::cmov(Condition c, CpuRegister dst, CpuRegister src, bool is64bit) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitOptionalRex(false, is64bit, dst.NeedsRex(), false, src.NeedsRex());
+ EmitUint8(0x0F);
+ EmitUint8(0x40 + c);
+ EmitRegisterOperand(dst.LowBits(), src.LowBits());
+}
+
+
void X86_64Assembler::movzxb(CpuRegister dst, CpuRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitOptionalByteRegNormalizingRex32(dst, src);
@@ -313,6 +328,14 @@
}
+void X86_64Assembler::leal(CpuRegister dst, const Address& src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x8D);
+ EmitOperand(dst.LowBits(), src);
+}
+
+
void X86_64Assembler::movaps(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitOptionalRex32(dst, src);
@@ -369,19 +392,26 @@
void X86_64Assembler::movd(XmmRegister dst, CpuRegister src) {
+ movd(dst, src, true);
+}
+
+void X86_64Assembler::movd(CpuRegister dst, XmmRegister src) {
+ movd(dst, src, true);
+}
+
+void X86_64Assembler::movd(XmmRegister dst, CpuRegister src, bool is64bit) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
- EmitRex64(dst, src);
+ EmitOptionalRex(false, is64bit, dst.NeedsRex(), false, src.NeedsRex());
EmitUint8(0x0F);
EmitUint8(0x6E);
EmitOperand(dst.LowBits(), Operand(src));
}
-
-void X86_64Assembler::movd(CpuRegister dst, XmmRegister src) {
+void X86_64Assembler::movd(CpuRegister dst, XmmRegister src, bool is64bit) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
- EmitRex64(src, dst);
+ EmitOptionalRex(false, is64bit, src.NeedsRex(), false, dst.NeedsRex());
EmitUint8(0x0F);
EmitUint8(0x7E);
EmitOperand(src.LowBits(), Operand(dst));
@@ -475,6 +505,13 @@
}
+void X86_64Assembler::fsts(const Address& dst) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xD9);
+ EmitOperand(2, dst);
+}
+
+
void X86_64Assembler::fstps(const Address& dst) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xD9);
@@ -663,9 +700,19 @@
void X86_64Assembler::cvttss2si(CpuRegister dst, XmmRegister src) {
+ cvttss2si(dst, src, false);
+}
+
+
+void X86_64Assembler::cvttss2si(CpuRegister dst, XmmRegister src, bool is64bit) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xF3);
- EmitOptionalRex32(dst, src);
+ if (is64bit) {
+ // Emit a REX.W prefix if the operand size is 64 bits.
+ EmitRex64(dst, src);
+ } else {
+ EmitOptionalRex32(dst, src);
+ }
EmitUint8(0x0F);
EmitUint8(0x2C);
EmitXmmRegisterOperand(dst.LowBits(), src);
@@ -673,9 +720,19 @@
void X86_64Assembler::cvttsd2si(CpuRegister dst, XmmRegister src) {
+ cvttsd2si(dst, src, false);
+}
+
+
+void X86_64Assembler::cvttsd2si(CpuRegister dst, XmmRegister src, bool is64bit) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xF2);
- EmitOptionalRex32(dst, src);
+ if (is64bit) {
+ // Emit a REX.W prefix if the operand size is 64 bits.
+ EmitRex64(dst, src);
+ } else {
+ EmitOptionalRex32(dst, src);
+ }
EmitUint8(0x0F);
EmitUint8(0x2C);
EmitXmmRegisterOperand(dst.LowBits(), src);
@@ -806,6 +863,39 @@
EmitOperand(dst.LowBits(), src);
}
+void X86_64Assembler::andpd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x54);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::andps(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x54);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::orpd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x56);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::orps(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x56);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
void X86_64Assembler::fldl(const Address& src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
@@ -814,6 +904,13 @@
}
+void X86_64Assembler::fstl(const Address& dst) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xDD);
+ EmitOperand(2, dst);
+}
+
+
void X86_64Assembler::fstpl(const Address& dst) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xDD);
@@ -821,6 +918,14 @@
}
+void X86_64Assembler::fstsw() {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x9B);
+ EmitUint8(0xDF);
+ EmitUint8(0xE0);
+}
+
+
void X86_64Assembler::fnstcw(const Address& dst) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xD9);
@@ -891,6 +996,19 @@
EmitUint8(0xF2);
}
+void X86_64Assembler::fucompp() {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xDA);
+ EmitUint8(0xE9);
+}
+
+
+void X86_64Assembler::fprem() {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xD9);
+ EmitUint8(0xF8);
+}
+
void X86_64Assembler::xchgl(CpuRegister dst, CpuRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
@@ -1047,6 +1165,14 @@
}
+void X86_64Assembler::testl(CpuRegister reg, const Address& address) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitOptionalRex32(reg, address);
+ EmitUint8(0x85);
+ EmitOperand(reg.LowBits(), address);
+}
+
+
void X86_64Assembler::testl(CpuRegister reg, const Immediate& immediate) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
// For registers that have a byte variant (RAX, RBX, RCX, and RDX)
@@ -1737,6 +1863,20 @@
EmitUint8(0xC0 + dst.LowBits());
}
+void X86_64Assembler::bswapl(CpuRegister dst) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitOptionalRex(false, false, false, false, dst.NeedsRex());
+ EmitUint8(0x0F);
+ EmitUint8(0xC8 + dst.LowBits());
+}
+
+void X86_64Assembler::bswapq(CpuRegister dst) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitOptionalRex(false, true, false, false, dst.NeedsRex());
+ EmitUint8(0x0F);
+ EmitUint8(0xC8 + dst.LowBits());
+}
+
void X86_64Assembler::LoadDoubleConstant(XmmRegister dst, double value) {
// TODO: Need to have a code constants table.
@@ -1748,38 +1888,6 @@
}
-void X86_64Assembler::FloatNegate(XmmRegister f) {
- static const struct {
- uint32_t a;
- uint32_t b;
- uint32_t c;
- uint32_t d;
- } float_negate_constant __attribute__((aligned(16))) =
- { 0x80000000, 0x00000000, 0x80000000, 0x00000000 };
- xorps(f, Address::Absolute(reinterpret_cast<uintptr_t>(&float_negate_constant)));
-}
-
-
-void X86_64Assembler::DoubleNegate(XmmRegister d) {
- static const struct {
- uint64_t a;
- uint64_t b;
- } double_negate_constant __attribute__((aligned(16))) =
- {0x8000000000000000LL, 0x8000000000000000LL};
- xorpd(d, Address::Absolute(reinterpret_cast<uintptr_t>(&double_negate_constant)));
-}
-
-
-void X86_64Assembler::DoubleAbs(XmmRegister reg) {
- static const struct {
- uint64_t a;
- uint64_t b;
- } double_abs_constant __attribute__((aligned(16))) =
- {0x7FFFFFFFFFFFFFFFLL, 0x7FFFFFFFFFFFFFFFLL};
- andpd(reg, Address::Absolute(reinterpret_cast<uintptr_t>(&double_abs_constant)));
-}
-
-
void X86_64Assembler::Align(int alignment, int offset) {
CHECK(IsPowerOfTwo(alignment));
// Emit nop instruction until the real position is aligned.
@@ -1997,6 +2105,10 @@
EmitOptionalRex(false, true, dst.NeedsRex(), false, src.NeedsRex());
}
+void X86_64Assembler::EmitRex64(CpuRegister dst, XmmRegister src) {
+ EmitOptionalRex(false, true, dst.NeedsRex(), false, src.NeedsRex());
+}
+
void X86_64Assembler::EmitRex64(CpuRegister dst, const Operand& operand) {
uint8_t rex = 0x48 | operand.rex(); // REX.W000
if (dst.NeedsRex()) {
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 6e71e4a..a1c704e 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -178,20 +178,20 @@
}
void Init(CpuRegister base_in, int32_t disp) {
- if (disp == 0 && base_in.AsRegister() != RBP) {
+ if (disp == 0 && base_in.LowBits() != RBP) {
SetModRM(0, base_in);
- if (base_in.AsRegister() == RSP) {
+ if (base_in.LowBits() == RSP) {
SetSIB(TIMES_1, CpuRegister(RSP), base_in);
}
} else if (disp >= -128 && disp <= 127) {
SetModRM(1, base_in);
- if (base_in.AsRegister() == RSP) {
+ if (base_in.LowBits() == RSP) {
SetSIB(TIMES_1, CpuRegister(RSP), base_in);
}
SetDisp8(disp);
} else {
SetModRM(2, base_in);
- if (base_in.AsRegister() == RSP) {
+ if (base_in.LowBits() == RSP) {
SetSIB(TIMES_1, CpuRegister(RSP), base_in);
}
SetDisp32(disp);
@@ -208,7 +208,7 @@
Address(CpuRegister base_in, CpuRegister index_in, ScaleFactor scale_in, int32_t disp) {
CHECK_NE(index_in.AsRegister(), RSP); // Illegal addressing mode.
- if (disp == 0 && base_in.AsRegister() != RBP) {
+ if (disp == 0 && base_in.LowBits() != RBP) {
SetModRM(0, CpuRegister(RSP));
SetSIB(scale_in, index_in, base_in);
} else if (disp >= -128 && disp <= 127) {
@@ -276,6 +276,9 @@
void movl(const Address& dst, CpuRegister src);
void movl(const Address& dst, const Immediate& imm);
+ void cmov(Condition c, CpuRegister dst, CpuRegister src); // This is the 64b version.
+ void cmov(Condition c, CpuRegister dst, CpuRegister src, bool is64bit);
+
void movzxb(CpuRegister dst, CpuRegister src);
void movzxb(CpuRegister dst, const Address& src);
void movsxb(CpuRegister dst, CpuRegister src);
@@ -293,6 +296,7 @@
void movw(const Address& dst, const Immediate& imm);
void leaq(CpuRegister dst, const Address& src);
+ void leal(CpuRegister dst, const Address& src);
void movaps(XmmRegister dst, XmmRegister src);
@@ -303,8 +307,10 @@
void movsxd(CpuRegister dst, CpuRegister src);
void movsxd(CpuRegister dst, const Address& src);
- void movd(XmmRegister dst, CpuRegister src);
- void movd(CpuRegister dst, XmmRegister src);
+ void movd(XmmRegister dst, CpuRegister src); // Note: this is the r64 version, formally movq.
+ void movd(CpuRegister dst, XmmRegister src); // Note: this is the r64 version, formally movq.
+ void movd(XmmRegister dst, CpuRegister src, bool is64bit);
+ void movd(CpuRegister dst, XmmRegister src, bool is64bit);
void addss(XmmRegister dst, XmmRegister src);
void addss(XmmRegister dst, const Address& src);
@@ -340,7 +346,9 @@
void cvtsd2ss(XmmRegister dst, XmmRegister src);
void cvttss2si(CpuRegister dst, XmmRegister src); // Note: this is the r32 version.
+ void cvttss2si(CpuRegister dst, XmmRegister src, bool is64bit);
void cvttsd2si(CpuRegister dst, XmmRegister src); // Note: this is the r32 version.
+ void cvttsd2si(CpuRegister dst, XmmRegister src, bool is64bit);
void cvtdq2pd(XmmRegister dst, XmmRegister src);
@@ -358,12 +366,23 @@
void xorps(XmmRegister dst, XmmRegister src);
void andpd(XmmRegister dst, const Address& src);
+ void andpd(XmmRegister dst, XmmRegister src);
+ void andps(XmmRegister dst, XmmRegister src);
+
+ void orpd(XmmRegister dst, XmmRegister src);
+ void orps(XmmRegister dst, XmmRegister src);
void flds(const Address& src);
void fstps(const Address& dst);
+ void fsts(const Address& dst);
void fldl(const Address& src);
void fstpl(const Address& dst);
+ void fstl(const Address& dst);
+
+ void fstsw();
+
+ void fucompp();
void fnstcw(const Address& dst);
void fldcw(const Address& src);
@@ -378,6 +397,7 @@
void fsin();
void fcos();
void fptan();
+ void fprem();
void xchgl(CpuRegister dst, CpuRegister src);
void xchgq(CpuRegister dst, CpuRegister src);
@@ -397,6 +417,7 @@
void cmpq(const Address& address, const Immediate& imm);
void testl(CpuRegister reg1, CpuRegister reg2);
+ void testl(CpuRegister reg, const Address& address);
void testl(CpuRegister reg, const Immediate& imm);
void testq(CpuRegister reg1, CpuRegister reg2);
@@ -502,6 +523,9 @@
void setcc(Condition condition, CpuRegister dst);
+ void bswapl(CpuRegister dst);
+ void bswapq(CpuRegister dst);
+
//
// Macros for High-level operations.
//
@@ -510,11 +534,6 @@
void LoadDoubleConstant(XmmRegister dst, double value);
- void DoubleNegate(XmmRegister d);
- void FloatNegate(XmmRegister f);
-
- void DoubleAbs(XmmRegister reg);
-
void LockCmpxchgl(const Address& address, CpuRegister reg) {
lock()->cmpxchgl(address, reg);
}
@@ -688,6 +707,7 @@
void EmitRex64(CpuRegister dst, CpuRegister src);
void EmitRex64(CpuRegister dst, const Operand& operand);
void EmitRex64(XmmRegister dst, CpuRegister src);
+ void EmitRex64(CpuRegister dst, XmmRegister src);
// Emit a REX prefix to normalize byte registers plus necessary register bit encodings.
void EmitOptionalByteRegNormalizingRex32(CpuRegister dst, CpuRegister src);
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index c8e923c..6df4144 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -330,7 +330,6 @@
assembler->shlq(*reg, shifter);
str << "shlq %cl, %" << assembler_test->GetRegisterName(*reg) << "\n";
}
- printf("%s\n", str.str().c_str());
return str.str();
}
@@ -536,10 +535,16 @@
x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12));
GetAssembler()->movl(x86_64::CpuRegister(x86_64::R8), x86_64::Address(
x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12));
+ GetAssembler()->movl(x86_64::CpuRegister(x86_64::RAX), x86_64::Address(
+ x86_64::CpuRegister(x86_64::R13), 0));
+ GetAssembler()->movl(x86_64::CpuRegister(x86_64::RAX), x86_64::Address(
+ x86_64::CpuRegister(x86_64::R13), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_1, 0));
const char* expected =
"movl 0xc(%RDI,%RBX,4), %EAX\n"
"movl 0xc(%RDI,%R9,4), %EAX\n"
- "movl 0xc(%RDI,%R9,4), %R8d\n";
+ "movl 0xc(%RDI,%R9,4), %R8d\n"
+ "movl (%R13), %EAX\n"
+ "movl (%R13,%R9,1), %EAX\n";
DriverStr(expected, "movl");
}
@@ -684,6 +689,22 @@
DriverStr(RepeatFF(&x86_64::X86_64Assembler::xorpd, "xorpd %{reg2}, %{reg1}"), "xorpd");
}
+TEST_F(AssemblerX86_64Test, Andps) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::andps, "andps %{reg2}, %{reg1}"), "andps");
+}
+
+TEST_F(AssemblerX86_64Test, Andpd) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::andpd, "andpd %{reg2}, %{reg1}"), "andpd");
+}
+
+TEST_F(AssemblerX86_64Test, Orps) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::orps, "orps %{reg2}, %{reg1}"), "orps");
+}
+
+TEST_F(AssemblerX86_64Test, Orpd) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::orpd, "orpd %{reg2}, %{reg1}"), "orpd");
+}
+
// X87
std::string x87_fn(AssemblerX86_64Test::Base* assembler_test ATTRIBUTE_UNUSED,
@@ -752,6 +773,14 @@
// MISC //
//////////
+TEST_F(AssemblerX86_64Test, Bswapl) {
+ DriverStr(Repeatr(&x86_64::X86_64Assembler::bswapl, "bswap %{reg}"), "bswapl");
+}
+
+TEST_F(AssemblerX86_64Test, Bswapq) {
+ DriverStr(RepeatR(&x86_64::X86_64Assembler::bswapq, "bswap %{reg}"), "bswapq");
+}
+
std::string setcc_test_fn(AssemblerX86_64Test::Base* assembler_test,
x86_64::X86_64Assembler* assembler) {
// From Condition
diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk
index 0ef20d6..8afd443 100644
--- a/dalvikvm/Android.mk
+++ b/dalvikvm/Android.mk
@@ -35,7 +35,7 @@
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := dalvikvm32
LOCAL_MODULE_STEM_64 := dalvikvm64
-include external/libcxx/libcxx.mk
+LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
include $(BUILD_EXECUTABLE)
# Create symlink for the primary version target.
@@ -68,7 +68,7 @@
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := dalvikvm32
LOCAL_MODULE_STEM_64 := dalvikvm64
-include external/libcxx/libcxx.mk
+LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
include $(BUILD_HOST_EXECUTABLE)
# Create symlink for the primary version target.
diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk
index 4f39c42..3f15964 100644
--- a/dex2oat/Android.mk
+++ b/dex2oat/Android.mk
@@ -24,22 +24,30 @@
# TODO: Remove this when the framework (installd) supports pushing the
# right instruction-set parameter for the primary architecture.
ifneq ($(filter ro.zygote=zygote64,$(PRODUCT_DEFAULT_PROPERTY_OVERRIDES)),)
- dex2oat_arch := 64
+ dex2oat_target_arch := 64
else
- dex2oat_arch := 32
+ dex2oat_target_arch := 32
+endif
+
+# We need to explcitly give the arch, as giving 'both' will make the
+# build-art-executable rule compile dex2oat for 64bits.
+ifeq ($(HOST_PREFER_32_BIT),true)
+ dex2oat_host_arch := 32
+else
+ dex2oat_host_arch := both
endif
ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
- $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libart-compiler,art/compiler,target,ndebug,$(dex2oat_arch)))
+ $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libart-compiler,art/compiler,target,ndebug,$(dex2oat_target_arch)))
endif
ifeq ($(ART_BUILD_TARGET_DEBUG),true)
- $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler,art/compiler,target,debug,$(dex2oat_arch)))
+ $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler,art/compiler,target,debug,$(dex2oat_target_arch)))
endif
# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
ifeq ($(ART_BUILD_HOST_NDEBUG),true)
- $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart-compiler libziparchive-host,art/compiler,host,ndebug))
+ $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart-compiler libziparchive-host,art/compiler,host,ndebug,$(dex2oat_host_arch)))
endif
ifeq ($(ART_BUILD_HOST_DEBUG),true)
- $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd-compiler libziparchive-host,art/compiler,host,debug))
+ $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd-compiler libziparchive-host,art/compiler,host,debug,$(dex2oat_host_arch)))
endif
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index a1ac2f0..e607e15 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -25,10 +25,6 @@
#include <string>
#include <vector>
-#ifndef __APPLE__
-#include <malloc.h> // For mallinfo
-#endif
-
#if defined(__linux__) && defined(__arm__)
#include <sys/personality.h>
#include <sys/utsname.h>
@@ -38,6 +34,7 @@
#include <cutils/trace.h>
#include "arch/instruction_set_features.h"
+#include "arch/mips/instruction_set_features_mips.h"
#include "base/dumpable.h"
#include "base/stl_util.h"
#include "base/stringpiece.h"
@@ -47,7 +44,7 @@
#include "compiler.h"
#include "compiler_callbacks.h"
#include "dex_file-inl.h"
-#include "dex/pass_driver_me_opts.h"
+#include "dex/pass_manager.h"
#include "dex/verification_results.h"
#include "dex/quick_compiler_callbacks.h"
#include "dex/quick/dex_file_to_method_inliner_map.h"
@@ -134,9 +131,6 @@
UsageError(" --oat-symbols=<file.oat>: specifies the oat output destination with full symbols.");
UsageError(" Example: --oat-symbols=/symbols/system/framework/boot.oat");
UsageError("");
- UsageError(" --bitcode=<file.bc>: specifies the optional bitcode filename.");
- UsageError(" Example: --bitcode=/system/framework/boot.bc");
- UsageError("");
UsageError(" --image=<file.art>: specifies the output image filename.");
UsageError(" Example: --image=/system/framework/boot.art");
UsageError("");
@@ -154,7 +148,7 @@
UsageError(" Example: --android-root=out/host/linux-x86");
UsageError(" Default: $ANDROID_ROOT");
UsageError("");
- UsageError(" --instruction-set=(arm|arm64|mips|x86|x86_64): compile for a particular");
+ UsageError(" --instruction-set=(arm|arm64|mips|mips64|x86|x86_64): compile for a particular");
UsageError(" instruction set.");
UsageError(" Example: --instruction-set=x86");
UsageError(" Default: arm");
@@ -166,12 +160,10 @@
UsageError(" --compile-pic: Force indirect use of code, methods, and classes");
UsageError(" Default: disabled");
UsageError("");
- UsageError(" --compiler-backend=(Quick|Optimizing|Portable): select compiler backend");
+ UsageError(" --compiler-backend=(Quick|Optimizing): select compiler backend");
UsageError(" set.");
- UsageError(" Example: --compiler-backend=Portable");
- if (kUsePortableCompiler) {
- UsageError(" Default: Portable");
- } else if (kUseOptimizingCompiler) {
+ UsageError(" Example: --compiler-backend=Optimizing");
+ if (kUseOptimizingCompiler) {
UsageError(" Default: Optimizing");
} else {
UsageError(" Default: Quick");
@@ -225,8 +217,6 @@
UsageError(" Example: --num-dex-method=%d", CompilerOptions::kDefaultNumDexMethodsThreshold);
UsageError(" Default: %d", CompilerOptions::kDefaultNumDexMethodsThreshold);
UsageError("");
- UsageError(" --host: used with Portable backend to link against host runtime libraries");
- UsageError("");
UsageError(" --dump-timing: display a breakdown of where time was spent");
UsageError("");
UsageError(" --include-patch-information: Include patching information so the generated code");
@@ -259,6 +249,12 @@
UsageError(" Used to specify a pass specific option. The setting itself must be integer.");
UsageError(" Separator used between options is a comma.");
UsageError("");
+ UsageError(" --swap-file=<file-name>: specifies a file to use for swap.");
+ UsageError(" Example: --swap-file=/data/tmp/swap.001");
+ UsageError("");
+ UsageError(" --swap-fd=<file-descriptor>: specifies a file to use for swap (by descriptor).");
+ UsageError(" Example: --swap-fd=10");
+ UsageError("");
std::cerr << "See log for usage error information\n";
exit(EXIT_FAILURE);
}
@@ -267,7 +263,7 @@
// during development when fatal aborts lead to a cascade of failures
// that result in a deadlock.
class WatchDog {
-// WatchDog defines its own CHECK_PTHREAD_CALL to avoid using Log which uses locks
+// WatchDog defines its own CHECK_PTHREAD_CALL to avoid using LOG which uses locks
#undef CHECK_PTHREAD_CALL
#define CHECK_WATCH_DOG_PTHREAD_CALL(call, args, what) \
do { \
@@ -330,41 +326,23 @@
message.c_str());
}
- static void Warn(const std::string& message) {
- Message('W', message);
- }
-
[[noreturn]] static void Fatal(const std::string& message) {
Message('F', message);
exit(1);
}
void Wait() {
- bool warning = true;
- CHECK_GT(kWatchDogTimeoutSeconds, kWatchDogWarningSeconds);
// TODO: tune the multiplier for GC verification, the following is just to make the timeout
// large.
int64_t multiplier = kVerifyObjectSupport > kVerifyObjectModeFast ? 100 : 1;
- timespec warning_ts;
- InitTimeSpec(true, CLOCK_REALTIME, multiplier * kWatchDogWarningSeconds * 1000, 0, &warning_ts);
timespec timeout_ts;
InitTimeSpec(true, CLOCK_REALTIME, multiplier * kWatchDogTimeoutSeconds * 1000, 0, &timeout_ts);
const char* reason = "dex2oat watch dog thread waiting";
CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_lock, (&mutex_), reason);
while (!shutting_down_) {
- int rc = TEMP_FAILURE_RETRY(pthread_cond_timedwait(&cond_, &mutex_,
- warning ? &warning_ts
- : &timeout_ts));
+ int rc = TEMP_FAILURE_RETRY(pthread_cond_timedwait(&cond_, &mutex_, &timeout_ts));
if (rc == ETIMEDOUT) {
- std::string message(StringPrintf("dex2oat did not finish after %d seconds",
- warning ? kWatchDogWarningSeconds
- : kWatchDogTimeoutSeconds));
- if (warning) {
- Warn(message.c_str());
- warning = false;
- } else {
- Fatal(message.c_str());
- }
+ Fatal(StringPrintf("dex2oat did not finish after %d seconds", kWatchDogTimeoutSeconds));
} else if (rc != 0) {
std::string message(StringPrintf("pthread_cond_timedwait failed: %s",
strerror(errno)));
@@ -378,12 +356,8 @@
// Debug builds are slower so they have larger timeouts.
static const unsigned int kSlowdownFactor = kIsDebugBuild ? 5U : 1U;
- static const unsigned int kWatchDogWarningSeconds = kUsePortableCompiler ?
- kSlowdownFactor * 2 * 60 : // 2 minutes scaled by kSlowdownFactor (portable).
- kSlowdownFactor * 1 * 60; // 1 minute scaled by kSlowdownFactor (not-portable).
- static const unsigned int kWatchDogTimeoutSeconds = kUsePortableCompiler ?
- kSlowdownFactor * 30 * 60 : // 30 minutes scaled by kSlowdownFactor (portable).
- kSlowdownFactor * 6 * 60; // 6 minutes scaled by kSlowdownFactor (not-portable).
+ // 6 minutes scaled by kSlowdownFactor.
+ static const unsigned int kWatchDogTimeoutSeconds = kSlowdownFactor * 6 * 60;
bool is_watch_dog_enabled_;
bool shutting_down_;
@@ -426,12 +400,29 @@
*parsed_value = value;
}
+static constexpr size_t kMinDexFilesForSwap = 2;
+static constexpr size_t kMinDexFileCumulativeSizeForSwap = 20 * MB;
+
+static bool UseSwap(bool is_image, std::vector<const DexFile*>& dex_files) {
+ if (is_image) {
+ // Don't use swap, we know generation should succeed, and we don't want to slow it down.
+ return false;
+ }
+ if (dex_files.size() < kMinDexFilesForSwap) {
+ // If there are less dex files than the threshold, assume it's gonna be fine.
+ return false;
+ }
+ size_t dex_files_size = 0;
+ for (const auto* dex_file : dex_files) {
+ dex_files_size += dex_file->GetHeader().file_size_;
+ }
+ return dex_files_size >= kMinDexFileCumulativeSizeForSwap;
+}
+
class Dex2Oat FINAL {
public:
explicit Dex2Oat(TimingLogger* timings) :
- compiler_kind_(kUsePortableCompiler
- ? Compiler::kPortable
- : (kUseOptimizingCompiler ? Compiler::kOptimizing : Compiler::kQuick)),
+ compiler_kind_(kUseOptimizingCompiler ? Compiler::kOptimizing : Compiler::kQuick),
instruction_set_(kRuntimeISA),
// Take the default set of instruction features from the build.
method_inliner_map_(),
@@ -451,10 +442,19 @@
dump_passes_(false),
dump_timing_(false),
dump_slow_timing_(kIsDebugBuild),
+ swap_fd_(-1),
timings_(timings) {}
~Dex2Oat() {
- LogCompletionTime(); // Needs to be before since it accesses the runtime.
+ // Free opened dex files before deleting the runtime_, because ~DexFile
+ // uses MemMap, which is shut down by ~Runtime.
+ class_path_files_.clear();
+ opened_dex_files_.clear();
+
+ // Log completion time before deleting the runtime_, because this accesses
+ // the runtime.
+ LogCompletionTime();
+
if (kIsDebugBuild || (RUNNING_ON_VALGRIND != 0)) {
delete runtime_; // See field declaration for why this is manual.
}
@@ -490,12 +490,13 @@
// Profile file to use
double top_k_profile_threshold = CompilerOptions::kDefaultTopKProfileThreshold;
- bool print_pass_options = false;
bool include_patch_information = CompilerOptions::kDefaultIncludePatchInformation;
bool include_debug_symbols = kIsDebugBuild;
bool watch_dog_enabled = true;
bool generate_gdb_information = kIsDebugBuild;
+ PassManagerOptions pass_manager_options;
+
std::string error_msg;
for (int i = 0; i < argc; i++) {
@@ -547,8 +548,6 @@
}
} else if (option.starts_with("--oat-location=")) {
oat_location_ = option.substr(strlen("--oat-location=")).data();
- } else if (option.starts_with("--bitcode=")) {
- bitcode_filename_ = option.substr(strlen("--bitcode=")).data();
} else if (option.starts_with("--image=")) {
image_filename_ = option.substr(strlen("--image=")).data();
} else if (option.starts_with("--image-classes=")) {
@@ -573,7 +572,7 @@
} else if (option.starts_with("--instruction-set=")) {
StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
// StringPiece is not necessarily zero-terminated, so need to make a copy and ensure it.
- std::unique_ptr<char> buf(new char[instruction_set_str.length() + 1]);
+ std::unique_ptr<char[]> buf(new char[instruction_set_str.length() + 1]);
strncpy(buf.get(), instruction_set_str.data(), instruction_set_str.length());
buf.get()[instruction_set_str.length()] = 0;
instruction_set_ = GetInstructionSetFromString(buf.get());
@@ -609,8 +608,6 @@
compiler_kind_ = Compiler::kQuick;
} else if (backend_str == "Optimizing") {
compiler_kind_ = Compiler::kOptimizing;
- } else if (backend_str == "Portable") {
- compiler_kind_ = Compiler::kPortable;
} else {
Usage("Unknown compiler backend: %s", backend_str.data());
}
@@ -672,6 +669,8 @@
dump_timing_ = true;
} else if (option == "--dump-passes") {
dump_passes_ = true;
+ } else if (option.starts_with("--dump-cfg=")) {
+ dump_cfg_file_name_ = option.substr(strlen("--dump-cfg=")).data();
} else if (option == "--dump-stats") {
dump_stats_ = true;
} else if (option == "--include-debug-symbols" || option == "--no-strip-symbols") {
@@ -687,30 +686,30 @@
} else if (option.starts_with("--top-k-profile-threshold=")) {
ParseDouble(option.data(), '=', 0.0, 100.0, &top_k_profile_threshold);
} else if (option == "--print-pass-names") {
- PassDriverMEOpts::PrintPassNames();
+ pass_manager_options.SetPrintPassNames(true);
} else if (option.starts_with("--disable-passes=")) {
- std::string disable_passes = option.substr(strlen("--disable-passes=")).data();
- PassDriverMEOpts::CreateDefaultPassList(disable_passes);
+ const std::string disable_passes = option.substr(strlen("--disable-passes=")).data();
+ pass_manager_options.SetDisablePassList(disable_passes);
} else if (option.starts_with("--print-passes=")) {
- std::string print_passes = option.substr(strlen("--print-passes=")).data();
- PassDriverMEOpts::SetPrintPassList(print_passes);
+ const std::string print_passes = option.substr(strlen("--print-passes=")).data();
+ pass_manager_options.SetPrintPassList(print_passes);
} else if (option == "--print-all-passes") {
- PassDriverMEOpts::SetPrintAllPasses();
+ pass_manager_options.SetPrintAllPasses();
} else if (option.starts_with("--dump-cfg-passes=")) {
- std::string dump_passes_string = option.substr(strlen("--dump-cfg-passes=")).data();
- PassDriverMEOpts::SetDumpPassList(dump_passes_string);
+ const std::string dump_passes_string = option.substr(strlen("--dump-cfg-passes=")).data();
+ pass_manager_options.SetDumpPassList(dump_passes_string);
} else if (option == "--print-pass-options") {
- print_pass_options = true;
+ pass_manager_options.SetPrintPassOptions(true);
} else if (option.starts_with("--pass-options=")) {
- std::string options = option.substr(strlen("--pass-options=")).data();
- PassDriverMEOpts::SetOverriddenPassOptions(options);
+ const std::string options = option.substr(strlen("--pass-options=")).data();
+ pass_manager_options.SetOverriddenPassOptions(options);
} else if (option == "--include-patch-information") {
include_patch_information = true;
} else if (option == "--no-include-patch-information") {
include_patch_information = false;
} else if (option.starts_with("--verbose-methods=")) {
- // TODO: rather than switch off compiler logging, make all VLOG(compiler) messages conditional
- // on having verbost methods.
+ // TODO: rather than switch off compiler logging, make all VLOG(compiler) messages
+ // conditional on having verbost methods.
gLogVerbosity.compiler = false;
Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods_);
} else if (option.starts_with("--dump-init-failures=")) {
@@ -723,6 +722,16 @@
<< "failures.";
init_failure_output_.reset();
}
+ } else if (option.starts_with("--swap-file=")) {
+ swap_file_name_ = option.substr(strlen("--swap-file=")).data();
+ } else if (option.starts_with("--swap-fd=")) {
+ const char* swap_fd_str = option.substr(strlen("--swap-fd=")).data();
+ if (!ParseInt(swap_fd_str, &swap_fd_)) {
+ Usage("Failed to parse --swap-fd argument '%s' as an integer", swap_fd_str);
+ }
+ if (swap_fd_ < 0) {
+ Usage("--swap-fd passed a negative value %d", swap_fd_);
+ }
} else {
Usage("Unknown argument %s", option.data());
}
@@ -855,7 +864,14 @@
}
if (compiler_filter_string == nullptr) {
- if (instruction_set_ == kMips64) {
+ if (instruction_set_ == kMips &&
+ reinterpret_cast<const MipsInstructionSetFeatures*>(instruction_set_features_.get())->
+ IsR6()) {
+ // For R6, only interpreter mode is working.
+ // TODO: fix compiler for Mips32r6.
+ compiler_filter_string = "interpret-only";
+ } else if (instruction_set_ == kMips64) {
+ // For Mips64, can only compile in interpreter mode.
// TODO: fix compiler for Mips64.
compiler_filter_string = "interpret-only";
} else if (image_) {
@@ -909,10 +925,6 @@
break;
}
- if (print_pass_options) {
- PassDriverMEOpts::PrintPassOptions();
- }
-
compiler_options_.reset(new CompilerOptions(compiler_filter,
huge_method_threshold,
large_method_threshold,
@@ -927,12 +939,10 @@
implicit_so_checks,
implicit_suspend_checks,
compile_pic,
- #ifdef ART_SEA_IR_MODE
- true,
- #endif
verbose_methods_.empty() ?
nullptr :
&verbose_methods_,
+ new PassManagerOptions(pass_manager_options),
init_failure_output_.get()));
// Done with usage checks, enable watchdog if requested
@@ -960,7 +970,8 @@
}
}
- // Check whether the oat output file is writable, and open it for later.
+ // Check whether the oat output file is writable, and open it for later. Also open a swap file,
+ // if a name is given.
bool OpenFile() {
bool create_file = !oat_unstripped_.empty(); // as opposed to using open file descriptor
if (create_file) {
@@ -984,25 +995,51 @@
oat_file_->Erase();
return false;
}
+
+ // Swap file handling.
+ //
+ // If the swap fd is not -1, we assume this is the file descriptor of an open but unlinked file
+ // that we can use for swap.
+ //
+ // If the swap fd is -1 and we have a swap-file string, open the given file as a swap file. We
+ // will immediately unlink to satisfy the swap fd assumption.
+ if (swap_fd_ == -1 && !swap_file_name_.empty()) {
+ std::unique_ptr<File> swap_file(OS::CreateEmptyFile(swap_file_name_.c_str()));
+ if (swap_file.get() == nullptr) {
+ PLOG(ERROR) << "Failed to create swap file: " << swap_file_name_;
+ return false;
+ }
+ swap_fd_ = swap_file->Fd();
+ swap_file->MarkUnchecked(); // We don't we to track this, it will be unlinked immediately.
+ swap_file->DisableAutoClose(); // We'll handle it ourselves, the File object will be
+ // released immediately.
+ unlink(swap_file_name_.c_str());
+ }
+
return true;
}
+ void EraseOatFile() {
+ DCHECK(oat_file_.get() != nullptr);
+ oat_file_->Erase();
+ oat_file_.reset();
+ }
+
// Set up the environment for compilation. Includes starting the runtime and loading/opening the
// boot class path.
bool Setup() {
TimingLogger::ScopedTiming t("dex2oat Setup", timings_);
RuntimeOptions runtime_options;
- std::vector<const DexFile*> boot_class_path;
art::MemMap::Init(); // For ZipEntry::ExtractToMemMap.
if (boot_image_option_.empty()) {
- size_t failure_count = OpenDexFiles(dex_filenames_, dex_locations_, boot_class_path);
- if (failure_count > 0) {
- LOG(ERROR) << "Failed to open some dex files: " << failure_count;
- return false;
- }
- runtime_options.push_back(std::make_pair("bootclasspath", &boot_class_path));
+ std::string boot_class_path = "-Xbootclasspath:";
+ boot_class_path += Join(dex_filenames_, ':');
+ runtime_options.push_back(std::make_pair(boot_class_path, nullptr));
+ std::string boot_class_path_locations = "-Xbootclasspath-locations:";
+ boot_class_path_locations += Join(dex_locations_, ':');
+ runtime_options.push_back(std::make_pair(boot_class_path_locations, nullptr));
} else {
- runtime_options.push_back(std::make_pair(boot_image_option_.c_str(), nullptr));
+ runtime_options.push_back(std::make_pair(boot_image_option_, nullptr));
}
for (size_t i = 0; i < runtime_args_.size(); i++) {
runtime_options.push_back(std::make_pair(runtime_args_[i], nullptr));
@@ -1080,25 +1117,32 @@
<< error_msg;
return false;
}
- if (!DexFile::OpenFromZip(*zip_archive.get(), zip_location_, &error_msg, &dex_files_)) {
+ if (!DexFile::OpenFromZip(*zip_archive.get(), zip_location_, &error_msg, &opened_dex_files_)) {
LOG(ERROR) << "Failed to open dex from file descriptor for zip file '" << zip_location_
<< "': " << error_msg;
return false;
}
+ for (auto& dex_file : opened_dex_files_) {
+ dex_files_.push_back(dex_file.get());
+ }
ATRACE_END();
} else {
- size_t failure_count = OpenDexFiles(dex_filenames_, dex_locations_, dex_files_);
+ size_t failure_count = OpenDexFiles(dex_filenames_, dex_locations_, &opened_dex_files_);
if (failure_count > 0) {
LOG(ERROR) << "Failed to open some dex files: " << failure_count;
return false;
}
+ for (auto& dex_file : opened_dex_files_) {
+ dex_files_.push_back(dex_file.get());
+ }
}
constexpr bool kSaveDexInput = false;
if (kSaveDexInput) {
for (size_t i = 0; i < dex_files_.size(); ++i) {
const DexFile* dex_file = dex_files_[i];
- std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%zd.dex", getpid(), i));
+ std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%zd.dex",
+ getpid(), i));
std::unique_ptr<File> tmp_file(OS::CreateEmptyFile(tmp_file_name.c_str()));
if (tmp_file.get() == nullptr) {
PLOG(ERROR) << "Failed to open file " << tmp_file_name
@@ -1120,11 +1164,25 @@
}
}
+ // If we use a swap file, ensure we are above the threshold to make it necessary.
+ if (swap_fd_ != -1) {
+ if (!UseSwap(image_, dex_files_)) {
+ close(swap_fd_);
+ swap_fd_ = -1;
+ LOG(INFO) << "Decided to run without swap.";
+ } else {
+ LOG(INFO) << "Accepted running with swap.";
+ }
+ }
+ // Note that dex2oat won't close the swap_fd_. The compiler driver's swap space will do that.
+
/*
* If we're not in interpret-only or verify-none mode, go ahead and compile small applications.
* Don't bother to check if we're doing the image.
*/
- if (!image_ && compiler_options_->IsCompilationEnabled() && compiler_kind_ == Compiler::kQuick) {
+ if (!image_ &&
+ compiler_options_->IsCompilationEnabled() &&
+ compiler_kind_ == Compiler::kQuick) {
size_t num_methods = 0;
for (size_t i = 0; i != dex_files_.size(); ++i) {
const DexFile* dex_file = dex_files_[i];
@@ -1150,9 +1208,13 @@
Thread* self = Thread::Current();
if (!boot_image_option_.empty()) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- std::vector<const DexFile*> class_path_files(dex_files_);
- OpenClassPathFiles(runtime_->GetClassPathString(), class_path_files);
+ OpenClassPathFiles(runtime_->GetClassPathString(), dex_files_, &class_path_files_);
ScopedObjectAccess soa(self);
+ std::vector<const DexFile*> class_path_files(dex_files_);
+ for (auto& class_path_file : class_path_files_) {
+ class_path_files.push_back(class_path_file.get());
+ }
+
for (size_t i = 0; i < class_path_files.size(); i++) {
class_linker->RegisterDexFile(*class_path_files[i]);
}
@@ -1175,11 +1237,11 @@
thread_count_,
dump_stats_,
dump_passes_,
+ dump_cfg_file_name_,
compiler_phases_timings_.get(),
+ swap_fd_,
profile_file_));
- driver_->GetCompiler()->SetBitcodeFileName(*driver_, bitcode_filename_);
-
driver_->CompileAll(class_loader, dex_files_, timings_);
}
@@ -1301,7 +1363,6 @@
if (!driver_->WriteElf(android_root_, is_host_, dex_files_, oat_writer.get(),
oat_file_.get())) {
LOG(ERROR) << "Failed to write ELF file " << oat_file_->GetPath();
- oat_file_->Erase();
return false;
}
}
@@ -1338,7 +1399,7 @@
std::unique_ptr<File> in(OS::OpenFileForReading(oat_unstripped_.c_str()));
std::unique_ptr<File> out(OS::CreateEmptyFile(oat_stripped_.c_str()));
size_t buffer_size = 8192;
- std::unique_ptr<uint8_t> buffer(new uint8_t[buffer_size]);
+ std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
while (true) {
int bytes_read = TEMP_FAILURE_RETRY(read(in->Fd(), buffer.get(), buffer_size));
if (bytes_read <= 0) {
@@ -1347,52 +1408,15 @@
bool write_ok = out->WriteFully(buffer.get(), bytes_read);
CHECK(write_ok);
}
- if (kUsePortableCompiler) {
- oat_file_.reset(out.release());
- } else {
- if (out->FlushCloseOrErase() != 0) {
- PLOG(ERROR) << "Failed to flush and close copied oat file: " << oat_stripped_;
- return false;
- }
+ if (out->FlushCloseOrErase() != 0) {
+ PLOG(ERROR) << "Failed to flush and close copied oat file: " << oat_stripped_;
+ return false;
}
VLOG(compiler) << "Oat file copied successfully (stripped): " << oat_stripped_;
}
return true;
}
- // Run the ElfStripper. Currently only relevant for the portable compiler.
- bool Strip() {
- if (kUsePortableCompiler) {
- // Portable includes debug symbols unconditionally. If we are not supposed to create them,
- // strip them now. Quick generates debug symbols only when the flag(s) are set.
- if (!compiler_options_->GetIncludeDebugSymbols()) {
- CHECK(oat_file_.get() != nullptr && oat_file_->IsOpened());
-
- TimingLogger::ScopedTiming t("dex2oat ElfStripper", timings_);
- // Strip unneeded sections for target
- off_t seek_actual = lseek(oat_file_->Fd(), 0, SEEK_SET);
- CHECK_EQ(0, seek_actual);
- std::string error_msg;
- if (!ElfFile::Strip(oat_file_.get(), &error_msg)) {
- LOG(ERROR) << "Failed to strip elf file: " << error_msg;
- oat_file_->Erase();
- return false;
- }
-
- if (!FlushCloseOatFile()) {
- return false;
- }
-
- // We wrote the oat file successfully, and want to keep it.
- VLOG(compiler) << "Oat file written successfully (stripped): " << oat_location_;
- } else {
- VLOG(compiler) << "Oat file written successfully without stripping: " << oat_location_;
- }
- }
-
- return true;
- }
-
bool FlushOatFile() {
if (oat_file_.get() != nullptr) {
TimingLogger::ScopedTiming t2("dex2oat Flush ELF", timings_);
@@ -1442,7 +1466,8 @@
private:
static size_t OpenDexFiles(const std::vector<const char*>& dex_filenames,
const std::vector<const char*>& dex_locations,
- std::vector<const DexFile*>& dex_files) {
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+ DCHECK(dex_files != nullptr) << "OpenDexFiles out-param is NULL";
size_t failure_count = 0;
for (size_t i = 0; i < dex_filenames.size(); i++) {
const char* dex_filename = dex_filenames[i];
@@ -1453,7 +1478,7 @@
LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'";
continue;
}
- if (!DexFile::Open(dex_filename, dex_location, &error_msg, &dex_files)) {
+ if (!DexFile::Open(dex_filename, dex_location, &error_msg, dex_files)) {
LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg;
++failure_count;
}
@@ -1473,10 +1498,12 @@
return false;
}
- // Appends to dex_files any elements of class_path that it doesn't already
- // contain. This will open those dex files as necessary.
+ // Appends to opened_dex_files any elements of class_path that dex_files
+ // doesn't already contain. This will open those dex files as necessary.
static void OpenClassPathFiles(const std::string& class_path,
- std::vector<const DexFile*>& dex_files) {
+ std::vector<const DexFile*> dex_files,
+ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
+ DCHECK(opened_dex_files != nullptr) << "OpenClassPathFiles out-param is NULL";
std::vector<std::string> parsed;
Split(class_path, ':', &parsed);
// Take Locks::mutator_lock_ so that lock ordering on the ClassLinker::dex_lock_ is maintained.
@@ -1486,7 +1513,7 @@
continue;
}
std::string error_msg;
- if (!DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg, &dex_files)) {
+ if (!DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg, opened_dex_files)) {
LOG(WARNING) << "Failed to open dex file '" << parsed[i] << "': " << error_msg;
}
}
@@ -1609,20 +1636,9 @@
}
void LogCompletionTime() {
- std::ostringstream mallinfostr;
-#ifdef HAVE_MALLOC_H
- struct mallinfo info = mallinfo();
- const size_t allocated_space = static_cast<size_t>(info.uordblks);
- const size_t free_space = static_cast<size_t>(info.fordblks);
- mallinfostr << " native alloc=" << PrettySize(allocated_space) << " free="
- << PrettySize(free_space);
-#endif
- const ArenaPool* arena_pool = driver_->GetArenaPool();
- gc::Heap* heap = Runtime::Current()->GetHeap();
LOG(INFO) << "dex2oat took " << PrettyDuration(NanoTime() - start_ns_)
- << " (threads: " << thread_count_ << ")"
- << " arena alloc=" << PrettySize(arena_pool->GetBytesAllocated())
- << " java alloc=" << PrettySize(heap->GetBytesAllocated()) << mallinfostr.str();
+ << " (threads: " << thread_count_ << ") "
+ << driver_->GetMemoryUsageString(kIsDebugBuild || VLOG_IS_ON(compiler));
}
std::unique_ptr<CompilerOptions> compiler_options_;
@@ -1637,6 +1653,9 @@
DexFileToMethodInlinerMap method_inliner_map_;
std::unique_ptr<QuickCompilerCallbacks> callbacks_;
+ // Ownership for the class path files.
+ std::vector<std::unique_ptr<const DexFile>> class_path_files_;
+
// Not a unique_ptr as we want to just exit on non-debug builds, not bringing the runtime down
// in an orderly fashion. The destructor takes care of deleting this.
Runtime* runtime_;
@@ -1650,7 +1669,6 @@
std::string oat_location_;
std::string oat_filename_;
int oat_fd_;
- std::string bitcode_filename_;
std::vector<const char*> dex_filenames_;
std::vector<const char*> dex_locations_;
int zip_fd_;
@@ -1670,12 +1688,16 @@
bool is_host_;
std::string android_root_;
std::vector<const DexFile*> dex_files_;
+ std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
std::unique_ptr<CompilerDriver> driver_;
std::vector<std::string> verbose_methods_;
bool dump_stats_;
bool dump_passes_;
bool dump_timing_;
bool dump_slow_timing_;
+ std::string dump_cfg_file_name_;
+ std::string swap_file_name_;
+ int swap_fd_;
std::string profile_file_; // Profile file to use
TimingLogger* timings_;
std::unique_ptr<CumulativeLogger> compiler_phases_timings_;
@@ -1684,7 +1706,6 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
};
-const unsigned int WatchDog::kWatchDogWarningSeconds;
const unsigned int WatchDog::kWatchDogTimeoutSeconds;
static void b13564922() {
@@ -1712,6 +1733,7 @@
// Create the boot.oat.
if (!dex2oat.CreateOatFile()) {
+ dex2oat.EraseOatFile();
return EXIT_FAILURE;
}
@@ -1737,11 +1759,6 @@
return EXIT_FAILURE;
}
- // Strip, if necessary.
- if (!dex2oat.Strip()) {
- return EXIT_FAILURE;
- }
-
// FlushClose again, as stripping might have re-opened the oat file.
if (!dex2oat.FlushCloseOatFile()) {
return EXIT_FAILURE;
@@ -1756,6 +1773,7 @@
// Create the app oat.
if (!dex2oat.CreateOatFile()) {
+ dex2oat.EraseOatFile();
return EXIT_FAILURE;
}
@@ -1781,11 +1799,6 @@
return EXIT_FAILURE;
}
- // Strip, if necessary.
- if (!dex2oat.Strip()) {
- return EXIT_FAILURE;
- }
-
// Flush and close the file.
if (!dex2oat.FlushCloseOatFile()) {
return EXIT_FAILURE;
@@ -1813,6 +1826,7 @@
LOG(INFO) << CommandLine();
if (!dex2oat.Setup()) {
+ dex2oat.EraseOatFile();
return EXIT_FAILURE;
}
diff --git a/disassembler/Android.mk b/disassembler/Android.mk
index 3ad2941..c9aa8c8 100644
--- a/disassembler/Android.mk
+++ b/disassembler/Android.mk
@@ -23,6 +23,7 @@
disassembler_arm.cc \
disassembler_arm64.cc \
disassembler_mips.cc \
+ disassembler_mips64.cc \
disassembler_x86.cc
# $(1): target or host
@@ -83,7 +84,7 @@
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
- include external/libcxx/libcxx.mk
+ LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
# For disassembler_arm64.
ifeq ($$(art_ndebug_or_debug),debug)
LOCAL_SHARED_LIBRARIES += libvixld
diff --git a/disassembler/disassembler.cc b/disassembler/disassembler.cc
index bf68204..fbc8dbb 100644
--- a/disassembler/disassembler.cc
+++ b/disassembler/disassembler.cc
@@ -23,6 +23,7 @@
#include "disassembler_arm.h"
#include "disassembler_arm64.h"
#include "disassembler_mips.h"
+#include "disassembler_mips64.h"
#include "disassembler_x86.h"
namespace art {
@@ -34,6 +35,8 @@
return new arm64::DisassemblerArm64(options);
} else if (instruction_set == kMips) {
return new mips::DisassemblerMips(options);
+ } else if (instruction_set == kMips64) {
+ return new mips64::DisassemblerMips64(options);
} else if (instruction_set == kX86) {
return new x86::DisassemblerX86(options, false);
} else if (instruction_set == kX86_64) {
diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index 9243b1a..31e653b 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -21,6 +21,7 @@
#include <ostream>
#include <sstream>
+#include "arch/arm/registers_arm.h"
#include "base/logging.h"
#include "base/stringprintf.h"
#include "thread.h"
@@ -148,15 +149,15 @@
ThumbRegister(uint16_t instruction, uint16_t at_bit) : ArmRegister((instruction >> at_bit) & 0x7) {}
};
-struct Rm {
- explicit Rm(uint32_t instruction) : shift((instruction >> 4) & 0xff), rm(instruction & 0xf) {}
- uint32_t shift;
+struct RmLslImm2 {
+ explicit RmLslImm2(uint32_t instr) : imm2((instr >> 4) & 0x3), rm(instr & 0xf) {}
+ uint32_t imm2;
ArmRegister rm;
};
-std::ostream& operator<<(std::ostream& os, const Rm& r) {
+std::ostream& operator<<(std::ostream& os, const RmLslImm2& r) {
os << r.rm;
- if (r.shift != 0) {
- os << "-shift-" << r.shift; // TODO
+ if (r.imm2 != 0) {
+ os << ", lsl #" << r.imm2;
}
return os;
}
@@ -397,7 +398,74 @@
uint64_t bit_a = (imm8 >> 7) & 1;
uint64_t bit_b = (imm8 >> 6) & 1;
uint64_t slice = imm8 & 0x3f;
- return (bit_a << 31) | ((UINT64_C(1) << 62) - (bit_b << 54)) | (slice << 48);
+ return (bit_a << 63) | ((UINT64_C(1) << 62) - (bit_b << 54)) | (slice << 48);
+}
+
+enum T2LitType {
+ kT2LitInvalid,
+ kT2LitUByte,
+ kT2LitSByte,
+ kT2LitUHalf,
+ kT2LitSHalf,
+ kT2LitUWord,
+ kT2LitSWord,
+ kT2LitHexWord,
+ kT2LitULong,
+ kT2LitSLong,
+ kT2LitHexLong,
+};
+std::ostream& operator<<(std::ostream& os, T2LitType type) {
+ return os << static_cast<int>(type);
+}
+
+void DumpThumb2Literal(std::ostream& args, const uint8_t* instr_ptr, uint32_t U, uint32_t imm32,
+ T2LitType type) {
+ // Literal offsets (imm32) are not required to be aligned so we may need unaligned access.
+ typedef const int16_t unaligned_int16_t __attribute__ ((aligned (1)));
+ typedef const uint16_t unaligned_uint16_t __attribute__ ((aligned (1)));
+ typedef const int32_t unaligned_int32_t __attribute__ ((aligned (1)));
+ typedef const uint32_t unaligned_uint32_t __attribute__ ((aligned (1)));
+ typedef const int64_t unaligned_int64_t __attribute__ ((aligned (1)));
+ typedef const uint64_t unaligned_uint64_t __attribute__ ((aligned (1)));
+
+ uintptr_t pc = RoundDown(reinterpret_cast<intptr_t>(instr_ptr) + 4, 4);
+ uintptr_t lit_adr = U ? pc + imm32 : pc - imm32;
+ args << " ; ";
+ switch (type) {
+ case kT2LitUByte:
+ args << *reinterpret_cast<const uint8_t*>(lit_adr);
+ break;
+ case kT2LitSByte:
+ args << *reinterpret_cast<const int8_t*>(lit_adr);
+ break;
+ case kT2LitUHalf:
+ args << *reinterpret_cast<const unaligned_uint16_t*>(lit_adr);
+ break;
+ case kT2LitSHalf:
+ args << *reinterpret_cast<const unaligned_int16_t*>(lit_adr);
+ break;
+ case kT2LitUWord:
+ args << *reinterpret_cast<const unaligned_uint32_t*>(lit_adr);
+ break;
+ case kT2LitSWord:
+ args << *reinterpret_cast<const unaligned_int32_t*>(lit_adr);
+ break;
+ case kT2LitHexWord:
+ args << StringPrintf("0x%08x", *reinterpret_cast<const unaligned_uint32_t*>(lit_adr));
+ break;
+ case kT2LitULong:
+ args << *reinterpret_cast<const unaligned_uint64_t*>(lit_adr);
+ break;
+ case kT2LitSLong:
+ args << *reinterpret_cast<const unaligned_int64_t*>(lit_adr);
+ break;
+ case kT2LitHexLong:
+ args << StringPrintf("0x%" PRIx64, *reinterpret_cast<unaligned_int64_t*>(lit_adr));
+ break;
+ default:
+ LOG(FATAL) << "Invalid type: " << type;
+ break;
+ }
}
size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) {
@@ -756,10 +824,7 @@
args << d << ", [" << Rn << ", #" << ((U == 1) ? "" : "-")
<< (imm8 << 2) << "]";
if (Rn.r == 15 && U == 1) {
- intptr_t lit_adr = reinterpret_cast<intptr_t>(instr_ptr);
- lit_adr = RoundDown(lit_adr, 4) + 4 + (imm8 << 2);
- typedef const int64_t unaligned_int64_t __attribute__ ((aligned (2)));
- args << StringPrintf(" ; 0x%" PRIx64, *reinterpret_cast<unaligned_int64_t*>(lit_adr));
+ DumpThumb2Literal(args, instr_ptr, U, imm8 << 2, kT2LitHexLong);
}
} else if (Rn.r == 13 && W == 1 && U == L) { // VPUSH/VPOP
opcode << (L == 1 ? "vpop" : "vpush");
@@ -1227,164 +1292,141 @@
break;
case 3:
switch (op2) {
- case 0x00: case 0x02: case 0x04: case 0x06: // 000xxx0
- case 0x08: case 0x09: case 0x0A: case 0x0C: case 0x0E: {
- // Store single data item
- // |111|11|100|000|0|0000|1111|110000|000000|
- // |5 3|21|098|765|4|3 0|5 2|10 6|5 0|
- // |---|--|---|---|-|----|----|------|------|
- // |332|22|222|222|2|1111|1111|110000|000000|
- // |1 9|87|654|321|0|9 6|5 2|10 6|5 0|
- // |---|--|---|---|-|----|----|------|------|
- // |111|11|000|op3|0| | | op4 | |
- uint32_t op3 = (instr >> 21) & 7;
- // uint32_t op4 = (instr >> 6) & 0x3F;
- switch (op3) {
- case 0x0: case 0x4: {
- // {ST,LD}RB Rt,[Rn,#+/-imm12] - 111 11 00 0 1 00 0 nnnn tttt 1 PUWii ii iiii
- // {ST,LD}RB Rt,[Rn,#+/-imm8] - 111 11 00 0 0 00 0 nnnn tttt 1 PUWii ii iiii
- // {ST,LD}RB Rt,[Rn,Rm,lsl #imm2] - 111 11 00 0 0 00 0 nnnn tttt 0 00000 ii mmmm
- ArmRegister Rn(instr, 16);
- ArmRegister Rt(instr, 12);
- opcode << (HasBitSet(instr, 20) ? "ldrb" : "strb");
- if (HasBitSet(instr, 23)) {
- uint32_t imm12 = instr & 0xFFF;
- args << Rt << ", [" << Rn << ",#" << imm12 << "]";
- } else if ((instr & 0x800) != 0) {
- uint32_t imm8 = instr & 0xFF;
- args << Rt << ", [" << Rn << ",#" << imm8 << "]";
- } else {
- uint32_t imm2 = (instr >> 4) & 3;
- ArmRegister Rm(instr, 0);
- args << Rt << ", [" << Rn << ", " << Rm;
- if (imm2 != 0) {
- args << ", " << "lsl #" << imm2;
- }
- args << "]";
- }
- break;
- }
- case 0x1: case 0x5: {
- // STRH Rt,[Rn,#+/-imm12] - 111 11 00 0 1 01 0 nnnn tttt 1 PUWii ii iiii
- // STRH Rt,[Rn,#+/-imm8] - 111 11 00 0 0 01 0 nnnn tttt 1 PUWii ii iiii
- // STRH Rt,[Rn,Rm,lsl #imm2] - 111 11 00 0 0 01 0 nnnn tttt 0 00000 ii mmmm
- ArmRegister Rn(instr, 16);
- ArmRegister Rt(instr, 12);
- opcode << "strh";
- if (HasBitSet(instr, 23)) {
- uint32_t imm12 = instr & 0xFFF;
- args << Rt << ", [" << Rn << ",#" << imm12 << "]";
- } else if ((instr & 0x800) != 0) {
- uint32_t imm8 = instr & 0xFF;
- args << Rt << ", [" << Rn << ",#" << imm8 << "]";
- } else {
- uint32_t imm2 = (instr >> 4) & 3;
- ArmRegister Rm(instr, 0);
- args << Rt << ", [" << Rn << ", " << Rm;
- if (imm2 != 0) {
- args << ", " << "lsl #" << imm2;
- }
- args << "]";
- }
- break;
- }
- case 0x2: case 0x6: {
- ArmRegister Rn(instr, 16);
- ArmRegister Rt(instr, 12);
- if (op3 == 2) {
- if ((instr & 0x800) != 0) {
- // STR Rt, [Rn, #imm8] - 111 11 000 010 0 nnnn tttt 1PUWiiiiiiii
- uint32_t P = (instr >> 10) & 1;
- uint32_t U = (instr >> 9) & 1;
- uint32_t W = (instr >> 8) & 1;
- uint32_t imm8 = instr & 0xFF;
- int32_t imm32 = (imm8 << 24) >> 24; // sign-extend imm8
- if (Rn.r == 13 && P == 1 && U == 0 && W == 1 && imm32 == 4) {
- opcode << "push";
- args << "{" << Rt << "}";
- } else if (Rn.r == 15 || (P == 0 && W == 0)) {
- opcode << "UNDEFINED";
- } else {
- if (P == 1 && U == 1 && W == 0) {
- opcode << "strt";
- } else {
- opcode << "str";
- }
- args << Rt << ", [" << Rn;
- if (P == 0 && W == 1) {
- args << "], #" << imm32;
- } else {
- args << ", #" << imm32 << "]";
- if (W == 1) {
- args << "!";
- }
- }
- }
- } else {
- // STR Rt, [Rn, Rm, LSL #imm2] - 111 11 000 010 0 nnnn tttt 000000iimmmm
- ArmRegister Rm(instr, 0);
- uint32_t imm2 = (instr >> 4) & 3;
- opcode << "str.w";
- args << Rt << ", [" << Rn << ", " << Rm;
- if (imm2 != 0) {
- args << ", lsl #" << imm2;
- }
- args << "]";
- }
- } else if (op3 == 6) {
- // STR.W Rt, [Rn, #imm12] - 111 11 000 110 0 nnnn tttt iiiiiiiiiiii
- uint32_t imm12 = instr & 0xFFF;
- opcode << "str.w";
- args << Rt << ", [" << Rn << ", #" << imm12 << "]";
- }
- break;
- }
- }
-
+ case 0x07: case 0x0F: case 0x17: case 0x1F: { // Explicitly UNDEFINED, A6.3.
+ opcode << "UNDEFINED";
break;
}
- case 0x03: case 0x0B: case 0x11: case 0x13: case 0x19: case 0x1B: { // 00xx011
- // Load byte/halfword
- // |111|11|10|0 0|00|0|0000|1111|110000|000000|
- // |5 3|21|09|8 7|65|4|3 0|5 2|10 6|5 0|
- // |---|--|--|---|--|-|----|----|------|------|
- // |332|22|22|2 2|22|2|1111|1111|110000|000000|
- // |1 9|87|65|4 3|21|0|9 6|5 2|10 6|5 0|
- // |---|--|--|---|--|-|----|----|------|------|
- // |111|11|00|op3|01|1| Rn | Rt | op4 | |
- // |111|11| op2 | | | imm12 |
- uint32_t op3 = (instr >> 23) & 3;
+ case 0x06: case 0x0E: { // "Store single data item" undefined opcodes, A6.3.10.
+ opcode << "UNDEFINED [store]";
+ break;
+ }
+ case 0x15: case 0x1D: { // "Load word" undefined opcodes, A6.3.7.
+ opcode << "UNDEFINED [load]";
+ break;
+ }
+ case 0x10: case 0x12: case 0x14: case 0x16: case 0x18: case 0x1A: case 0x1C: case 0x1E: {
+ opcode << "UNKNOWN " << op2 << " [SIMD]";
+ break;
+ }
+ case 0x01: case 0x00: case 0x09: case 0x08: // {LD,ST}RB{,T}
+ case 0x03: case 0x02: case 0x0B: case 0x0A: // {LD,ST}RH{,T}
+ case 0x05: case 0x04: case 0x0D: case 0x0C: // {LD,ST}R{,T}
+ case 0x11: case 0x19: // LDRSB{,T} (no signed store)
+ case 0x13: case 0x1B: { // LDRSH{,T} (no signed store)
+ // Load:
+ // (Store is the same except that l==0 and always s==0 below.)
+ // 00s.whl (sign, word, half, load)
+ // LDR{S}B imm12: 11111|00s1001| Rn | Rt |imm12 (0x09)
+ // LDR{S}B imm8: 11111|00s0001| Rn | Rt |1PUW|imm8 (0x01)
+ // LDR{S}BT imm8: 11111|00s0001| Rn | Rt |1110|imm8 (0x01)
+ // LDR{S}B lit: 11111|00sU001|1111| Rt |imm12 (0x01/0x09)
+ // LDR{S}B reg: 11111|00s0001| Rn | Rt |000000|imm2| Rm (0x01)
+ // LDR{S}H imm12: 11111|00s1011| Rn | Rt |imm12 (0x0B)
+ // LDR{S}H imm8: 11111|00s0011| Rn | Rt |1PUW|imm8 (0x03)
+ // LDR{S}HT imm8: 11111|00s0011| Rn | Rt |1110|imm8 (0x03)
+ // LDR{S}H lit: 11111|00sU011|1111| Rt |imm12 (0x03/0x0B)
+ // LDR{S}H reg: 11111|00s0011| Rn | Rt |000000|imm2| Rm (0x03)
+ // LDR imm12: 11111|0001101| Rn | Rt |imm12 (0x0D)
+ // LDR imm8: 11111|0000101| Rn | Rt |1PUW|imm8 (0x05)
+ // LDRT imm8: 11111|0000101| Rn | Rt |1110|imm8 (0x05)
+ // LDR lit: 11111|000U101|1111| Rt |imm12 (0x05/0x0D)
+ // LDR reg: 11111|0000101| Rn | Rt |000000|imm2| Rm (0x05)
+ //
+ // If Rt == 15, instead of load we have preload:
+ // PLD{W} imm12: 11111|00010W1| Rn |1111|imm12 (0x09/0x0B)
+ // PLD{W} imm8: 11111|00000W1| Rn |1111|1100|imm8 (0x01/0x03); -imm8
+ // PLD lit: 11111|000U001|1111|1111|imm12 (0x01/0x09)
+ // PLD{W} reg: 11111|00000W1| Rn |1111|000000|imm2| Rm (0x01/0x03)
+ // PLI imm12: 11111|0011001| Rn |1111|imm12 (0x19)
+ // PLI imm8: 11111|0010001| Rn |1111|1100|imm8 (0x11); -imm8
+ // PLI lit: 11111|001U001|1111|1111|imm12 (0x01/0x09)
+ // PLI reg: 11111|0010001| Rn |1111|000000|imm2| Rm (0x01/0x03)
+
+ bool is_load = HasBitSet(instr, 20);
+ bool is_half = HasBitSet(instr, 21); // W for PLD/PLDW.
+ bool is_word = HasBitSet(instr, 22);
+ bool is_signed = HasBitSet(instr, 24);
ArmRegister Rn(instr, 16);
ArmRegister Rt(instr, 12);
- if (Rt.r != 15) {
- if (op3 == 1) {
- // LDRH.W Rt, [Rn, #imm12] - 111 11 00 01 011 nnnn tttt iiiiiiiiiiii
- uint32_t imm12 = instr & 0xFFF;
- opcode << "ldrh.w";
- args << Rt << ", [" << Rn << ", #" << imm12 << "]";
- if (Rn.r == 9) {
- args << " ; ";
- Thread::DumpThreadOffset<4>(args, imm12);
- } else if (Rn.r == 15) {
- intptr_t lit_adr = reinterpret_cast<intptr_t>(instr_ptr);
- lit_adr = RoundDown(lit_adr, 4) + 4 + imm12;
- args << StringPrintf(" ; 0x%08x", *reinterpret_cast<int32_t*>(lit_adr));
+ uint32_t imm12 = instr & 0xFFF;
+ uint32_t U = (instr >> 23) & 1; // U for imm12
+ uint32_t imm8 = instr & 0xFF;
+ uint32_t op4 = (instr >> 8) & 0xF; // 1PUW for imm8
+ if (Rt.r == PC && is_load && !is_word) {
+ // PLD, PLDW, PLI
+ const char* pld_pli = (is_signed ? "pli" : "pld");
+ const char* w = (is_half ? "w" : "");
+ if (is_signed && !is_half) {
+ opcode << "UNDEFINED [PLI+W]";
+ } else if (Rn.r == PC || U != 0u) {
+ opcode << pld_pli << w;
+ args << "[" << Rn << ", #" << (U != 0u ? "" : "-") << imm12 << "]";
+ if (Rn.r == PC && is_half) {
+ args << " (UNPREDICTABLE)";
}
- } else if (op3 == 3) {
- // LDRSH.W Rt, [Rn, #imm12] - 111 11 00 11 011 nnnn tttt iiiiiiiiiiii
- // LDRSB.W Rt, [Rn, #imm12] - 111 11 00 11 001 nnnn tttt iiiiiiiiiiii
- uint32_t imm12 = instr & 0xFFF;
- opcode << (HasBitSet(instr, 20) ? "ldrsb.w" : "ldrsh.w");
- args << Rt << ", [" << Rn << ", #" << imm12 << "]";
- if (Rn.r == 9) {
- args << " ; ";
- Thread::DumpThreadOffset<4>(args, imm12);
- } else if (Rn.r == 15) {
- intptr_t lit_adr = reinterpret_cast<intptr_t>(instr_ptr);
- lit_adr = RoundDown(lit_adr, 4) + 4 + imm12;
- args << StringPrintf(" ; 0x%08x", *reinterpret_cast<int32_t*>(lit_adr));
- }
+ } else if ((instr & 0xFC0) == 0) {
+ opcode << pld_pli << w;
+ RmLslImm2 Rm(instr);
+ args << "[" << Rn << ", " << Rm << "]";
+ } else if (op4 == 0xC) {
+ opcode << pld_pli << w;
+ args << "[" << Rn << ", #-" << imm8 << "]";
+ } else {
+ opcode << "UNDEFINED [~" << pld_pli << "]";
}
+ break;
+ }
+ const char* ldr_str = is_load ? "ldr" : "str";
+ const char* sign = is_signed ? "s" : "";
+ const char* type = is_word ? "" : is_half ? "h" : "b";
+ bool unpred = (Rt.r == SP && !is_word) || (Rt.r == PC && !is_load);
+ if (Rn.r == PC && !is_load) {
+ opcode << "UNDEFINED [STR-lit]";
+ unpred = false;
+ } else if (Rn.r == PC || U != 0u) {
+ // Load/store with imm12 (load literal if Rn.r == PC; there's no store literal).
+ opcode << ldr_str << sign << type << ".w";
+ args << Rt << ", [" << Rn << ", #" << (U != 0u ? "" : "-") << imm12 << "]";
+ if (Rn.r == TR && is_load) {
+ args << " ; ";
+ Thread::DumpThreadOffset<4>(args, imm12);
+ } else if (Rn.r == PC) {
+ T2LitType lit_type[] = {
+ kT2LitUByte, kT2LitUHalf, kT2LitHexWord, kT2LitInvalid,
+ kT2LitUByte, kT2LitUHalf, kT2LitHexWord, kT2LitInvalid,
+ kT2LitSByte, kT2LitSHalf, kT2LitInvalid, kT2LitInvalid,
+ kT2LitSByte, kT2LitSHalf, kT2LitInvalid, kT2LitInvalid,
+ };
+ DCHECK_LT(op2 >> 1, arraysize(lit_type));
+ DCHECK_NE(lit_type[op2 >> 1], kT2LitInvalid);
+ DumpThumb2Literal(args, instr_ptr, U, imm12, lit_type[op2 >> 1]);
+ }
+ } else if ((instr & 0xFC0) == 0) {
+ opcode << ldr_str << sign << type << ".w";
+ RmLslImm2 Rm(instr);
+ args << Rt << ", [" << Rn << ", " << Rm << "]";
+ unpred = unpred || (Rm.rm.r == SP) || (Rm.rm.r == PC);
+ } else if (is_word && Rn.r == SP && imm8 == 4 && op4 == (is_load ? 0xB : 0xD)) {
+ opcode << (is_load ? "pop" : "push") << ".w";
+ args << Rn;
+ unpred = unpred || (Rn.r == SP);
+ } else if ((op4 & 5) == 0) {
+ opcode << "UNDEFINED [P = W = 0 for " << ldr_str << "]";
+ unpred = false;
+ } else {
+ uint32_t P = (instr >> 10) & 1;
+ U = (instr >> 9) & 1;
+ uint32_t W = (instr >> 8) & 1;
+ bool pre_index = (P != 0 && W == 1);
+ bool post_index = (P == 0 && W == 1);
+ const char* t = (P != 0 && U != 0 && W == 0) ? "t" : ""; // Unprivileged load/store?
+ opcode << ldr_str << sign << type << t << ".w";
+ args << Rt << ", [" << Rn << (post_index ? "]" : "") << ", #" << (U != 0 ? "" : "-")
+ << imm8 << (post_index ? "" : "]") << (pre_index ? "!" : "");
+ unpred = (W != 0 && Rn.r == Rt.r);
+ }
+ if (unpred) {
+ args << " (UNPREDICTABLE)";
}
break;
}
@@ -1413,75 +1455,6 @@
} // else unknown instruction
break;
}
- case 0x05: case 0x0D: case 0x15: case 0x1D: { // 00xx101
- // Load word
- // |111|11|10|0 0|00|0|0000|1111|110000|000000|
- // |5 3|21|09|8 7|65|4|3 0|5 2|10 6|5 0|
- // |---|--|--|---|--|-|----|----|------|------|
- // |332|22|22|2 2|22|2|1111|1111|110000|000000|
- // |1 9|87|65|4 3|21|0|9 6|5 2|10 6|5 0|
- // |---|--|--|---|--|-|----|----|------|------|
- // |111|11|00|op3|10|1| Rn | Rt | op4 | |
- // |111|11| op2 | | | imm12 |
- uint32_t op3 = (instr >> 23) & 3;
- uint32_t op4 = (instr >> 6) & 0x3F;
- ArmRegister Rn(instr, 16);
- ArmRegister Rt(instr, 12);
- if (op3 == 1 || Rn.r == 15) {
- // LDR.W Rt, [Rn, #imm12] - 111 11 00 00 101 nnnn tttt iiiiiiiiiiii
- // LDR.W Rt, [PC, #imm12] - 111 11 00 0x 101 1111 tttt iiiiiiiiiiii
- uint32_t imm12 = instr & 0xFFF;
- opcode << "ldr.w";
- args << Rt << ", [" << Rn << ", #" << imm12 << "]";
- if (Rn.r == 9) {
- args << " ; ";
- Thread::DumpThreadOffset<4>(args, imm12);
- } else if (Rn.r == 15) {
- intptr_t lit_adr = reinterpret_cast<intptr_t>(instr_ptr);
- lit_adr = RoundDown(lit_adr, 4) + 4 + imm12;
- args << StringPrintf(" ; 0x%08x", *reinterpret_cast<int32_t*>(lit_adr));
- }
- } else if (op4 == 0) {
- // LDR.W Rt, [Rn, Rm{, LSL #imm2}] - 111 11 00 00 101 nnnn tttt 000000iimmmm
- uint32_t imm2 = (instr >> 4) & 0xF;
- ArmRegister rm(instr, 0);
- opcode << "ldr.w";
- args << Rt << ", [" << Rn << ", " << rm;
- if (imm2 != 0) {
- args << ", lsl #" << imm2;
- }
- args << "]";
- } else {
- bool p = (instr & (1 << 10)) != 0;
- bool w = (instr & (1 << 8)) != 0;
- bool u = (instr & (1 << 9)) != 0;
- if (p && u && !w) {
- // LDRT Rt, [Rn, #imm8] - 111 11 00 00 101 nnnn tttt 1110iiiiiiii
- uint32_t imm8 = instr & 0xFF;
- opcode << "ldrt";
- args << Rt << ", [" << Rn << ", #" << imm8 << "]";
- } else if (Rn.r == 13 && !p && u && w && (instr & 0xff) == 4) {
- // POP
- opcode << "pop";
- args << "{" << Rt << "}";
- } else {
- bool wback = !p || w;
- uint32_t offset = (instr & 0xff);
- opcode << "ldr.w";
- args << Rt << ",";
- if (p && !wback) {
- args << "[" << Rn << ", #" << offset << "]";
- } else if (p && wback) {
- args << "[" << Rn << ", #" << offset << "]!";
- } else if (!p && wback) {
- args << "[" << Rn << "], #" << offset;
- } else {
- LOG(FATAL) << p << " " << w;
- }
- }
- }
- break;
- }
default: // more formats
if ((op2 >> 4) == 2) { // 010xxxx
// data processing (register)
@@ -1498,7 +1471,7 @@
} else if ((op2 >> 3) == 6) { // 0110xxx
// Multiply, multiply accumulate, and absolute difference
op1 = (instr >> 20) & 0x7;
- op2 = (instr >> 4) & 0x2;
+ op2 = (instr >> 4) & 0x1;
ArmRegister Ra(instr, 12);
ArmRegister Rn(instr, 16);
ArmRegister Rm(instr, 0);
@@ -1808,6 +1781,23 @@
DumpBranchTarget(args, instr_ptr + 4, imm32);
break;
}
+ case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
+ case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2E: case 0x2F: {
+ opcode << "push";
+ args << RegisterList((instr & 0xFF) | ((instr & 0x100) << 6));
+ break;
+ }
+ case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67:
+ case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6D: case 0x6E: case 0x6F: {
+ opcode << "pop";
+ args << RegisterList((instr & 0xFF) | ((instr & 0x100) << 7));
+ break;
+ }
+ case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: {
+ opcode << "bkpt";
+ args << "#" << (instr & 0xFF);
+ break;
+ }
case 0x50: case 0x51: // 101000x
case 0x52: case 0x53: // 101001x
case 0x56: case 0x57: { // 101011x
diff --git a/disassembler/disassembler_arm64.cc b/disassembler/disassembler_arm64.cc
index bd3bebf..4ff44b4 100644
--- a/disassembler/disassembler_arm64.cc
+++ b/disassembler/disassembler_arm64.cc
@@ -18,7 +18,7 @@
#include <inttypes.h>
-#include <ostream>
+#include <sstream>
#include "base/logging.h"
#include "base/stringprintf.h"
@@ -27,22 +27,23 @@
namespace art {
namespace arm64 {
+// This enumeration should mirror the declarations in
+// runtime/arch/arm64/registers_arm64.h. We do not include that file to
+// avoid a dependency on libart.
+enum {
+ TR = 18,
+ ETR = 21,
+ IP0 = 16,
+ IP1 = 17,
+ FP = 29,
+ LR = 30
+};
+
void CustomDisassembler::AppendRegisterNameToOutput(
const vixl::Instruction* instr,
const vixl::CPURegister& reg) {
USE(instr);
if (reg.IsRegister()) {
- // This enumeration should mirror the declarations in
- // runtime/arch/arm64/registers_arm64.h. We do not include that file to
- // avoid a dependency on libart.
- enum {
- TR = 18,
- ETR = 21,
- IP0 = 16,
- IP1 = 17,
- FP = 29,
- LR = 30
- };
switch (reg.code()) {
case IP0: AppendToOutput(reg.Is64Bits() ? "ip0" : "wip0"); return;
case IP1: AppendToOutput(reg.Is64Bits() ? "ip1" : "wip1"); return;
@@ -66,16 +67,7 @@
return;
}
- char* buffer = buffer_;
- char* buffer_end = buffer_ + buffer_size_;
-
- // Find the end position in the buffer.
- while ((*buffer != 0) && (buffer < buffer_end)) {
- ++buffer;
- }
-
void* data_address = instr->LiteralAddress<void*>();
- ptrdiff_t buf_size_remaining = buffer_end - buffer;
vixl::Instr op = instr->Mask(vixl::LoadLiteralMask);
switch (op) {
@@ -84,14 +76,14 @@
case vixl::LDRSW_x_lit: {
int64_t data = op == vixl::LDR_x_lit ? *reinterpret_cast<int64_t*>(data_address)
: *reinterpret_cast<int32_t*>(data_address);
- snprintf(buffer, buf_size_remaining, " (0x%" PRIx64 " / %" PRId64 ")", data, data);
+ AppendToOutput(" (0x%" PRIx64 " / %" PRId64 ")", data, data);
break;
}
case vixl::LDR_s_lit:
case vixl::LDR_d_lit: {
double data = (op == vixl::LDR_s_lit) ? *reinterpret_cast<float*>(data_address)
: *reinterpret_cast<double*>(data_address);
- snprintf(buffer, buf_size_remaining, " (%g)", data);
+ AppendToOutput(" (%g)", data);
break;
}
default:
@@ -99,6 +91,17 @@
}
}
+void CustomDisassembler::VisitLoadStoreUnsignedOffset(const vixl::Instruction* instr) {
+ Disassembler::VisitLoadStoreUnsignedOffset(instr);
+
+ if (instr->Rn() == TR) {
+ int64_t offset = instr->ImmLSUnsigned() << instr->SizeLS();
+ std::ostringstream tmp_stream;
+ Thread::DumpThreadOffset<8>(tmp_stream, static_cast<uint32_t>(offset));
+ AppendToOutput(" (%s)", tmp_stream.str().c_str());
+ }
+}
+
size_t DisassemblerArm64::Dump(std::ostream& os, const uint8_t* begin) {
const vixl::Instruction* instr = reinterpret_cast<const vixl::Instruction*>(begin);
decoder.Decode(instr);
diff --git a/disassembler/disassembler_arm64.h b/disassembler/disassembler_arm64.h
index a370b8d..57f11c8 100644
--- a/disassembler/disassembler_arm64.h
+++ b/disassembler/disassembler_arm64.h
@@ -34,11 +34,14 @@
vixl::Disassembler(), read_literals_(read_literals) {}
// Use register aliases in the disassembly.
- virtual void AppendRegisterNameToOutput(const vixl::Instruction* instr,
- const vixl::CPURegister& reg) OVERRIDE;
+ void AppendRegisterNameToOutput(const vixl::Instruction* instr,
+ const vixl::CPURegister& reg) OVERRIDE;
// Improve the disassembly of literal load instructions.
- virtual void VisitLoadLiteral(const vixl::Instruction* instr) OVERRIDE;
+ void VisitLoadLiteral(const vixl::Instruction* instr) OVERRIDE;
+
+ // Improve the disassembly of thread offset.
+ void VisitLoadStoreUnsignedOffset(const vixl::Instruction* instr) OVERRIDE;
private:
// Indicate if the disassembler should read data loaded from literal pools.
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index 97c06f1..7442c70 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -138,7 +138,9 @@
{ kITypeMask, 41u << kOpcodeShift, "sh", "TO", },
{ kITypeMask, 43u << kOpcodeShift, "sw", "TO", },
{ kITypeMask, 49u << kOpcodeShift, "lwc1", "tO", },
+ { kITypeMask, 53u << kOpcodeShift, "ldc1", "tO", },
{ kITypeMask, 57u << kOpcodeShift, "swc1", "tO", },
+ { kITypeMask, 61u << kOpcodeShift, "sdc1", "tO", },
// Floating point.
{ kFpMask, kCop1 | 0, "add", "fdst" },
diff --git a/disassembler/disassembler_mips64.cc b/disassembler/disassembler_mips64.cc
new file mode 100644
index 0000000..2d3239f
--- /dev/null
+++ b/disassembler/disassembler_mips64.cc
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "disassembler_mips64.h"
+
+#include <ostream>
+#include <sstream>
+
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "thread.h"
+
+namespace art {
+namespace mips64 {
+
+
+struct Mips64Instruction {
+ uint32_t mask;
+ uint32_t value;
+ const char* name;
+ const char* args_fmt;
+
+ bool Matches(uint32_t instruction) const {
+ return (instruction & mask) == value;
+ }
+};
+
+static const uint32_t kOpcodeShift = 26;
+static const uint32_t kCop1 = (17 << kOpcodeShift);
+static const uint32_t kITypeMask = (0x3f << kOpcodeShift);
+static const uint32_t kJTypeMask = (0x3f << kOpcodeShift);
+static const uint32_t kRTypeMask = ((0x3f << kOpcodeShift) | (0x3f));
+static const uint32_t kSpecial2Mask = (0x3f << kOpcodeShift);
+static const uint32_t kFpMask = kRTypeMask;
+
+static const Mips64Instruction gMips64Instructions[] = {
+ // "sll r0, r0, 0" is the canonical "nop", used in delay slots.
+ { 0xffffffff, 0, "nop", "" },
+
+ // R-type instructions.
+ { kRTypeMask, 0, "sll", "DTA", },
+ // 0, 1, movci
+ { kRTypeMask, 2, "srl", "DTA", },
+ { kRTypeMask, 3, "sra", "DTA", },
+ { kRTypeMask, 4, "sllv", "DTS", },
+ { kRTypeMask, 6, "srlv", "DTS", },
+ { kRTypeMask, 7, "srav", "DTS", },
+ { kRTypeMask, 8, "jr", "S", },
+ // rd = 31 is implicit.
+ { kRTypeMask | (0x1f << 11), 9 | (31 << 11), "jalr", "S", },
+ { kRTypeMask, 9, "jalr", "DS", }, // General case.
+ { kRTypeMask | (0x1f << 6), 10, "movz", "DST", },
+ { kRTypeMask | (0x1f << 6), 11, "movn", "DST", },
+ { kRTypeMask, 12, "syscall", "", }, // TODO: code
+ { kRTypeMask, 13, "break", "", }, // TODO: code
+ { kRTypeMask, 15, "sync", "", }, // TODO: type
+ { kRTypeMask, 16, "mfhi", "D", },
+ { kRTypeMask, 17, "mthi", "S", },
+ { kRTypeMask, 18, "mflo", "D", },
+ { kRTypeMask, 19, "mtlo", "S", },
+ { kRTypeMask, 24, "mult", "ST", },
+ { kRTypeMask, 25, "multu", "ST", },
+ { kRTypeMask, 26, "div", "ST", },
+ { kRTypeMask, 27, "divu", "ST", },
+ { kRTypeMask, 32, "add", "DST", },
+ { kRTypeMask, 33, "addu", "DST", },
+ { kRTypeMask, 34, "sub", "DST", },
+ { kRTypeMask, 35, "subu", "DST", },
+ { kRTypeMask, 36, "and", "DST", },
+ { kRTypeMask, 37, "or", "DST", },
+ { kRTypeMask, 38, "xor", "DST", },
+ { kRTypeMask, 39, "nor", "DST", },
+ { kRTypeMask, 42, "slt", "DST", },
+ { kRTypeMask, 43, "sltu", "DST", },
+ { kRTypeMask, 44, "dadd", "DST", },
+ { kRTypeMask, 45, "daddu", "DST", },
+ { kRTypeMask, 46, "dsub", "DST", },
+ { kRTypeMask, 47, "dsubu", "DST", },
+ // 0, 48, tge
+ // 0, 49, tgeu
+ // 0, 50, tlt
+ // 0, 51, tltu
+ // 0, 52, teq
+ // 0, 54, tne
+
+ // SPECIAL2
+ { kSpecial2Mask | 0x7ff, (28 << kOpcodeShift) | 2, "mul", "DST" },
+ { kSpecial2Mask | 0x7ff, (28 << kOpcodeShift) | 32, "clz", "DS" },
+ { kSpecial2Mask | 0x7ff, (28 << kOpcodeShift) | 36, "dclz", "DS" },
+ { kSpecial2Mask | 0xffff, (28 << kOpcodeShift) | 0, "madd", "ST" },
+ { kSpecial2Mask | 0xffff, (28 << kOpcodeShift) | 1, "maddu", "ST" },
+ { kSpecial2Mask | 0xffff, (28 << kOpcodeShift) | 2, "mul", "DST" },
+ { kSpecial2Mask | 0xffff, (28 << kOpcodeShift) | 4, "msub", "ST" },
+ { kSpecial2Mask | 0xffff, (28 << kOpcodeShift) | 5, "msubu", "ST" },
+ { kSpecial2Mask | 0x3f, (28 << kOpcodeShift) | 0x3f, "sdbbp", "" },
+
+ // J-type instructions.
+ { kJTypeMask, 2 << kOpcodeShift, "j", "L" },
+ { kJTypeMask, 3 << kOpcodeShift, "jal", "L" },
+
+ // I-type instructions.
+ { kITypeMask, 4 << kOpcodeShift, "beq", "STB" },
+ { kITypeMask, 5 << kOpcodeShift, "bne", "STB" },
+ { kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (1 << 16), "bgez", "SB" },
+ { kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (0 << 16), "bltz", "SB" },
+ { kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (2 << 16), "bltzl", "SB" },
+ { kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (16 << 16), "bltzal", "SB" },
+ { kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (18 << 16), "bltzall", "SB" },
+ { kITypeMask | (0x1f << 16), 6 << kOpcodeShift | (0 << 16), "blez", "SB" },
+ { kITypeMask | (0x1f << 16), 7 << kOpcodeShift | (0 << 16), "bgtz", "SB" },
+
+ { 0xffff0000, (4 << kOpcodeShift), "b", "B" },
+ { 0xffff0000, (1 << kOpcodeShift) | (17 << 16), "bal", "B" },
+
+ { kITypeMask, 8 << kOpcodeShift, "addi", "TSi", },
+ { kITypeMask, 9 << kOpcodeShift, "addiu", "TSi", },
+ { kITypeMask, 10 << kOpcodeShift, "slti", "TSi", },
+ { kITypeMask, 11 << kOpcodeShift, "sltiu", "TSi", },
+ { kITypeMask, 12 << kOpcodeShift, "andi", "TSi", },
+ { kITypeMask, 13 << kOpcodeShift, "ori", "TSi", },
+ { kITypeMask, 14 << kOpcodeShift, "ori", "TSi", },
+ { kITypeMask, 15 << kOpcodeShift, "lui", "TI", },
+
+ { kITypeMask, 24 << kOpcodeShift, "daddi", "TSi", },
+ { kITypeMask, 25 << kOpcodeShift, "daddiu", "TSi", },
+
+
+ { kITypeMask, 32u << kOpcodeShift, "lb", "TO", },
+ { kITypeMask, 33u << kOpcodeShift, "lh", "TO", },
+ { kITypeMask, 35u << kOpcodeShift, "lw", "TO", },
+ { kITypeMask, 36u << kOpcodeShift, "lbu", "TO", },
+ { kITypeMask, 37u << kOpcodeShift, "lhu", "TO", },
+ { kITypeMask, 40u << kOpcodeShift, "sb", "TO", },
+ { kITypeMask, 41u << kOpcodeShift, "sh", "TO", },
+ { kITypeMask, 43u << kOpcodeShift, "sw", "TO", },
+ { kITypeMask, 49u << kOpcodeShift, "lwc1", "tO", },
+ { kITypeMask, 53u << kOpcodeShift, "ldc1", "tO", },
+ { kITypeMask, 55u << kOpcodeShift, "ld", "TO", },
+ { kITypeMask, 57u << kOpcodeShift, "swc1", "tO", },
+ { kITypeMask, 61u << kOpcodeShift, "sdc1", "tO", },
+ { kITypeMask, 63u << kOpcodeShift, "sd", "TO", },
+
+ // Floating point.
+ { kFpMask, kCop1 | 0, "add", "fdst" },
+ { kFpMask, kCop1 | 1, "sub", "fdst" },
+ { kFpMask, kCop1 | 2, "mul", "fdst" },
+ { kFpMask, kCop1 | 3, "div", "fdst" },
+ { kFpMask | (0x1f << 16), kCop1 | 4, "sqrt", "fdst" },
+ { kFpMask | (0x1f << 16), kCop1 | 5, "abs", "fds" },
+ { kFpMask | (0x1f << 16), kCop1 | 6, "mov", "fds" },
+ { kFpMask | (0x1f << 16), kCop1 | 7, "neg", "fds" },
+ { kFpMask | (0x1f << 16), kCop1 | 8, "round.l", "fds" },
+ { kFpMask | (0x1f << 16), kCop1 | 9, "trunc.l", "fds" },
+ { kFpMask | (0x1f << 16), kCop1 | 10, "ceil.l", "fds" },
+ { kFpMask | (0x1f << 16), kCop1 | 11, "floor.l", "fds" },
+ { kFpMask | (0x1f << 16), kCop1 | 12, "round.w", "fds" },
+ { kFpMask | (0x1f << 16), kCop1 | 13, "trunc.w", "fds" },
+ { kFpMask | (0x1f << 16), kCop1 | 14, "ceil.w", "fds" },
+ { kFpMask | (0x1f << 16), kCop1 | 15, "floor.w", "fds" },
+ { kFpMask | (0x1f << 16), kCop1 | 32, "cvt.s", "fds" },
+ { kFpMask | (0x1f << 16), kCop1 | 33, "cvt.d", "fds" },
+ { kFpMask | (0x1f << 16), kCop1 | 36, "cvt.w", "fds" },
+ { kFpMask | (0x1f << 16), kCop1 | 37, "cvt.l", "fds" },
+ { kFpMask | (0x1f << 16), kCop1 | 38, "cvt.ps", "fds" },
+};
+
+static uint32_t ReadU32(const uint8_t* ptr) {
+ // We only support little-endian MIPS64.
+ return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
+}
+
+static void DumpMips64(std::ostream& os, const uint8_t* instr_ptr) {
+ uint32_t instruction = ReadU32(instr_ptr);
+
+ uint32_t rs = (instruction >> 21) & 0x1f; // I-type, R-type.
+ uint32_t rt = (instruction >> 16) & 0x1f; // I-type, R-type.
+ uint32_t rd = (instruction >> 11) & 0x1f; // R-type.
+ uint32_t sa = (instruction >> 6) & 0x1f; // R-type.
+
+ std::string opcode;
+ std::ostringstream args;
+
+ // TODO: remove this!
+ uint32_t op = (instruction >> 26) & 0x3f;
+ uint32_t function = (instruction & 0x3f); // R-type.
+ opcode = StringPrintf("op=%d fn=%d", op, function);
+
+ for (size_t i = 0; i < arraysize(gMips64Instructions); ++i) {
+ if (gMips64Instructions[i].Matches(instruction)) {
+ opcode = gMips64Instructions[i].name;
+ for (const char* args_fmt = gMips64Instructions[i].args_fmt; *args_fmt; ++args_fmt) {
+ switch (*args_fmt) {
+ case 'A': // sa (shift amount).
+ args << sa;
+ break;
+ case 'B': // Branch offset.
+ {
+ int32_t offset = static_cast<int16_t>(instruction & 0xffff);
+ offset <<= 2;
+ offset += 4; // Delay slot.
+ args << StringPrintf("%p ; %+d", instr_ptr + offset, offset);
+ }
+ break;
+ case 'D': args << 'r' << rd; break;
+ case 'd': args << 'f' << rd; break;
+ case 'f': // Floating point "fmt".
+ {
+ size_t fmt = (instruction >> 21) & 0x7; // TODO: other fmts?
+ switch (fmt) {
+ case 0: opcode += ".s"; break;
+ case 1: opcode += ".d"; break;
+ case 4: opcode += ".w"; break;
+ case 5: opcode += ".l"; break;
+ case 6: opcode += ".ps"; break;
+ default: opcode += ".?"; break;
+ }
+ continue; // No ", ".
+ }
+ break;
+ case 'I': // Upper 16-bit immediate.
+ args << reinterpret_cast<void*>((instruction & 0xffff) << 16);
+ break;
+ case 'i': // Sign-extended lower 16-bit immediate.
+ args << static_cast<int16_t>(instruction & 0xffff);
+ break;
+ case 'L': // Jump label.
+ {
+ // TODO: is this right?
+ uint32_t instr_index = (instruction & 0x1ffffff);
+ uint32_t target = (instr_index << 2);
+ target |= (reinterpret_cast<uintptr_t>(instr_ptr + 4)
+ & 0xf0000000);
+ args << reinterpret_cast<void*>(target);
+ }
+ break;
+ case 'O': // +x(rs)
+ {
+ int32_t offset = static_cast<int16_t>(instruction & 0xffff);
+ args << StringPrintf("%+d(r%d)", offset, rs);
+ if (rs == 17) {
+ args << " ; ";
+ Thread::DumpThreadOffset<8>(args, offset);
+ }
+ }
+ break;
+ case 'S': args << 'r' << rs; break;
+ case 's': args << 'f' << rs; break;
+ case 'T': args << 'r' << rt; break;
+ case 't': args << 'f' << rt; break;
+ }
+ if (*(args_fmt + 1)) {
+ args << ", ";
+ }
+ }
+ break;
+ }
+ }
+
+ os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str())
+ << args.str() << '\n';
+}
+
+size_t DisassemblerMips64::Dump(std::ostream& os, const uint8_t* begin) {
+ DumpMips64(os, begin);
+ return 4;
+}
+
+void DisassemblerMips64::Dump(std::ostream& os, const uint8_t* begin,
+ const uint8_t* end) {
+ for (const uint8_t* cur = begin; cur < end; cur += 4) {
+ DumpMips64(os, cur);
+ }
+}
+
+} // namespace mips64
+} // namespace art
diff --git a/disassembler/disassembler_mips64.h b/disassembler/disassembler_mips64.h
new file mode 100644
index 0000000..06efdc8
--- /dev/null
+++ b/disassembler/disassembler_mips64.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_DISASSEMBLER_DISASSEMBLER_MIPS64_H_
+#define ART_DISASSEMBLER_DISASSEMBLER_MIPS64_H_
+
+#include <vector>
+
+#include "disassembler.h"
+
+namespace art {
+namespace mips64 {
+
+class DisassemblerMips64 FINAL : public Disassembler {
+ public:
+ explicit DisassemblerMips64(DisassemblerOptions* options) : Disassembler(options) {}
+
+ size_t Dump(std::ostream& os, const uint8_t* begin) OVERRIDE;
+ void Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DisassemblerMips64);
+};
+
+} // namespace mips64
+} // namespace art
+
+#endif // ART_DISASSEMBLER_DISASSEMBLER_MIPS64_H_
diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc
index b3af8a6..203488d 100644
--- a/disassembler/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -80,8 +80,6 @@
}
}
-enum RegFile { GPR, MMX, SSE };
-
static void DumpAnyReg(std::ostream& os, uint8_t rex, size_t reg,
bool byte_operand, uint8_t size_override, RegFile reg_file) {
if (reg_file == GPR) {
@@ -121,12 +119,6 @@
DumpAddrReg(os, rex, reg_num);
}
-static void DumpIndexReg(std::ostream& os, uint8_t rex, uint8_t reg) {
- bool rex_x = (rex & REX_X) != 0;
- uint8_t reg_num = rex_x ? (reg + 8) : reg;
- DumpAddrReg(os, rex, reg_num);
-}
-
static void DumpOpcodeReg(std::ostream& os, uint8_t rex, uint8_t reg,
bool byte_operand, uint8_t size_override) {
bool rex_b = (rex & REX_B) != 0;
@@ -155,6 +147,102 @@
}
}
+// Do not inline to avoid Clang stack frame problems. b/18733806
+NO_INLINE
+static std::string DumpCodeHex(const uint8_t* begin, const uint8_t* end) {
+ std::stringstream hex;
+ for (size_t i = 0; begin + i < end; ++i) {
+ hex << StringPrintf("%02X", begin[i]);
+ }
+ return hex.str();
+}
+
+std::string DisassemblerX86::DumpAddress(uint8_t mod, uint8_t rm, uint8_t rex64, uint8_t rex_w,
+ bool no_ops, bool byte_operand, bool byte_second_operand,
+ uint8_t* prefix, bool load, RegFile src_reg_file,
+ RegFile dst_reg_file, const uint8_t** instr,
+ uint32_t* address_bits) {
+ std::ostringstream address;
+ if (mod == 0 && rm == 5) {
+ if (!supports_rex_) { // Absolute address.
+ *address_bits = *reinterpret_cast<const uint32_t*>(*instr);
+ address << StringPrintf("[0x%x]", *address_bits);
+ } else { // 64-bit RIP relative addressing.
+ address << StringPrintf("[RIP + 0x%x]", *reinterpret_cast<const uint32_t*>(*instr));
+ }
+ (*instr) += 4;
+ } else if (rm == 4 && mod != 3) { // SIB
+ uint8_t sib = **instr;
+ (*instr)++;
+ uint8_t scale = (sib >> 6) & 3;
+ uint8_t index = (sib >> 3) & 7;
+ uint8_t base = sib & 7;
+ address << "[";
+
+ // REX.x is bit 3 of index.
+ if ((rex64 & REX_X) != 0) {
+ index += 8;
+ }
+
+ // Mod = 0 && base = 5 (ebp): no base (ignores REX.b).
+ bool has_base = false;
+ if (base != 5 || mod != 0) {
+ has_base = true;
+ DumpBaseReg(address, rex64, base);
+ }
+
+ // Index = 4 (esp/rsp) is disallowed.
+ if (index != 4) {
+ if (has_base) {
+ address << " + ";
+ }
+ DumpAddrReg(address, rex64, index);
+ if (scale != 0) {
+ address << StringPrintf(" * %d", 1 << scale);
+ }
+ }
+
+ if (mod == 0) {
+ if (base == 5) {
+ if (index != 4) {
+ address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(*instr));
+ } else {
+ // 64-bit low 32-bit absolute address, redundant absolute address encoding on 32-bit.
+ *address_bits = *reinterpret_cast<const uint32_t*>(*instr);
+ address << StringPrintf("%d", *address_bits);
+ }
+ (*instr) += 4;
+ }
+ } else if (mod == 1) {
+ address << StringPrintf(" + %d", *reinterpret_cast<const int8_t*>(*instr));
+ (*instr)++;
+ } else if (mod == 2) {
+ address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(*instr));
+ (*instr) += 4;
+ }
+ address << "]";
+ } else {
+ if (mod == 3) {
+ if (!no_ops) {
+ DumpRmReg(address, rex_w, rm, byte_operand || byte_second_operand,
+ prefix[2], load ? src_reg_file : dst_reg_file);
+ }
+ } else {
+ address << "[";
+ DumpBaseReg(address, rex64, rm);
+ if (mod == 1) {
+ address << StringPrintf(" + %d", *reinterpret_cast<const int8_t*>(*instr));
+ (*instr)++;
+ } else if (mod == 2) {
+ address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(*instr));
+ (*instr) += 4;
+ }
+ address << "]";
+ }
+ }
+ return address.str();
+}
+
size_t DisassemblerX86::DumpInstruction(std::ostream& os, const uint8_t* instr) {
const uint8_t* begin_instr = instr;
bool have_prefixes = true;
@@ -201,7 +289,12 @@
bool reg_is_opcode = false;
size_t immediate_bytes = 0;
size_t branch_bytes = 0;
- std::ostringstream opcode;
+ std::string opcode_tmp; // Storage to keep StringPrintf result alive.
+ const char* opcode0 = ""; // Prefix part.
+ const char* opcode1 = ""; // Main opcode.
+ const char* opcode2 = ""; // Sub-opcode. E.g., jump type.
+ const char* opcode3 = ""; // Mod-rm part.
+ const char* opcode4 = ""; // Suffix part.
bool store = false; // stores to memory (ie rm is on the left)
bool load = false; // loads from memory (ie rm is on the right)
bool byte_operand = false; // true when the opcode is dealing with byte operands
@@ -220,12 +313,12 @@
rm8_r8, rm32_r32, \
r8_rm8, r32_rm32, \
ax8_i8, ax32_i32) \
- case rm8_r8: opcode << #opname; store = true; has_modrm = true; byte_operand = true; break; \
- case rm32_r32: opcode << #opname; store = true; has_modrm = true; break; \
- case r8_rm8: opcode << #opname; load = true; has_modrm = true; byte_operand = true; break; \
- case r32_rm32: opcode << #opname; load = true; has_modrm = true; break; \
- case ax8_i8: opcode << #opname; ax = true; immediate_bytes = 1; byte_operand = true; break; \
- case ax32_i32: opcode << #opname; ax = true; immediate_bytes = 4; break;
+ case rm8_r8: opcode1 = #opname; store = true; has_modrm = true; byte_operand = true; break; \
+ case rm32_r32: opcode1 = #opname; store = true; has_modrm = true; break; \
+ case r8_rm8: opcode1 = #opname; load = true; has_modrm = true; byte_operand = true; break; \
+ case r32_rm32: opcode1 = #opname; load = true; has_modrm = true; break; \
+ case ax8_i8: opcode1 = #opname; ax = true; immediate_bytes = 1; byte_operand = true; break; \
+ case ax32_i32: opcode1 = #opname; ax = true; immediate_bytes = 4; break;
DISASSEMBLER_ENTRY(add,
0x00 /* RegMem8/Reg8 */, 0x01 /* RegMem32/Reg32 */,
@@ -262,65 +355,67 @@
#undef DISASSEMBLER_ENTRY
case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
- opcode << "push";
+ opcode1 = "push";
reg_in_opcode = true;
target_specific = true;
break;
case 0x58: case 0x59: case 0x5A: case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F:
- opcode << "pop";
+ opcode1 = "pop";
reg_in_opcode = true;
target_specific = true;
break;
case 0x63:
if ((rex & REX_W) != 0) {
- opcode << "movsxd";
+ opcode1 = "movsxd";
has_modrm = true;
load = true;
} else {
// In 32-bit mode (!supports_rex_) this is ARPL, with no REX prefix the functionality is the
// same as 'mov' but the use of the instruction is discouraged.
- opcode << StringPrintf("unknown opcode '%02X'", *instr);
+ opcode_tmp = StringPrintf("unknown opcode '%02X'", *instr);
+ opcode1 = opcode_tmp.c_str();
}
break;
- case 0x68: opcode << "push"; immediate_bytes = 4; break;
- case 0x69: opcode << "imul"; load = true; has_modrm = true; immediate_bytes = 4; break;
- case 0x6A: opcode << "push"; immediate_bytes = 1; break;
- case 0x6B: opcode << "imul"; load = true; has_modrm = true; immediate_bytes = 1; break;
+ case 0x68: opcode1 = "push"; immediate_bytes = 4; break;
+ case 0x69: opcode1 = "imul"; load = true; has_modrm = true; immediate_bytes = 4; break;
+ case 0x6A: opcode1 = "push"; immediate_bytes = 1; break;
+ case 0x6B: opcode1 = "imul"; load = true; has_modrm = true; immediate_bytes = 1; break;
case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F:
static const char* condition_codes[] =
{"o", "no", "b/nae/c", "nb/ae/nc", "z/eq", "nz/ne", "be/na", "nbe/a",
"s", "ns", "p/pe", "np/po", "l/nge", "nl/ge", "le/ng", "nle/g"
};
- opcode << "j" << condition_codes[*instr & 0xF];
+ opcode1 = "j";
+ opcode2 = condition_codes[*instr & 0xF];
branch_bytes = 1;
break;
case 0x86: case 0x87:
- opcode << "xchg";
+ opcode1 = "xchg";
store = true;
has_modrm = true;
byte_operand = (*instr == 0x86);
break;
- case 0x88: opcode << "mov"; store = true; has_modrm = true; byte_operand = true; break;
- case 0x89: opcode << "mov"; store = true; has_modrm = true; break;
- case 0x8A: opcode << "mov"; load = true; has_modrm = true; byte_operand = true; break;
- case 0x8B: opcode << "mov"; load = true; has_modrm = true; break;
+ case 0x88: opcode1 = "mov"; store = true; has_modrm = true; byte_operand = true; break;
+ case 0x89: opcode1 = "mov"; store = true; has_modrm = true; break;
+ case 0x8A: opcode1 = "mov"; load = true; has_modrm = true; byte_operand = true; break;
+ case 0x8B: opcode1 = "mov"; load = true; has_modrm = true; break;
case 0x0F: // 2 byte extended opcode
instr++;
switch (*instr) {
case 0x10: case 0x11:
if (prefix[0] == 0xF2) {
- opcode << "movsd";
+ opcode1 = "movsd";
prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[0] == 0xF3) {
- opcode << "movss";
+ opcode1 = "movss";
prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[2] == 0x66) {
- opcode << "movupd";
+ opcode1 = "movupd";
prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
} else {
- opcode << "movups";
+ opcode1 = "movups";
}
has_modrm = true;
src_reg_file = dst_reg_file = SSE;
@@ -329,10 +424,10 @@
break;
case 0x12: case 0x13:
if (prefix[2] == 0x66) {
- opcode << "movlpd";
+ opcode1 = "movlpd";
prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[0] == 0) {
- opcode << "movlps";
+ opcode1 = "movlps";
}
has_modrm = true;
src_reg_file = dst_reg_file = SSE;
@@ -341,10 +436,10 @@
break;
case 0x16: case 0x17:
if (prefix[2] == 0x66) {
- opcode << "movhpd";
+ opcode1 = "movhpd";
prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[0] == 0) {
- opcode << "movhps";
+ opcode1 = "movhps";
}
has_modrm = true;
src_reg_file = dst_reg_file = SSE;
@@ -353,10 +448,10 @@
break;
case 0x28: case 0x29:
if (prefix[2] == 0x66) {
- opcode << "movapd";
+ opcode1 = "movapd";
prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[0] == 0) {
- opcode << "movaps";
+ opcode1 = "movaps";
}
has_modrm = true;
src_reg_file = dst_reg_file = SSE;
@@ -365,16 +460,16 @@
break;
case 0x2A:
if (prefix[2] == 0x66) {
- opcode << "cvtpi2pd";
+ opcode1 = "cvtpi2pd";
prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[0] == 0xF2) {
- opcode << "cvtsi2sd";
+ opcode1 = "cvtsi2sd";
prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[0] == 0xF3) {
- opcode << "cvtsi2ss";
+ opcode1 = "cvtsi2ss";
prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode
} else {
- opcode << "cvtpi2ps";
+ opcode1 = "cvtpi2ps";
}
load = true;
has_modrm = true;
@@ -382,16 +477,16 @@
break;
case 0x2C:
if (prefix[2] == 0x66) {
- opcode << "cvttpd2pi";
+ opcode1 = "cvttpd2pi";
prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[0] == 0xF2) {
- opcode << "cvttsd2si";
+ opcode1 = "cvttsd2si";
prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[0] == 0xF3) {
- opcode << "cvttss2si";
+ opcode1 = "cvttss2si";
prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode
} else {
- opcode << "cvttps2pi";
+ opcode1 = "cvttps2pi";
}
load = true;
has_modrm = true;
@@ -399,30 +494,30 @@
break;
case 0x2D:
if (prefix[2] == 0x66) {
- opcode << "cvtpd2pi";
+ opcode1 = "cvtpd2pi";
prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[0] == 0xF2) {
- opcode << "cvtsd2si";
+ opcode1 = "cvtsd2si";
prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[0] == 0xF3) {
- opcode << "cvtss2si";
+ opcode1 = "cvtss2si";
prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode
} else {
- opcode << "cvtps2pi";
+ opcode1 = "cvtps2pi";
}
load = true;
has_modrm = true;
src_reg_file = SSE;
break;
case 0x2E:
- opcode << "u";
+ opcode0 = "u";
FALLTHROUGH_INTENDED;
case 0x2F:
if (prefix[2] == 0x66) {
- opcode << "comisd";
+ opcode1 = "comisd";
prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
} else {
- opcode << "comiss";
+ opcode1 = "comiss";
}
has_modrm = true;
load = true;
@@ -433,31 +528,33 @@
if (prefix[2] == 0x66) {
switch (*instr) {
case 0x01:
- opcode << "phaddw";
+ opcode1 = "phaddw";
prefix[2] = 0;
has_modrm = true;
load = true;
src_reg_file = dst_reg_file = SSE;
break;
case 0x02:
- opcode << "phaddd";
+ opcode1 = "phaddd";
prefix[2] = 0;
has_modrm = true;
load = true;
src_reg_file = dst_reg_file = SSE;
break;
case 0x40:
- opcode << "pmulld";
+ opcode1 = "pmulld";
prefix[2] = 0;
has_modrm = true;
load = true;
src_reg_file = dst_reg_file = SSE;
break;
default:
- opcode << StringPrintf("unknown opcode '0F 38 %02X'", *instr);
+ opcode_tmp = StringPrintf("unknown opcode '0F 38 %02X'", *instr);
+ opcode1 = opcode_tmp.c_str();
}
} else {
- opcode << StringPrintf("unknown opcode '0F 38 %02X'", *instr);
+ opcode_tmp = StringPrintf("unknown opcode '0F 38 %02X'", *instr);
+ opcode1 = opcode_tmp.c_str();
}
break;
case 0x3A: // 3 byte extended opcode
@@ -465,7 +562,7 @@
if (prefix[2] == 0x66) {
switch (*instr) {
case 0x14:
- opcode << "pextrb";
+ opcode1 = "pextrb";
prefix[2] = 0;
has_modrm = true;
store = true;
@@ -473,7 +570,7 @@
immediate_bytes = 1;
break;
case 0x16:
- opcode << "pextrd";
+ opcode1 = "pextrd";
prefix[2] = 0;
has_modrm = true;
store = true;
@@ -481,48 +578,51 @@
immediate_bytes = 1;
break;
default:
- opcode << StringPrintf("unknown opcode '0F 3A %02X'", *instr);
+ opcode_tmp = StringPrintf("unknown opcode '0F 3A %02X'", *instr);
+ opcode1 = opcode_tmp.c_str();
}
} else {
- opcode << StringPrintf("unknown opcode '0F 3A %02X'", *instr);
+ opcode_tmp = StringPrintf("unknown opcode '0F 3A %02X'", *instr);
+ opcode1 = opcode_tmp.c_str();
}
break;
case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4E: case 0x4F:
- opcode << "cmov" << condition_codes[*instr & 0xF];
+ opcode1 = "cmov";
+ opcode2 = condition_codes[*instr & 0xF];
has_modrm = true;
load = true;
break;
case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
case 0x58: case 0x59: case 0x5C: case 0x5D: case 0x5E: case 0x5F: {
switch (*instr) {
- case 0x50: opcode << "movmsk"; break;
- case 0x51: opcode << "sqrt"; break;
- case 0x52: opcode << "rsqrt"; break;
- case 0x53: opcode << "rcp"; break;
- case 0x54: opcode << "and"; break;
- case 0x55: opcode << "andn"; break;
- case 0x56: opcode << "or"; break;
- case 0x57: opcode << "xor"; break;
- case 0x58: opcode << "add"; break;
- case 0x59: opcode << "mul"; break;
- case 0x5C: opcode << "sub"; break;
- case 0x5D: opcode << "min"; break;
- case 0x5E: opcode << "div"; break;
- case 0x5F: opcode << "max"; break;
+ case 0x50: opcode1 = "movmsk"; break;
+ case 0x51: opcode1 = "sqrt"; break;
+ case 0x52: opcode1 = "rsqrt"; break;
+ case 0x53: opcode1 = "rcp"; break;
+ case 0x54: opcode1 = "and"; break;
+ case 0x55: opcode1 = "andn"; break;
+ case 0x56: opcode1 = "or"; break;
+ case 0x57: opcode1 = "xor"; break;
+ case 0x58: opcode1 = "add"; break;
+ case 0x59: opcode1 = "mul"; break;
+ case 0x5C: opcode1 = "sub"; break;
+ case 0x5D: opcode1 = "min"; break;
+ case 0x5E: opcode1 = "div"; break;
+ case 0x5F: opcode1 = "max"; break;
default: LOG(FATAL) << "Unreachable"; UNREACHABLE();
}
if (prefix[2] == 0x66) {
- opcode << "pd";
+ opcode2 = "pd";
prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[0] == 0xF2) {
- opcode << "sd";
+ opcode2 = "sd";
prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[0] == 0xF3) {
- opcode << "ss";
+ opcode2 = "ss";
prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode
} else {
- opcode << "ps";
+ opcode2 = "ps";
}
load = true;
has_modrm = true;
@@ -531,16 +631,16 @@
}
case 0x5A:
if (prefix[2] == 0x66) {
- opcode << "cvtpd2ps";
+ opcode1 = "cvtpd2ps";
prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[0] == 0xF2) {
- opcode << "cvtsd2ss";
+ opcode1 = "cvtsd2ss";
prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[0] == 0xF3) {
- opcode << "cvtss2sd";
+ opcode1 = "cvtss2sd";
prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode
} else {
- opcode << "cvtps2pd";
+ opcode1 = "cvtps2pd";
}
load = true;
has_modrm = true;
@@ -548,15 +648,15 @@
break;
case 0x5B:
if (prefix[2] == 0x66) {
- opcode << "cvtps2dq";
+ opcode1 = "cvtps2dq";
prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[0] == 0xF2) {
- opcode << "bad opcode F2 0F 5B";
+ opcode1 = "bad opcode F2 0F 5B";
} else if (prefix[0] == 0xF3) {
- opcode << "cvttps2dq";
+ opcode1 = "cvttps2dq";
prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode
} else {
- opcode << "cvtdq2ps";
+ opcode1 = "cvtdq2ps";
}
load = true;
has_modrm = true;
@@ -570,10 +670,10 @@
src_reg_file = dst_reg_file = MMX;
}
switch (*instr) {
- case 0x60: opcode << "punpcklbw"; break;
- case 0x61: opcode << "punpcklwd"; break;
- case 0x62: opcode << "punpckldq"; break;
- case 0x6c: opcode << "punpcklqdq"; break;
+ case 0x60: opcode1 = "punpcklbw"; break;
+ case 0x61: opcode1 = "punpcklwd"; break;
+ case 0x62: opcode1 = "punpckldq"; break;
+ case 0x6c: opcode1 = "punpcklqdq"; break;
}
load = true;
has_modrm = true;
@@ -585,43 +685,44 @@
} else {
dst_reg_file = MMX;
}
- opcode << "movd";
+ opcode1 = "movd";
load = true;
has_modrm = true;
break;
case 0x6F:
if (prefix[2] == 0x66) {
src_reg_file = dst_reg_file = SSE;
- opcode << "movdqa";
+ opcode1 = "movdqa";
prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[0] == 0xF3) {
src_reg_file = dst_reg_file = SSE;
- opcode << "movdqu";
+ opcode1 = "movdqu";
prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode
} else {
dst_reg_file = MMX;
- opcode << "movq";
+ opcode1 = "movq";
}
load = true;
has_modrm = true;
break;
case 0x70:
if (prefix[2] == 0x66) {
- opcode << "pshufd";
+ opcode1 = "pshufd";
prefix[2] = 0;
has_modrm = true;
store = true;
src_reg_file = dst_reg_file = SSE;
immediate_bytes = 1;
} else if (prefix[0] == 0xF2) {
- opcode << "pshuflw";
+ opcode1 = "pshuflw";
prefix[0] = 0;
has_modrm = true;
store = true;
src_reg_file = dst_reg_file = SSE;
immediate_bytes = 1;
} else {
- opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
+ opcode_tmp = StringPrintf("unknown opcode '0F %02X'", *instr);
+ opcode1 = opcode_tmp.c_str();
}
break;
case 0x71:
@@ -674,13 +775,14 @@
break;
case 0x7C:
if (prefix[0] == 0xF2) {
- opcode << "haddps";
+ opcode1 = "haddps";
prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode
} else if (prefix[2] == 0x66) {
- opcode << "haddpd";
+ opcode1 = "haddpd";
prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
} else {
- opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
+ opcode_tmp = StringPrintf("unknown opcode '0F %02X'", *instr);
+ opcode1 = opcode_tmp.c_str();
break;
}
src_reg_file = dst_reg_file = SSE;
@@ -694,43 +796,45 @@
} else {
src_reg_file = MMX;
}
- opcode << "movd";
+ opcode1 = "movd";
has_modrm = true;
store = true;
break;
case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E: case 0x8F:
- opcode << "j" << condition_codes[*instr & 0xF];
+ opcode1 = "j";
+ opcode2 = condition_codes[*instr & 0xF];
branch_bytes = 4;
break;
case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97:
case 0x98: case 0x99: case 0x9A: case 0x9B: case 0x9C: case 0x9D: case 0x9E: case 0x9F:
- opcode << "set" << condition_codes[*instr & 0xF];
+ opcode1 = "set";
+ opcode2 = condition_codes[*instr & 0xF];
modrm_opcodes = nullptr;
reg_is_opcode = true;
has_modrm = true;
store = true;
break;
case 0xA4:
- opcode << "shld";
+ opcode1 = "shld";
has_modrm = true;
load = true;
immediate_bytes = 1;
break;
case 0xA5:
- opcode << "shld";
+ opcode1 = "shld";
has_modrm = true;
load = true;
cx = true;
break;
case 0xAC:
- opcode << "shrd";
+ opcode1 = "shrd";
has_modrm = true;
load = true;
immediate_bytes = 1;
break;
case 0xAD:
- opcode << "shrd";
+ opcode1 = "shrd";
has_modrm = true;
load = true;
cx = true;
@@ -778,61 +882,62 @@
}
break;
case 0xAF:
- opcode << "imul";
+ opcode1 = "imul";
has_modrm = true;
load = true;
break;
case 0xB1:
- opcode << "cmpxchg";
+ opcode1 = "cmpxchg";
has_modrm = true;
store = true;
break;
case 0xB6:
- opcode << "movzxb";
+ opcode1 = "movzxb";
has_modrm = true;
load = true;
byte_second_operand = true;
break;
case 0xB7:
- opcode << "movzxw";
+ opcode1 = "movzxw";
has_modrm = true;
load = true;
break;
case 0xBE:
- opcode << "movsxb";
+ opcode1 = "movsxb";
has_modrm = true;
load = true;
byte_second_operand = true;
rex |= (rex == 0 ? 0 : REX_W);
break;
case 0xBF:
- opcode << "movsxw";
+ opcode1 = "movsxw";
has_modrm = true;
load = true;
break;
case 0xC3:
- opcode << "movnti";
+ opcode1 = "movnti";
store = true;
has_modrm = true;
break;
case 0xC5:
if (prefix[2] == 0x66) {
- opcode << "pextrw";
+ opcode1 = "pextrw";
prefix[2] = 0;
has_modrm = true;
store = true;
src_reg_file = SSE;
immediate_bytes = 1;
} else {
- opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
+ opcode_tmp = StringPrintf("unknown opcode '0F %02X'", *instr);
+ opcode1 = opcode_tmp.c_str();
}
break;
case 0xC6:
if (prefix[2] == 0x66) {
- opcode << "shufpd";
+ opcode1 = "shufpd";
prefix[2] = 0;
} else {
- opcode << "shufps";
+ opcode1 = "shufps";
}
has_modrm = true;
store = true;
@@ -849,7 +954,7 @@
store = true;
break;
case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF:
- opcode << "bswap";
+ opcode1 = "bswap";
reg_in_opcode = true;
break;
case 0xD4:
@@ -859,7 +964,7 @@
} else {
src_reg_file = dst_reg_file = MMX;
}
- opcode << "paddq";
+ opcode1 = "paddq";
prefix[2] = 0;
has_modrm = true;
load = true;
@@ -871,20 +976,21 @@
} else {
src_reg_file = dst_reg_file = MMX;
}
- opcode << "pand";
+ opcode1 = "pand";
prefix[2] = 0;
has_modrm = true;
load = true;
break;
case 0xD5:
if (prefix[2] == 0x66) {
- opcode << "pmullw";
+ opcode1 = "pmullw";
prefix[2] = 0;
has_modrm = true;
load = true;
src_reg_file = dst_reg_file = SSE;
} else {
- opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
+ opcode_tmp = StringPrintf("unknown opcode '0F %02X'", *instr);
+ opcode1 = opcode_tmp.c_str();
}
break;
case 0xEB:
@@ -894,7 +1000,7 @@
} else {
src_reg_file = dst_reg_file = MMX;
}
- opcode << "por";
+ opcode1 = "por";
prefix[2] = 0;
has_modrm = true;
load = true;
@@ -906,7 +1012,7 @@
} else {
src_reg_file = dst_reg_file = MMX;
}
- opcode << "pxor";
+ opcode1 = "pxor";
prefix[2] = 0;
has_modrm = true;
load = true;
@@ -927,22 +1033,23 @@
src_reg_file = dst_reg_file = MMX;
}
switch (*instr) {
- case 0xF4: opcode << "pmuludq"; break;
- case 0xF6: opcode << "psadbw"; break;
- case 0xF8: opcode << "psubb"; break;
- case 0xF9: opcode << "psubw"; break;
- case 0xFA: opcode << "psubd"; break;
- case 0xFB: opcode << "psubq"; break;
- case 0xFC: opcode << "paddb"; break;
- case 0xFD: opcode << "paddw"; break;
- case 0xFE: opcode << "paddd"; break;
+ case 0xF4: opcode1 = "pmuludq"; break;
+ case 0xF6: opcode1 = "psadbw"; break;
+ case 0xF8: opcode1 = "psubb"; break;
+ case 0xF9: opcode1 = "psubw"; break;
+ case 0xFA: opcode1 = "psubd"; break;
+ case 0xFB: opcode1 = "psubq"; break;
+ case 0xFC: opcode1 = "paddb"; break;
+ case 0xFD: opcode1 = "paddw"; break;
+ case 0xFE: opcode1 = "paddd"; break;
}
prefix[2] = 0;
has_modrm = true;
load = true;
break;
default:
- opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
+ opcode_tmp = StringPrintf("unknown opcode '0F %02X'", *instr);
+ opcode1 = opcode_tmp.c_str();
break;
}
break;
@@ -956,38 +1063,39 @@
immediate_bytes = *instr == 0x81 ? 4 : 1;
break;
case 0x84: case 0x85:
- opcode << "test";
+ opcode1 = "test";
has_modrm = true;
load = true;
byte_operand = (*instr & 1) == 0;
break;
case 0x8D:
- opcode << "lea";
+ opcode1 = "lea";
has_modrm = true;
load = true;
break;
case 0x8F:
- opcode << "pop";
+ opcode1 = "pop";
has_modrm = true;
reg_is_opcode = true;
store = true;
break;
case 0x99:
- opcode << "cdq";
+ opcode1 = "cdq";
break;
case 0x9B:
if (instr[1] == 0xDF && instr[2] == 0xE0) {
- opcode << "fstsw\tax";
+ opcode1 = "fstsw\tax";
instr += 2;
} else {
- opcode << StringPrintf("unknown opcode '%02X'", *instr);
+ opcode_tmp = StringPrintf("unknown opcode '%02X'", *instr);
+ opcode1 = opcode_tmp.c_str();
}
break;
case 0xAF:
- opcode << (prefix[2] == 0x66 ? "scasw" : "scasl");
+ opcode1 = (prefix[2] == 0x66 ? "scasw" : "scasl");
break;
case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7:
- opcode << "mov";
+ opcode1 = "mov";
immediate_bytes = 1;
byte_operand = true;
reg_in_opcode = true;
@@ -995,12 +1103,12 @@
break;
case 0xB8: case 0xB9: case 0xBA: case 0xBB: case 0xBC: case 0xBD: case 0xBE: case 0xBF:
if ((rex & REX_W) != 0) {
- opcode << "movabsq";
+ opcode1 = "movabsq";
immediate_bytes = 8;
reg_in_opcode = true;
break;
}
- opcode << "mov";
+ opcode1 = "mov";
immediate_bytes = 4;
reg_in_opcode = true;
break;
@@ -1016,7 +1124,7 @@
cx = (*instr == 0xD2) || (*instr == 0xD3);
byte_operand = (*instr == 0xC0);
break;
- case 0xC3: opcode << "ret"; break;
+ case 0xC3: opcode1 = "ret"; break;
case 0xC6:
static const char* c6_opcodes[] = {"mov", "unknown-c6", "unknown-c6",
"unknown-c6", "unknown-c6", "unknown-c6",
@@ -1038,10 +1146,10 @@
has_modrm = true;
reg_is_opcode = true;
break;
- case 0xCC: opcode << "int 3"; break;
+ case 0xCC: opcode1 = "int 3"; break;
case 0xD9:
if (instr[1] == 0xF8) {
- opcode << "fprem";
+ opcode1 = "fprem";
instr++;
} else {
static const char* d9_opcodes[] = {"flds", "unknown-d9", "fsts", "fstps", "fldenv", "fldcw",
@@ -1054,10 +1162,11 @@
break;
case 0xDA:
if (instr[1] == 0xE9) {
- opcode << "fucompp";
+ opcode1 = "fucompp";
instr++;
} else {
- opcode << StringPrintf("unknown opcode '%02X'", *instr);
+ opcode_tmp = StringPrintf("unknown opcode '%02X'", *instr);
+ opcode1 = opcode_tmp.c_str();
}
break;
case 0xDB:
@@ -1087,11 +1196,11 @@
has_modrm = true;
reg_is_opcode = true;
break;
- case 0xE3: opcode << "jecxz"; branch_bytes = 1; break;
- case 0xE8: opcode << "call"; branch_bytes = 4; break;
- case 0xE9: opcode << "jmp"; branch_bytes = 4; break;
- case 0xEB: opcode << "jmp"; branch_bytes = 1; break;
- case 0xF5: opcode << "cmc"; break;
+ case 0xE3: opcode1 = "jecxz"; branch_bytes = 1; break;
+ case 0xE8: opcode1 = "call"; branch_bytes = 4; break;
+ case 0xE9: opcode1 = "jmp"; branch_bytes = 4; break;
+ case 0xEB: opcode1 = "jmp"; branch_bytes = 1; break;
+ case 0xF5: opcode1 = "cmc"; break;
case 0xF6: case 0xF7:
static const char* f7_opcodes[] = {
"test", "unknown-f7", "not", "neg", "mul edx:eax, eax *",
@@ -1120,7 +1229,8 @@
}
break;
default:
- opcode << StringPrintf("unknown opcode '%02X'", *instr);
+ opcode_tmp = StringPrintf("unknown opcode '%02X'", *instr);
+ opcode1 = opcode_tmp.c_str();
break;
}
std::ostringstream args;
@@ -1141,84 +1251,21 @@
uint8_t mod = modrm >> 6;
uint8_t reg_or_opcode = (modrm >> 3) & 7;
uint8_t rm = modrm & 7;
- std::ostringstream address;
- if (mod == 0 && rm == 5) {
- if (!supports_rex_) { // Absolute address.
- address_bits = *reinterpret_cast<const uint32_t*>(instr);
- address << StringPrintf("[0x%x]", address_bits);
- } else { // 64-bit RIP relative addressing.
- address << StringPrintf("[RIP + 0x%x]", *reinterpret_cast<const uint32_t*>(instr));
- }
- instr += 4;
- } else if (rm == 4 && mod != 3) { // SIB
- uint8_t sib = *instr;
- instr++;
- uint8_t scale = (sib >> 6) & 3;
- uint8_t index = (sib >> 3) & 7;
- uint8_t base = sib & 7;
- address << "[";
- if (base != 5 || mod != 0) {
- DumpBaseReg(address, rex64, base);
- if (index != 4) {
- address << " + ";
- }
- }
- if (index != 4) {
- DumpIndexReg(address, rex64, index);
- if (scale != 0) {
- address << StringPrintf(" * %d", 1 << scale);
- }
- }
- if (mod == 0) {
- if (base == 5) {
- if (index != 4) {
- address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(instr));
- } else {
- // 64-bit low 32-bit absolute address, redundant absolute address encoding on 32-bit.
- address_bits = *reinterpret_cast<const uint32_t*>(instr);
- address << StringPrintf("%d", address_bits);
- }
- instr += 4;
- }
- } else if (mod == 1) {
- address << StringPrintf(" + %d", *reinterpret_cast<const int8_t*>(instr));
- instr++;
- } else if (mod == 2) {
- address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(instr));
- instr += 4;
- }
- address << "]";
- } else {
- if (mod == 3) {
- if (!no_ops) {
- DumpRmReg(address, rex_w, rm, byte_operand || byte_second_operand,
- prefix[2], load ? src_reg_file : dst_reg_file);
- }
- } else {
- address << "[";
- DumpBaseReg(address, rex64, rm);
- if (mod == 1) {
- address << StringPrintf(" + %d", *reinterpret_cast<const int8_t*>(instr));
- instr++;
- } else if (mod == 2) {
- address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(instr));
- instr += 4;
- }
- address << "]";
- }
- }
+ std::string address = DumpAddress(mod, rm, rex64, rex_w, no_ops, byte_operand,
+ byte_second_operand, prefix, load, src_reg_file, dst_reg_file,
+ &instr, &address_bits);
if (reg_is_opcode && modrm_opcodes != nullptr) {
- opcode << modrm_opcodes[reg_or_opcode];
+ opcode3 = modrm_opcodes[reg_or_opcode];
}
// Add opcode suffixes to indicate size.
if (byte_operand) {
- opcode << 'b';
+ opcode4 = "b";
} else if ((rex & REX_W) != 0) {
- opcode << 'q';
+ opcode4 = "q";
} else if (prefix[2] == 0x66) {
- opcode << 'w';
+ opcode4 = "w";
}
if (load) {
@@ -1227,11 +1274,11 @@
args << ", ";
}
DumpSegmentOverride(args, prefix[1]);
- args << address.str();
+ args << address;
} else {
DCHECK(store);
DumpSegmentOverride(args, prefix[1]);
- args << address.str();
+ args << address;
if (!reg_is_opcode) {
args << ", ";
DumpReg(args, rex, reg_or_opcode, byte_operand, prefix[2], src_reg_file);
@@ -1289,21 +1336,17 @@
args << " ; ";
Thread::DumpThreadOffset<8>(args, address_bits);
}
- std::stringstream hex;
- for (size_t i = 0; begin_instr + i < instr; ++i) {
- hex << StringPrintf("%02X", begin_instr[i]);
- }
- std::stringstream prefixed_opcode;
+ const char* prefix_str;
switch (prefix[0]) {
- case 0xF0: prefixed_opcode << "lock "; break;
- case 0xF2: prefixed_opcode << "repne "; break;
- case 0xF3: prefixed_opcode << "repe "; break;
- case 0: break;
+ case 0xF0: prefix_str = "lock "; break;
+ case 0xF2: prefix_str = "repne "; break;
+ case 0xF3: prefix_str = "repe "; break;
+ case 0: prefix_str = ""; break;
default: LOG(FATAL) << "Unreachable"; UNREACHABLE();
}
- prefixed_opcode << opcode.str();
os << FormatInstructionPointer(begin_instr)
- << StringPrintf(": %22s \t%-7s ", hex.str().c_str(), prefixed_opcode.str().c_str())
+ << StringPrintf(": %22s \t%-7s%s%s%s%s%s ", DumpCodeHex(begin_instr, instr).c_str(),
+ prefix_str, opcode0, opcode1, opcode2, opcode3, opcode4)
<< args.str() << '\n';
return instr - begin_instr;
} // NOLINT(readability/fn_size)
diff --git a/disassembler/disassembler_x86.h b/disassembler/disassembler_x86.h
index f448662..71c3e41 100644
--- a/disassembler/disassembler_x86.h
+++ b/disassembler/disassembler_x86.h
@@ -22,6 +22,8 @@
namespace art {
namespace x86 {
+enum RegFile { GPR, MMX, SSE };
+
class DisassemblerX86 FINAL : public Disassembler {
public:
DisassemblerX86(DisassemblerOptions* options, bool supports_rex)
@@ -33,6 +35,11 @@
private:
size_t DumpInstruction(std::ostream& os, const uint8_t* instr);
+ std::string DumpAddress(uint8_t mod, uint8_t rm, uint8_t rex64, uint8_t rex_w, bool no_ops,
+ bool byte_operand, bool byte_second_operand, uint8_t* prefix, bool load,
+ RegFile src_reg_file, RegFile dst_reg_file, const uint8_t** instr,
+ uint32_t* address_bits);
+
const bool supports_rex_;
DISALLOW_COPY_AND_ASSIGN(DisassemblerX86);
diff --git a/imgdiag/Android.mk b/imgdiag/Android.mk
new file mode 100644
index 0000000..d5d7c22
--- /dev/null
+++ b/imgdiag/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include art/build/Android.executable.mk
+
+IMGDIAG_SRC_FILES := \
+ imgdiag.cc
+
+# Note that this tool needs to be built for both 32-bit and 64-bit since it requires
+# that the image it's analyzing be the same ISA as the runtime ISA.
+
+# Build variants {target,host} x {debug,ndebug} x {32,64}
+$(eval $(call build-art-multi-executable,imgdiag,$(IMGDIAG_SRC_FILES),libart-compiler libbacktrace,libcutils,libziparchive-host,art/compiler,both))
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
new file mode 100644
index 0000000..9b57ecb
--- /dev/null
+++ b/imgdiag/imgdiag.cc
@@ -0,0 +1,951 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+#include <set>
+#include <map>
+
+#include "base/unix_file/fd_file.h"
+#include "base/stringprintf.h"
+#include "gc/space/image_space.h"
+#include "gc/heap.h"
+#include "mirror/class-inl.h"
+#include "mirror/object-inl.h"
+#include "mirror/art_method-inl.h"
+#include "image.h"
+#include "scoped_thread_state_change.h"
+#include "os.h"
+#include "gc_map.h"
+
+#include "cmdline.h"
+#include "backtrace/BacktraceMap.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <signal.h>
+
+namespace art {
+
+class ImgDiagDumper {
+ public:
+ explicit ImgDiagDumper(std::ostream* os,
+ const ImageHeader& image_header,
+ const char* image_location,
+ pid_t image_diff_pid)
+ : os_(os),
+ image_header_(image_header),
+ image_location_(image_location),
+ image_diff_pid_(image_diff_pid) {}
+
+ bool Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ std::ostream& os = *os_;
+ os << "MAGIC: " << image_header_.GetMagic() << "\n\n";
+
+ os << "IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n";
+
+ bool ret = true;
+ if (image_diff_pid_ >= 0) {
+ os << "IMAGE DIFF PID (" << image_diff_pid_ << "): ";
+ ret = DumpImageDiff(image_diff_pid_);
+ os << "\n\n";
+ } else {
+ os << "IMAGE DIFF PID: disabled\n\n";
+ }
+
+ os << std::flush;
+
+ return ret;
+ }
+
+ private:
+ static bool EndsWith(const std::string& str, const std::string& suffix) {
+ return str.size() >= suffix.size() &&
+ str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
+ }
+
+ // Return suffix of the file path after the last /. (e.g. /foo/bar -> bar, bar -> bar)
+ static std::string BaseName(const std::string& str) {
+ size_t idx = str.rfind("/");
+ if (idx == std::string::npos) {
+ return str;
+ }
+
+ return str.substr(idx + 1);
+ }
+
+ bool DumpImageDiff(pid_t image_diff_pid) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ std::ostream& os = *os_;
+
+ {
+ struct stat sts;
+ std::string proc_pid_str = StringPrintf("/proc/%ld", static_cast<long>(image_diff_pid)); // NOLINT [runtime/int]
+ if (stat(proc_pid_str.c_str(), &sts) == -1) {
+ os << "Process does not exist";
+ return false;
+ }
+ }
+
+ // Open /proc/$pid/maps to view memory maps
+ auto proc_maps = std::unique_ptr<BacktraceMap>(BacktraceMap::Create(image_diff_pid));
+ if (proc_maps == nullptr) {
+ os << "Could not read backtrace maps";
+ return false;
+ }
+
+ bool found_boot_map = false;
+ backtrace_map_t boot_map = backtrace_map_t();
+ // Find the memory map only for boot.art
+ for (const backtrace_map_t& map : *proc_maps) {
+ if (EndsWith(map.name, GetImageLocationBaseName())) {
+ if ((map.flags & PROT_WRITE) != 0) {
+ boot_map = map;
+ found_boot_map = true;
+ break;
+ }
+ // In actuality there's more than 1 map, but the second one is read-only.
+ // The one we care about is the write-able map.
+ // The readonly maps are guaranteed to be identical, so its not interesting to compare
+ // them.
+ }
+ }
+
+ if (!found_boot_map) {
+ os << "Could not find map for " << GetImageLocationBaseName();
+ return false;
+ }
+
+ // Future idea: diff against zygote so we can ignore the shared dirty pages.
+ return DumpImageDiffMap(image_diff_pid, boot_map);
+ }
+
+ // Look at /proc/$pid/mem and only diff the things from there
+ bool DumpImageDiffMap(pid_t image_diff_pid, const backtrace_map_t& boot_map)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ std::ostream& os = *os_;
+ const size_t pointer_size = InstructionSetPointerSize(
+ Runtime::Current()->GetInstructionSet());
+
+ std::string file_name = StringPrintf("/proc/%ld/mem", static_cast<long>(image_diff_pid)); // NOLINT [runtime/int]
+
+ size_t boot_map_size = boot_map.end - boot_map.start;
+
+ // Open /proc/$pid/mem as a file
+ auto map_file = std::unique_ptr<File>(OS::OpenFileForReading(file_name.c_str()));
+ if (map_file == nullptr) {
+ os << "Failed to open " << file_name << " for reading";
+ return false;
+ }
+
+ // Memory-map /proc/$pid/mem subset from the boot map
+ CHECK(boot_map.end >= boot_map.start);
+
+ std::string error_msg;
+
+ // Walk the bytes and diff against our boot image
+ const ImageHeader& boot_image_header = GetBootImageHeader();
+
+ os << "\nObserving boot image header at address "
+ << reinterpret_cast<const void*>(&boot_image_header)
+ << "\n\n";
+
+ const uint8_t* image_begin_unaligned = boot_image_header.GetImageBegin();
+ const uint8_t* image_end_unaligned = image_begin_unaligned + boot_image_header.GetImageSize();
+
+ // Adjust range to nearest page
+ const uint8_t* image_begin = AlignDown(image_begin_unaligned, kPageSize);
+ const uint8_t* image_end = AlignUp(image_end_unaligned, kPageSize);
+
+ ptrdiff_t page_off_begin = boot_image_header.GetImageBegin() - image_begin;
+
+ if (reinterpret_cast<uintptr_t>(image_begin) > boot_map.start ||
+ reinterpret_cast<uintptr_t>(image_end) < boot_map.end) {
+ // Sanity check that we aren't trying to read a completely different boot image
+ os << "Remote boot map is out of range of local boot map: " <<
+ "local begin " << reinterpret_cast<const void*>(image_begin) <<
+ ", local end " << reinterpret_cast<const void*>(image_end) <<
+ ", remote begin " << reinterpret_cast<const void*>(boot_map.start) <<
+ ", remote end " << reinterpret_cast<const void*>(boot_map.end);
+ return false;
+ // If we wanted even more validation we could map the ImageHeader from the file
+ }
+
+ std::vector<uint8_t> remote_contents(boot_map_size);
+ if (!map_file->PreadFully(&remote_contents[0], boot_map_size, boot_map.start)) {
+ os << "Could not fully read file " << file_name;
+ return false;
+ }
+
+ std::string page_map_file_name = StringPrintf("/proc/%ld/pagemap",
+ static_cast<long>(image_diff_pid)); // NOLINT [runtime/int]
+ auto page_map_file = std::unique_ptr<File>(OS::OpenFileForReading(page_map_file_name.c_str()));
+ if (page_map_file == nullptr) {
+ os << "Failed to open " << page_map_file_name << " for reading: " << strerror(errno);
+ return false;
+ }
+
+ // Not truly clean, mmap-ing boot.art again would be more pristine, but close enough
+ const char* clean_page_map_file_name = "/proc/self/pagemap";
+ auto clean_page_map_file = std::unique_ptr<File>(
+ OS::OpenFileForReading(clean_page_map_file_name));
+ if (clean_page_map_file == nullptr) {
+ os << "Failed to open " << clean_page_map_file_name << " for reading: " << strerror(errno);
+ return false;
+ }
+
+ auto kpage_flags_file = std::unique_ptr<File>(OS::OpenFileForReading("/proc/kpageflags"));
+ if (kpage_flags_file == nullptr) {
+ os << "Failed to open /proc/kpageflags for reading: " << strerror(errno);
+ return false;
+ }
+
+ auto kpage_count_file = std::unique_ptr<File>(OS::OpenFileForReading("/proc/kpagecount"));
+ if (kpage_count_file == nullptr) {
+ os << "Failed to open /proc/kpagecount for reading:" << strerror(errno);
+ return false;
+ }
+
+ std::set<size_t> dirty_page_set_remote; // Set of the remote virtual page indices that are dirty
+ std::set<size_t> dirty_page_set_local; // Set of the local virtual page indices that are dirty
+
+ size_t different_int32s = 0;
+ size_t different_bytes = 0;
+ size_t different_pages = 0;
+ size_t virtual_page_idx = 0; // Virtual page number (for an absolute memory address)
+ size_t page_idx = 0; // Page index relative to 0
+ size_t previous_page_idx = 0; // Previous page index relative to 0
+ size_t dirty_pages = 0;
+ size_t private_pages = 0;
+ size_t private_dirty_pages = 0;
+
+ // Iterate through one page at a time. Boot map begin/end already implicitly aligned.
+ for (uintptr_t begin = boot_map.start; begin != boot_map.end; begin += kPageSize) {
+ ptrdiff_t offset = begin - boot_map.start;
+
+ // We treat the image header as part of the memory map for now
+ // If we wanted to change this, we could pass base=start+sizeof(ImageHeader)
+ // But it might still be interesting to see if any of the ImageHeader data mutated
+ const uint8_t* local_ptr = reinterpret_cast<const uint8_t*>(&boot_image_header) + offset;
+ uint8_t* remote_ptr = &remote_contents[offset];
+
+ if (memcmp(local_ptr, remote_ptr, kPageSize) != 0) {
+ different_pages++;
+
+ // Count the number of 32-bit integers that are different.
+ for (size_t i = 0; i < kPageSize / sizeof(uint32_t); ++i) {
+ uint32_t* remote_ptr_int32 = reinterpret_cast<uint32_t*>(remote_ptr);
+ const uint32_t* local_ptr_int32 = reinterpret_cast<const uint32_t*>(local_ptr);
+
+ if (remote_ptr_int32[i] != local_ptr_int32[i]) {
+ different_int32s++;
+ }
+ }
+ }
+ }
+
+ // Iterate through one byte at a time.
+ for (uintptr_t begin = boot_map.start; begin != boot_map.end; ++begin) {
+ previous_page_idx = page_idx;
+ ptrdiff_t offset = begin - boot_map.start;
+
+ // We treat the image header as part of the memory map for now
+ // If we wanted to change this, we could pass base=start+sizeof(ImageHeader)
+ // But it might still be interesting to see if any of the ImageHeader data mutated
+ const uint8_t* local_ptr = reinterpret_cast<const uint8_t*>(&boot_image_header) + offset;
+ uint8_t* remote_ptr = &remote_contents[offset];
+
+ virtual_page_idx = reinterpret_cast<uintptr_t>(local_ptr) / kPageSize;
+
+ // Calculate the page index, relative to the 0th page where the image begins
+ page_idx = (offset + page_off_begin) / kPageSize;
+ if (*local_ptr != *remote_ptr) {
+ // Track number of bytes that are different
+ different_bytes++;
+ }
+
+ // Independently count the # of dirty pages on the remote side
+ size_t remote_virtual_page_idx = begin / kPageSize;
+ if (previous_page_idx != page_idx) {
+ uint64_t page_count = 0xC0FFEE;
+ // TODO: virtual_page_idx needs to be from the same process
+ int dirtiness = (IsPageDirty(page_map_file.get(), // Image-diff-pid procmap
+ clean_page_map_file.get(), // Self procmap
+ kpage_flags_file.get(),
+ kpage_count_file.get(),
+ remote_virtual_page_idx, // potentially "dirty" page
+ virtual_page_idx, // true "clean" page
+ &page_count,
+ &error_msg));
+ if (dirtiness < 0) {
+ os << error_msg;
+ return false;
+ } else if (dirtiness > 0) {
+ dirty_pages++;
+ dirty_page_set_remote.insert(dirty_page_set_remote.end(), remote_virtual_page_idx);
+ dirty_page_set_local.insert(dirty_page_set_local.end(), virtual_page_idx);
+ }
+
+ bool is_dirty = dirtiness > 0;
+ bool is_private = page_count == 1;
+
+ if (page_count == 1) {
+ private_pages++;
+ }
+
+ if (is_dirty && is_private) {
+ private_dirty_pages++;
+ }
+ }
+ }
+
+ // Walk each object in the remote image space and compare it against ours
+ size_t different_objects = 0;
+ std::map<mirror::Class*, int /*count*/> dirty_object_class_map;
+ // Track only the byte-per-byte dirtiness (in bytes)
+ std::map<mirror::Class*, int /*byte_count*/> dirty_object_byte_count;
+ // Track the object-by-object dirtiness (in bytes)
+ std::map<mirror::Class*, int /*byte_count*/> dirty_object_size_in_bytes;
+ std::map<mirror::Class*, int /*count*/> clean_object_class_map;
+
+ std::map<mirror::Class*, std::string> class_to_descriptor_map;
+
+ std::map<off_t /* field offset */, int /* count */> art_method_field_dirty_count;
+ std::vector<mirror::ArtMethod*> art_method_dirty_objects;
+
+ std::map<off_t /* field offset */, int /* count */> class_field_dirty_count;
+ std::vector<mirror::Class*> class_dirty_objects;
+
+ // List of local objects that are clean, but located on dirty pages.
+ std::vector<mirror::Object*> false_dirty_objects;
+ std::map<mirror::Class*, int /*byte_count*/> false_dirty_byte_count;
+ std::map<mirror::Class*, int /*object_count*/> false_dirty_object_count;
+ std::map<mirror::Class*, std::vector<mirror::Object*>> false_dirty_objects_map;
+ size_t false_dirty_object_bytes = 0;
+
+ // Remote pointers to dirty objects
+ std::map<mirror::Class*, std::vector<mirror::Object*>> dirty_objects_by_class;
+ // Look up remote classes by their descriptor
+ std::map<std::string, mirror::Class*> remote_class_map;
+ // Look up local classes by their descriptor
+ std::map<std::string, mirror::Class*> local_class_map;
+
+ size_t dirty_object_bytes = 0;
+ {
+ const uint8_t* begin_image_ptr = image_begin_unaligned;
+ const uint8_t* end_image_ptr = image_end_unaligned;
+
+ const uint8_t* current = begin_image_ptr + RoundUp(sizeof(ImageHeader), kObjectAlignment);
+ while (reinterpret_cast<const uintptr_t>(current)
+ < reinterpret_cast<const uintptr_t>(end_image_ptr)) {
+ CHECK_ALIGNED(current, kObjectAlignment);
+ mirror::Object* obj = reinterpret_cast<mirror::Object*>(const_cast<uint8_t*>(current));
+
+ // Sanity check that we are reading a real object
+ CHECK(obj->GetClass() != nullptr) << "Image object at address " << obj << " has null class";
+ if (kUseBakerOrBrooksReadBarrier) {
+ obj->AssertReadBarrierPointer();
+ }
+
+ // Iterate every page this object belongs to
+ bool on_dirty_page = false;
+ size_t page_off = 0;
+ size_t current_page_idx;
+ uintptr_t object_address;
+ do {
+ object_address = reinterpret_cast<uintptr_t>(current);
+ current_page_idx = object_address / kPageSize + page_off;
+
+ if (dirty_page_set_local.find(current_page_idx) != dirty_page_set_local.end()) {
+ // This object is on a dirty page
+ on_dirty_page = true;
+ }
+
+ page_off++;
+ } while ((current_page_idx * kPageSize) <
+ RoundUp(object_address + obj->SizeOf(), kObjectAlignment));
+
+ mirror::Class* klass = obj->GetClass();
+
+ bool different_object = false;
+
+ // Check against the other object and see if they are different
+ ptrdiff_t offset = current - begin_image_ptr;
+ const uint8_t* current_remote = &remote_contents[offset];
+ mirror::Object* remote_obj = reinterpret_cast<mirror::Object*>(
+ const_cast<uint8_t*>(current_remote));
+ if (memcmp(current, current_remote, obj->SizeOf()) != 0) {
+ different_objects++;
+ dirty_object_bytes += obj->SizeOf();
+
+ ++dirty_object_class_map[klass];
+
+ // Go byte-by-byte and figure out what exactly got dirtied
+ size_t dirty_byte_count_per_object = 0;
+ for (size_t i = 0; i < obj->SizeOf(); ++i) {
+ if (current[i] != current_remote[i]) {
+ dirty_byte_count_per_object++;
+ }
+ }
+ dirty_object_byte_count[klass] += dirty_byte_count_per_object;
+ dirty_object_size_in_bytes[klass] += obj->SizeOf();
+
+ different_object = true;
+
+ dirty_objects_by_class[klass].push_back(remote_obj);
+ } else {
+ ++clean_object_class_map[klass];
+ }
+
+ std::string descriptor = GetClassDescriptor(klass);
+ if (different_object) {
+ if (strcmp(descriptor.c_str(), "Ljava/lang/Class;") == 0) {
+ // this is a "Class"
+ mirror::Class* obj_as_class = reinterpret_cast<mirror::Class*>(remote_obj);
+
+ // print the fields that are dirty
+ for (size_t i = 0; i < obj->SizeOf(); ++i) {
+ if (current[i] != current_remote[i]) {
+ class_field_dirty_count[i]++;
+ }
+ }
+
+ class_dirty_objects.push_back(obj_as_class);
+ } else if (strcmp(descriptor.c_str(), "Ljava/lang/reflect/ArtMethod;") == 0) {
+ // this is an ArtMethod
+ mirror::ArtMethod* art_method = reinterpret_cast<mirror::ArtMethod*>(remote_obj);
+
+ // print the fields that are dirty
+ for (size_t i = 0; i < obj->SizeOf(); ++i) {
+ if (current[i] != current_remote[i]) {
+ art_method_field_dirty_count[i]++;
+ }
+ }
+
+ art_method_dirty_objects.push_back(art_method);
+ }
+ } else if (on_dirty_page) {
+ // This object was either never mutated or got mutated back to the same value.
+ // TODO: Do I want to distinguish a "different" vs a "dirty" page here?
+ false_dirty_objects.push_back(obj);
+ false_dirty_objects_map[klass].push_back(obj);
+ false_dirty_object_bytes += obj->SizeOf();
+ false_dirty_byte_count[obj->GetClass()] += obj->SizeOf();
+ false_dirty_object_count[obj->GetClass()] += 1;
+ }
+
+ if (strcmp(descriptor.c_str(), "Ljava/lang/Class;") == 0) {
+ local_class_map[descriptor] = reinterpret_cast<mirror::Class*>(obj);
+ remote_class_map[descriptor] = reinterpret_cast<mirror::Class*>(remote_obj);
+ }
+
+ // Unconditionally store the class descriptor in case we need it later
+ class_to_descriptor_map[klass] = descriptor;
+ current += RoundUp(obj->SizeOf(), kObjectAlignment);
+ }
+ }
+
+ // Looking at only dirty pages, figure out how many of those bytes belong to dirty objects.
+ float true_dirtied_percent = dirty_object_bytes * 1.0f / (dirty_pages * kPageSize);
+ size_t false_dirty_pages = dirty_pages - different_pages;
+
+ os << "Mapping at [" << reinterpret_cast<void*>(boot_map.start) << ", "
+ << reinterpret_cast<void*>(boot_map.end) << ") had: \n "
+ << different_bytes << " differing bytes, \n "
+ << different_int32s << " differing int32s, \n "
+ << different_objects << " different objects, \n "
+ << dirty_object_bytes << " different object [bytes], \n "
+ << false_dirty_objects.size() << " false dirty objects,\n "
+ << false_dirty_object_bytes << " false dirty object [bytes], \n "
+ << true_dirtied_percent << " different objects-vs-total in a dirty page;\n "
+ << different_pages << " different pages; \n "
+ << dirty_pages << " pages are dirty; \n "
+ << false_dirty_pages << " pages are false dirty; \n "
+ << private_pages << " pages are private; \n "
+ << private_dirty_pages << " pages are Private_Dirty\n "
+ << "";
+
+ // vector of pairs (int count, Class*)
+ auto dirty_object_class_values = SortByValueDesc(dirty_object_class_map);
+ auto clean_object_class_values = SortByValueDesc(clean_object_class_map);
+
+ os << "\n" << " Dirty object count by class:\n";
+ for (const auto& vk_pair : dirty_object_class_values) {
+ int dirty_object_count = vk_pair.first;
+ mirror::Class* klass = vk_pair.second;
+ int object_sizes = dirty_object_size_in_bytes[klass];
+ float avg_dirty_bytes_per_class = dirty_object_byte_count[klass] * 1.0f / object_sizes;
+ float avg_object_size = object_sizes * 1.0f / dirty_object_count;
+ const std::string& descriptor = class_to_descriptor_map[klass];
+ os << " " << PrettyClass(klass) << " ("
+ << "objects: " << dirty_object_count << ", "
+ << "avg dirty bytes: " << avg_dirty_bytes_per_class << ", "
+ << "avg object size: " << avg_object_size << ", "
+ << "class descriptor: '" << descriptor << "'"
+ << ")\n";
+
+ constexpr size_t kMaxAddressPrint = 5;
+ if (strcmp(descriptor.c_str(), "Ljava/lang/reflect/ArtMethod;") == 0) {
+ os << " sample object addresses: ";
+ for (size_t i = 0; i < art_method_dirty_objects.size() && i < kMaxAddressPrint; ++i) {
+ auto art_method = art_method_dirty_objects[i];
+
+ os << reinterpret_cast<void*>(art_method) << ", ";
+ }
+ os << "\n";
+
+ os << " dirty byte +offset:count list = ";
+ auto art_method_field_dirty_count_sorted = SortByValueDesc(art_method_field_dirty_count);
+ for (auto pair : art_method_field_dirty_count_sorted) {
+ off_t offset = pair.second;
+ int count = pair.first;
+
+ os << "+" << offset << ":" << count << ", ";
+ }
+
+ os << "\n";
+
+ os << " field contents:\n";
+ const auto& dirty_objects_list = dirty_objects_by_class[klass];
+ for (mirror::Object* obj : dirty_objects_list) {
+ // remote method
+ auto art_method = reinterpret_cast<mirror::ArtMethod*>(obj);
+
+ // remote class
+ mirror::Class* remote_declaring_class =
+ FixUpRemotePointer(art_method->GetDeclaringClass(), remote_contents, boot_map);
+
+ // local class
+ mirror::Class* declaring_class =
+ RemoteContentsPointerToLocal(remote_declaring_class,
+ remote_contents,
+ boot_image_header);
+
+ os << " " << reinterpret_cast<void*>(obj) << " ";
+ os << " entryPointFromJni: "
+ << reinterpret_cast<const void*>(
+ art_method->GetEntryPointFromJniPtrSize(pointer_size)) << ", ";
+ os << " entryPointFromInterpreter: "
+ << reinterpret_cast<const void*>(
+ art_method->GetEntryPointFromInterpreterPtrSize<kVerifyNone>(pointer_size))
+ << ", ";
+ os << " entryPointFromQuickCompiledCode: "
+ << reinterpret_cast<const void*>(
+ art_method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size))
+ << ", ";
+ os << " isNative? " << (art_method->IsNative() ? "yes" : "no") << ", ";
+ os << " class_status (local): " << declaring_class->GetStatus();
+ os << " class_status (remote): " << remote_declaring_class->GetStatus();
+ os << "\n";
+ }
+ }
+ if (strcmp(descriptor.c_str(), "Ljava/lang/Class;") == 0) {
+ os << " sample object addresses: ";
+ for (size_t i = 0; i < class_dirty_objects.size() && i < kMaxAddressPrint; ++i) {
+ auto class_ptr = class_dirty_objects[i];
+
+ os << reinterpret_cast<void*>(class_ptr) << ", ";
+ }
+ os << "\n";
+
+ os << " dirty byte +offset:count list = ";
+ auto class_field_dirty_count_sorted = SortByValueDesc(class_field_dirty_count);
+ for (auto pair : class_field_dirty_count_sorted) {
+ off_t offset = pair.second;
+ int count = pair.first;
+
+ os << "+" << offset << ":" << count << ", ";
+ }
+ os << "\n";
+
+ os << " field contents:\n";
+ const auto& dirty_objects_list = dirty_objects_by_class[klass];
+ for (mirror::Object* obj : dirty_objects_list) {
+ // remote class object
+ auto remote_klass = reinterpret_cast<mirror::Class*>(obj);
+
+ // local class object
+ auto local_klass = RemoteContentsPointerToLocal(remote_klass,
+ remote_contents,
+ boot_image_header);
+
+ os << " " << reinterpret_cast<void*>(obj) << " ";
+ os << " class_status (remote): " << remote_klass->GetStatus() << ", ";
+ os << " class_status (local): " << local_klass->GetStatus();
+ os << "\n";
+ }
+ }
+ }
+
+ auto false_dirty_object_class_values = SortByValueDesc(false_dirty_object_count);
+
+ os << "\n" << " False-dirty object count by class:\n";
+ for (const auto& vk_pair : false_dirty_object_class_values) {
+ int object_count = vk_pair.first;
+ mirror::Class* klass = vk_pair.second;
+ int object_sizes = false_dirty_byte_count[klass];
+ float avg_object_size = object_sizes * 1.0f / object_count;
+ const std::string& descriptor = class_to_descriptor_map[klass];
+ os << " " << PrettyClass(klass) << " ("
+ << "objects: " << object_count << ", "
+ << "avg object size: " << avg_object_size << ", "
+ << "total bytes: " << object_sizes << ", "
+ << "class descriptor: '" << descriptor << "'"
+ << ")\n";
+
+ if (strcmp(descriptor.c_str(), "Ljava/lang/reflect/ArtMethod;") == 0) {
+ auto& art_method_false_dirty_objects = false_dirty_objects_map[klass];
+
+ os << " field contents:\n";
+ for (mirror::Object* obj : art_method_false_dirty_objects) {
+ // local method
+ auto art_method = reinterpret_cast<mirror::ArtMethod*>(obj);
+
+ // local class
+ mirror::Class* declaring_class = art_method->GetDeclaringClass();
+
+ os << " " << reinterpret_cast<void*>(obj) << " ";
+ os << " entryPointFromJni: "
+ << reinterpret_cast<const void*>(
+ art_method->GetEntryPointFromJniPtrSize(pointer_size)) << ", ";
+ os << " entryPointFromInterpreter: "
+ << reinterpret_cast<const void*>(
+ art_method->GetEntryPointFromInterpreterPtrSize<kVerifyNone>(pointer_size))
+ << ", ";
+ os << " entryPointFromQuickCompiledCode: "
+ << reinterpret_cast<const void*>(
+ art_method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size))
+ << ", ";
+ os << " isNative? " << (art_method->IsNative() ? "yes" : "no") << ", ";
+ os << " class_status (local): " << declaring_class->GetStatus();
+ os << "\n";
+ }
+ }
+ }
+
+ os << "\n" << " Clean object count by class:\n";
+ for (const auto& vk_pair : clean_object_class_values) {
+ os << " " << PrettyClass(vk_pair.second) << " (" << vk_pair.first << ")\n";
+ }
+
+ return true;
+ }
+
+ // Fixup a remote pointer that we read from a foreign boot.art to point to our own memory.
+ // Returned pointer will point to inside of remote_contents.
+ template <typename T>
+ static T* FixUpRemotePointer(T* remote_ptr,
+ std::vector<uint8_t>& remote_contents,
+ const backtrace_map_t& boot_map) {
+ if (remote_ptr == nullptr) {
+ return nullptr;
+ }
+
+ uintptr_t remote = reinterpret_cast<uintptr_t>(remote_ptr);
+
+ CHECK_LE(boot_map.start, remote);
+ CHECK_GT(boot_map.end, remote);
+
+ off_t boot_offset = remote - boot_map.start;
+
+ return reinterpret_cast<T*>(&remote_contents[boot_offset]);
+ }
+
+ template <typename T>
+ static T* RemoteContentsPointerToLocal(T* remote_ptr,
+ std::vector<uint8_t>& remote_contents,
+ const ImageHeader& image_header) {
+ if (remote_ptr == nullptr) {
+ return nullptr;
+ }
+
+ uint8_t* remote = reinterpret_cast<uint8_t*>(remote_ptr);
+ ptrdiff_t boot_offset = remote - &remote_contents[0];
+
+ const uint8_t* local_ptr = reinterpret_cast<const uint8_t*>(&image_header) + boot_offset;
+
+ return reinterpret_cast<T*>(const_cast<uint8_t*>(local_ptr));
+ }
+
+ static std::string GetClassDescriptor(mirror::Class* klass)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ CHECK(klass != nullptr);
+
+ std::string descriptor;
+ const char* descriptor_str = klass->GetDescriptor(&descriptor);
+
+ return std::string(descriptor_str);
+ }
+
+ template <typename K, typename V>
+ static std::vector<std::pair<V, K>> SortByValueDesc(const std::map<K, V> map) {
+ // Store value->key so that we can use the default sort from pair which
+ // sorts by value first and then key
+ std::vector<std::pair<V, K>> value_key_vector;
+
+ for (const auto& kv_pair : map) {
+ value_key_vector.push_back(std::make_pair(kv_pair.second, kv_pair.first));
+ }
+
+ // Sort in reverse (descending order)
+ std::sort(value_key_vector.rbegin(), value_key_vector.rend());
+ return value_key_vector;
+ }
+
+ static bool GetPageFrameNumber(File* page_map_file,
+ size_t virtual_page_index,
+ uint64_t* page_frame_number,
+ std::string* error_msg) {
+ CHECK(page_map_file != nullptr);
+ CHECK(page_frame_number != nullptr);
+ CHECK(error_msg != nullptr);
+
+ constexpr size_t kPageMapEntrySize = sizeof(uint64_t);
+ constexpr uint64_t kPageFrameNumberMask = (1ULL << 55) - 1; // bits 0-54 [in /proc/$pid/pagemap]
+ constexpr uint64_t kPageSoftDirtyMask = (1ULL << 55); // bit 55 [in /proc/$pid/pagemap]
+
+ uint64_t page_map_entry = 0;
+
+ // Read 64-bit entry from /proc/$pid/pagemap to get the physical page frame number
+ if (!page_map_file->PreadFully(&page_map_entry, kPageMapEntrySize,
+ virtual_page_index * kPageMapEntrySize)) {
+ *error_msg = StringPrintf("Failed to read the virtual page index entry from %s",
+ page_map_file->GetPath().c_str());
+ return false;
+ }
+
+ // TODO: seems useless, remove this.
+ bool soft_dirty = (page_map_entry & kPageSoftDirtyMask) != 0;
+ if ((false)) {
+ LOG(VERBOSE) << soft_dirty; // Suppress unused warning
+ UNREACHABLE();
+ }
+
+ *page_frame_number = page_map_entry & kPageFrameNumberMask;
+
+ return true;
+ }
+
+ static int IsPageDirty(File* page_map_file,
+ File* clean_page_map_file,
+ File* kpage_flags_file,
+ File* kpage_count_file,
+ size_t virtual_page_idx,
+ size_t clean_virtual_page_idx,
+ // Out parameters:
+ uint64_t* page_count, std::string* error_msg) {
+ CHECK(page_map_file != nullptr);
+ CHECK(clean_page_map_file != nullptr);
+ CHECK_NE(page_map_file, clean_page_map_file);
+ CHECK(kpage_flags_file != nullptr);
+ CHECK(kpage_count_file != nullptr);
+ CHECK(page_count != nullptr);
+ CHECK(error_msg != nullptr);
+
+ // Constants are from https://www.kernel.org/doc/Documentation/vm/pagemap.txt
+
+ constexpr size_t kPageFlagsEntrySize = sizeof(uint64_t);
+ constexpr size_t kPageCountEntrySize = sizeof(uint64_t);
+ constexpr uint64_t kPageFlagsDirtyMask = (1ULL << 4); // in /proc/kpageflags
+ constexpr uint64_t kPageFlagsNoPageMask = (1ULL << 20); // in /proc/kpageflags
+ constexpr uint64_t kPageFlagsMmapMask = (1ULL << 11); // in /proc/kpageflags
+
+ uint64_t page_frame_number = 0;
+ if (!GetPageFrameNumber(page_map_file, virtual_page_idx, &page_frame_number, error_msg)) {
+ return -1;
+ }
+
+ uint64_t page_frame_number_clean = 0;
+ if (!GetPageFrameNumber(clean_page_map_file, clean_virtual_page_idx, &page_frame_number_clean,
+ error_msg)) {
+ return -1;
+ }
+
+ // Read 64-bit entry from /proc/kpageflags to get the dirty bit for a page
+ uint64_t kpage_flags_entry = 0;
+ if (!kpage_flags_file->PreadFully(&kpage_flags_entry,
+ kPageFlagsEntrySize,
+ page_frame_number * kPageFlagsEntrySize)) {
+ *error_msg = StringPrintf("Failed to read the page flags from %s",
+ kpage_flags_file->GetPath().c_str());
+ return -1;
+ }
+
+ // Read 64-bit entyry from /proc/kpagecount to get mapping counts for a page
+ if (!kpage_count_file->PreadFully(page_count /*out*/,
+ kPageCountEntrySize,
+ page_frame_number * kPageCountEntrySize)) {
+ *error_msg = StringPrintf("Failed to read the page count from %s",
+ kpage_count_file->GetPath().c_str());
+ return -1;
+ }
+
+ // There must be a page frame at the requested address.
+ CHECK_EQ(kpage_flags_entry & kPageFlagsNoPageMask, 0u);
+ // The page frame must be memory mapped
+ CHECK_NE(kpage_flags_entry & kPageFlagsMmapMask, 0u);
+
+ // Page is dirty, i.e. has diverged from file, if the 4th bit is set to 1
+ bool flags_dirty = (kpage_flags_entry & kPageFlagsDirtyMask) != 0;
+
+ // page_frame_number_clean must come from the *same* process
+ // but a *different* mmap than page_frame_number
+ if (flags_dirty) {
+ CHECK_NE(page_frame_number, page_frame_number_clean);
+ }
+
+ return page_frame_number != page_frame_number_clean;
+ }
+
+ static const ImageHeader& GetBootImageHeader() {
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ gc::space::ImageSpace* image_space = heap->GetImageSpace();
+ CHECK(image_space != nullptr);
+ const ImageHeader& image_header = image_space->GetImageHeader();
+ return image_header;
+ }
+
+ private:
+ // Return the image location, stripped of any directories, e.g. "boot.art" or "core.art"
+ std::string GetImageLocationBaseName() const {
+ return BaseName(std::string(image_location_));
+ }
+
+ std::ostream* os_;
+ const ImageHeader& image_header_;
+ const char* image_location_;
+ pid_t image_diff_pid_; // Dump image diff against boot.art if pid is non-negative
+
+ DISALLOW_COPY_AND_ASSIGN(ImgDiagDumper);
+};
+
+static int DumpImage(Runtime* runtime, const char* image_location,
+ std::ostream* os, pid_t image_diff_pid) {
+ ScopedObjectAccess soa(Thread::Current());
+ gc::Heap* heap = runtime->GetHeap();
+ gc::space::ImageSpace* image_space = heap->GetImageSpace();
+ CHECK(image_space != nullptr);
+ const ImageHeader& image_header = image_space->GetImageHeader();
+ if (!image_header.IsValid()) {
+ fprintf(stderr, "Invalid image header %s\n", image_location);
+ return EXIT_FAILURE;
+ }
+
+ ImgDiagDumper img_diag_dumper(os, image_header, image_location, image_diff_pid);
+
+ bool success = img_diag_dumper.Dump();
+ return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+struct ImgDiagArgs : public CmdlineArgs {
+ protected:
+ using Base = CmdlineArgs;
+
+ virtual ParseStatus ParseCustom(const StringPiece& option,
+ std::string* error_msg) OVERRIDE {
+ {
+ ParseStatus base_parse = Base::ParseCustom(option, error_msg);
+ if (base_parse != kParseUnknownArgument) {
+ return base_parse;
+ }
+ }
+
+ if (option.starts_with("--image-diff-pid=")) {
+ const char* image_diff_pid = option.substr(strlen("--image-diff-pid=")).data();
+
+ if (!ParseInt(image_diff_pid, &image_diff_pid_)) {
+ *error_msg = "Image diff pid out of range";
+ return kParseError;
+ }
+ } else {
+ return kParseUnknownArgument;
+ }
+
+ return kParseOk;
+ }
+
+ virtual ParseStatus ParseChecks(std::string* error_msg) OVERRIDE {
+ // Perform the parent checks.
+ ParseStatus parent_checks = Base::ParseChecks(error_msg);
+ if (parent_checks != kParseOk) {
+ return parent_checks;
+ }
+
+ // Perform our own checks.
+
+ if (kill(image_diff_pid_,
+ /*sig*/0) != 0) { // No signal is sent, perform error-checking only.
+ // Check if the pid exists before proceeding.
+ if (errno == ESRCH) {
+ *error_msg = "Process specified does not exist";
+ } else {
+ *error_msg = StringPrintf("Failed to check process status: %s", strerror(errno));
+ }
+ return kParseError;
+ } else if (instruction_set_ != kRuntimeISA) {
+ // Don't allow different ISAs since the images are ISA-specific.
+ // Right now the code assumes both the runtime ISA and the remote ISA are identical.
+ *error_msg = "Must use the default runtime ISA; changing ISA is not supported.";
+ return kParseError;
+ }
+
+ return kParseOk;
+ }
+
+ virtual std::string GetUsage() const {
+ std::string usage;
+
+ usage +=
+ "Usage: imgdiag [options] ...\n"
+ " Example: imgdiag --image-diff-pid=$(pidof dex2oat)\n"
+ " Example: adb shell imgdiag --image-diff-pid=$(pid zygote)\n"
+ "\n";
+
+ usage += Base::GetUsage();
+
+ usage += // Optional.
+ " --image-diff-pid=<pid>: provide the PID of a process whose boot.art you want to diff.\n"
+ " Example: --image-diff-pid=$(pid zygote)\n"
+ "\n";
+
+ return usage;
+ }
+
+ public:
+ pid_t image_diff_pid_ = -1;
+};
+
+struct ImgDiagMain : public CmdlineMain<ImgDiagArgs> {
+ virtual bool ExecuteWithRuntime(Runtime* runtime) {
+ CHECK(args_ != nullptr);
+
+ return DumpImage(runtime,
+ args_->boot_image_location_,
+ args_->os_,
+ args_->image_diff_pid_) == EXIT_SUCCESS;
+ }
+};
+
+} // namespace art
+
+int main(int argc, char** argv) {
+ art::ImgDiagMain main;
+ return main.Main(argc, argv);
+}
diff --git a/imgdiag/imgdiag_test.cc b/imgdiag/imgdiag_test.cc
new file mode 100644
index 0000000..1ac7930
--- /dev/null
+++ b/imgdiag/imgdiag_test.cc
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+#include "common_runtime_test.h"
+
+#include "runtime/os.h"
+#include "runtime/arch/instruction_set.h"
+#include "runtime/utils.h"
+#include "runtime/gc/space/image_space.h"
+#include "runtime/gc/heap.h"
+#include "base/stringprintf.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace art {
+
+static const char* kImgDiagDiffPid = "--image-diff-pid";
+static const char* kImgDiagBootImage = "--boot-image";
+static const char* kImgDiagBinaryName = "imgdiag";
+
+class ImgDiagTest : public CommonRuntimeTest {
+ protected:
+ virtual void SetUp() {
+ CommonRuntimeTest::SetUp();
+
+ // We loaded the runtime with an explicit image. Therefore the image space must exist.
+ gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
+ ASSERT_TRUE(image_space != nullptr);
+ boot_image_location_ = image_space->GetImageLocation();
+ }
+
+ virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
+ // Needs to live until CommonRuntimeTest::SetUp finishes, since we pass it a cstring.
+ runtime_args_image_ = StringPrintf("-Ximage:%s", GetCoreArtLocation().c_str());
+ options->push_back(std::make_pair(runtime_args_image_, nullptr));
+ }
+
+ // Path to the imgdiag(d?)[32|64] binary.
+ std::string GetImgDiagFilePath() {
+ std::string root = GetTestAndroidRoot();
+
+ root += "/bin/";
+ root += kImgDiagBinaryName;
+
+ if (kIsDebugBuild) {
+ root += "d";
+ }
+
+ std::string root32 = root + "32";
+ // If we have both a 32-bit and a 64-bit build, the 32-bit file will have a 32 suffix.
+ if (OS::FileExists(root32.c_str()) && !Is64BitInstructionSet(kRuntimeISA)) {
+ return root32;
+ // Only a single build exists, so the filename never has an extra suffix.
+ } else {
+ return root;
+ }
+ }
+
+ // Run imgdiag with a custom boot image location.
+ bool Exec(pid_t image_diff_pid, const std::string& boot_image, std::string* error_msg) {
+ // Invoke 'img_diag' against the current process.
+ // This should succeed because we have a runtime and so it should
+ // be able to map in the boot.art and do a diff for it.
+ std::string file_path = GetImgDiagFilePath();
+ EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
+
+ // Run imgdiag --image-diff-pid=$image_diff_pid and wait until it's done with a 0 exit code.
+ std::string diff_pid_args;
+ {
+ std::stringstream diff_pid_args_ss;
+ diff_pid_args_ss << kImgDiagDiffPid << "=" << image_diff_pid;
+ diff_pid_args = diff_pid_args_ss.str();
+ }
+ std::string boot_image_args;
+ {
+ boot_image_args = boot_image_args + kImgDiagBootImage + "=" + boot_image;
+ }
+
+ std::vector<std::string> exec_argv = { file_path, diff_pid_args, boot_image_args };
+
+ return ::art::Exec(exec_argv, error_msg);
+ }
+
+ // Run imgdiag with the default boot image location.
+ bool ExecDefaultBootImage(pid_t image_diff_pid, std::string* error_msg) {
+ return Exec(image_diff_pid, boot_image_location_, error_msg);
+ }
+
+ private:
+ std::string runtime_args_image_;
+ std::string boot_image_location_;
+};
+
+#if defined (ART_TARGET)
+TEST_F(ImgDiagTest, ImageDiffPidSelf) {
+#else
+// Can't run this test on the host, it will fail when trying to open /proc/kpagestats
+// because it's root read-only.
+TEST_F(ImgDiagTest, DISABLED_ImageDiffPidSelf) {
+#endif
+ // Invoke 'img_diag' against the current process.
+ // This should succeed because we have a runtime and so it should
+ // be able to map in the boot.art and do a diff for it.
+
+ // Run imgdiag --image-diff-pid=$(self pid) and wait until it's done with a 0 exit code.
+ std::string error_msg;
+ ASSERT_TRUE(ExecDefaultBootImage(getpid(), &error_msg)) << "Failed to execute -- because: "
+ << error_msg;
+}
+
+TEST_F(ImgDiagTest, ImageDiffBadPid) {
+ // Invoke 'img_diag' against a non-existing process. This should fail.
+
+ // Run imgdiag --image-diff-pid=some_bad_pid and wait until it's done with a 0 exit code.
+ std::string error_msg;
+ ASSERT_FALSE(ExecDefaultBootImage(-12345, &error_msg)) << "Incorrectly executed";
+ UNUSED(error_msg);
+}
+
+} // namespace art
diff --git a/oatdump/Android.mk b/oatdump/Android.mk
index b8a3b49..f01afc5 100644
--- a/oatdump/Android.mk
+++ b/oatdump/Android.mk
@@ -21,19 +21,8 @@
OATDUMP_SRC_FILES := \
oatdump.cc
-ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libart-disassembler libart-compiler,art/disassembler art/compiler,target,ndebug))
-endif
-ifeq ($(ART_BUILD_TARGET_DEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libartd-disassembler libartd-compiler,art/disassembler art/compiler,target,debug))
-endif
-
-ifeq ($(ART_BUILD_HOST_NDEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler libart-compiler,art/disassembler art/compiler,host,ndebug))
-endif
-ifeq ($(ART_BUILD_HOST_DEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler libartd-compiler,art/disassembler art/compiler,host,debug))
-endif
+# Build variants {target,host} x {debug,ndebug}
+$(eval $(call build-art-multi-executable,oatdump,$(OATDUMP_SRC_FILES),libart-compiler libart-disassembler,libcutils,,art/compiler art/disassembler))
########################################################################
# oatdump targets
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index b048833..931cca7 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -19,12 +19,13 @@
#include <fstream>
#include <iostream>
+#include <map>
+#include <set>
#include <string>
#include <unordered_map>
#include <vector>
#include "arch/instruction_set_features.h"
-#include "base/stringpiece.h"
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
#include "class_linker-inl.h"
@@ -45,12 +46,10 @@
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
-#include "noop_compiler_callbacks.h"
#include "oat.h"
#include "oat_file-inl.h"
#include "os.h"
#include "output_stream.h"
-#include "runtime.h"
#include "safe_map.h"
#include "scoped_thread_state_change.h"
#include "ScopedLocalRef.h"
@@ -60,58 +59,10 @@
#include "vmap_table.h"
#include "well_known_classes.h"
-namespace art {
+#include <sys/stat.h>
+#include "cmdline.h"
-static void usage() {
- fprintf(stderr,
- "Usage: oatdump [options] ...\n"
- " Example: oatdump --image=$ANDROID_PRODUCT_OUT/system/framework/boot.art\n"
- " Example: adb shell oatdump --image=/system/framework/boot.art\n"
- "\n");
- fprintf(stderr,
- " --oat-file=<file.oat>: specifies an input oat filename.\n"
- " Example: --oat-file=/system/framework/boot.oat\n"
- "\n");
- fprintf(stderr,
- " --image=<file.art>: specifies an input image filename.\n"
- " Example: --image=/system/framework/boot.art\n"
- "\n");
- fprintf(stderr,
- " --boot-image=<file.art>: provide the image file for the boot class path.\n"
- " Example: --boot-image=/system/framework/boot.art\n"
- "\n");
- fprintf(stderr,
- " --instruction-set=(arm|arm64|mips|x86|x86_64): for locating the image\n"
- " file based on the image location set.\n"
- " Example: --instruction-set=x86\n"
- " Default: %s\n"
- "\n",
- GetInstructionSetString(kRuntimeISA));
- fprintf(stderr,
- " --output=<file> may be used to send the output to a file.\n"
- " Example: --output=/tmp/oatdump.txt\n"
- "\n");
- fprintf(stderr,
- " --dump:raw_mapping_table enables dumping of the mapping table.\n"
- " Example: --dump:raw_mapping_table\n"
- "\n");
- fprintf(stderr,
- " --dump:raw_mapping_table enables dumping of the GC map.\n"
- " Example: --dump:raw_gc_map\n"
- "\n");
- fprintf(stderr,
- " --no-dump:vmap may be used to disable vmap dumping.\n"
- " Example: --no-dump:vmap\n"
- "\n");
- fprintf(stderr,
- " --no-disassemble may be used to disable disassembly.\n"
- " Example: --no-disassemble\n"
- "\n");
- fprintf(stderr,
- " --method-filter=<method name>: only dumps methods that contain the filter.\n"
- " Example: --method-filter=foo\n"
- "\n");
-}
+namespace art {
const char* image_roots_descriptions_[] = {
"kResolutionMethod",
@@ -360,15 +311,14 @@
bool dump_vmap,
bool disassemble_code,
bool absolute_addresses,
- const char* method_filter,
- Handle<mirror::ClassLoader>* class_loader)
+ const char* method_filter)
: dump_raw_mapping_table_(dump_raw_mapping_table),
dump_raw_gc_map_(dump_raw_gc_map),
dump_vmap_(dump_vmap),
disassemble_code_(disassemble_code),
absolute_addresses_(absolute_addresses),
method_filter_(method_filter),
- class_loader_(class_loader) {}
+ class_loader_(nullptr) {}
const bool dump_raw_mapping_table_;
const bool dump_raw_gc_map_;
@@ -442,12 +392,6 @@
GetInterpreterToCompiledCodeBridgeOffset);
DUMP_OAT_HEADER_OFFSET("JNI DLSYM LOOKUP",
GetJniDlsymLookupOffset);
- DUMP_OAT_HEADER_OFFSET("PORTABLE IMT CONFLICT TRAMPOLINE",
- GetPortableImtConflictTrampolineOffset);
- DUMP_OAT_HEADER_OFFSET("PORTABLE RESOLUTION TRAMPOLINE",
- GetPortableResolutionTrampolineOffset);
- DUMP_OAT_HEADER_OFFSET("PORTABLE TO INTERPRETER BRIDGE",
- GetPortableToInterpreterBridgeOffset);
DUMP_OAT_HEADER_OFFSET("QUICK GENERIC JNI TRAMPOLINE",
GetQuickGenericJniTrampolineOffset);
DUMP_OAT_HEADER_OFFSET("QUICK IMT CONFLICT TRAMPOLINE",
@@ -851,11 +795,6 @@
} else {
const void* code = oat_method.GetQuickCode();
uint32_t code_size = oat_method.GetQuickCodeSize();
- if (code == nullptr) {
- code = oat_method.GetPortableCode();
- code_size = oat_method.GetPortableCodeSize();
- code_size_offset = 0;
- }
uint32_t code_offset = oat_method.GetCodeOffset();
uint32_t aligned_code_begin = AlignCodeOffset(code_offset);
uint64_t aligned_code_end = aligned_code_begin + code_size;
@@ -1054,23 +993,12 @@
return; // No GC map.
}
const void* quick_code = oat_method.GetQuickCode();
- if (quick_code != nullptr) {
- NativePcOffsetToReferenceMap map(gc_map_raw);
- for (size_t entry = 0; entry < map.NumEntries(); entry++) {
- const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(quick_code) +
- map.GetNativePcOffset(entry);
- os << StringPrintf("%p", native_pc);
- DumpGcMapRegisters(os, oat_method, code_item, map.RegWidth() * 8, map.GetBitMap(entry));
- }
- } else {
- const void* portable_code = oat_method.GetPortableCode();
- CHECK(portable_code != nullptr);
- verifier::DexPcToReferenceMap map(gc_map_raw);
- for (size_t entry = 0; entry < map.NumEntries(); entry++) {
- uint32_t dex_pc = map.GetDexPc(entry);
- os << StringPrintf("0x%08x", dex_pc);
- DumpGcMapRegisters(os, oat_method, code_item, map.RegWidth() * 8, map.GetBitMap(entry));
- }
+ NativePcOffsetToReferenceMap map(gc_map_raw);
+ for (size_t entry = 0; entry < map.NumEntries(); entry++) {
+ const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(quick_code) +
+ map.GetNativePcOffset(entry);
+ os << StringPrintf("%p", native_pc);
+ DumpGcMapRegisters(os, oat_method, code_item, map.RegWidth() * 8, map.GetBitMap(entry));
}
}
@@ -1228,16 +1156,15 @@
void DumpCode(std::ostream& os, verifier::MethodVerifier* verifier,
const OatFile::OatMethod& oat_method, const DexFile::CodeItem* code_item,
bool bad_input, size_t code_size) {
- const void* portable_code = oat_method.GetPortableCode();
const void* quick_code = oat_method.GetQuickCode();
if (code_size == 0) {
code_size = oat_method.GetQuickCodeSize();
}
- if ((code_size == 0) || ((portable_code == nullptr) && (quick_code == nullptr))) {
+ if (code_size == 0 || quick_code == nullptr) {
os << "NO CODE!\n";
return;
- } else if (quick_code != nullptr) {
+ } else {
const uint8_t* quick_native_pc = reinterpret_cast<const uint8_t*>(quick_code);
size_t offset = 0;
while (offset < code_size) {
@@ -1255,9 +1182,6 @@
}
}
}
- } else {
- CHECK(portable_code != nullptr);
- CHECK_EQ(code_size, 0U); // TODO: disassembly of portable is currently not supported.
}
}
@@ -1636,7 +1560,6 @@
state->oat_dumper_->GetOatInstructionSet());
mirror::ArtMethod* method = obj->AsArtMethod();
if (method->IsNative()) {
- // TODO: portable dumping.
DCHECK(method->GetNativeGcMap(image_pointer_size) == nullptr) << PrettyMethod(method);
DCHECK(method->GetMappingTable(image_pointer_size) == nullptr) << PrettyMethod(method);
bool first_occurrence;
@@ -1679,7 +1602,6 @@
state->stats_.vmap_table_bytes += vmap_table_bytes;
}
- // TODO: portable dumping.
const void* quick_oat_code_begin = state->GetQuickOatCodeBegin(method);
const void* quick_oat_code_end = state->GetQuickOatCodeEnd(method);
uint32_t quick_oat_code_size = state->GetQuickOatCodeSize(method);
@@ -2011,45 +1933,6 @@
DISALLOW_COPY_AND_ASSIGN(ImageDumper);
};
-static NoopCompilerCallbacks callbacks;
-
-static Runtime* StartRuntime(const char* boot_image_location, const char* image_location,
- InstructionSet instruction_set) {
- RuntimeOptions options;
- std::string image_option;
- std::string oat_option;
- std::string boot_image_option;
- std::string boot_oat_option;
-
- // We are more like a compiler than a run-time. We don't want to execute code.
- options.push_back(std::make_pair("compilercallbacks", &callbacks));
-
- if (boot_image_location != nullptr) {
- boot_image_option += "-Ximage:";
- boot_image_option += boot_image_location;
- options.push_back(std::make_pair(boot_image_option.c_str(), nullptr));
- }
- if (image_location != nullptr) {
- image_option += "-Ximage:";
- image_option += image_location;
- options.push_back(std::make_pair(image_option.c_str(), nullptr));
- }
- options.push_back(
- std::make_pair("imageinstructionset",
- reinterpret_cast<const void*>(GetInstructionSetString(instruction_set))));
-
- if (!Runtime::Create(options, false)) {
- fprintf(stderr, "Failed to create runtime\n");
- return nullptr;
- }
-
- // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
- // give it away now and then switch to a more manageable ScopedObjectAccess.
- Thread::Current()->TransitionFromRunnableToSuspended(kNative);
-
- return Runtime::Current();
-}
-
static int DumpImage(Runtime* runtime, const char* image_location, OatDumperOptions* options,
std::ostream* os) {
// Dumping the image, no explicit class loader.
@@ -2065,7 +1948,9 @@
fprintf(stderr, "Invalid image header %s\n", image_location);
return EXIT_FAILURE;
}
+
ImageDumper image_dumper(os, *image_space, image_header, options);
+
bool success = image_dumper.Dump();
return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
}
@@ -2083,13 +1968,13 @@
ScopedObjectAccess soa(self);
ClassLinker* class_linker = runtime->GetClassLinker();
class_linker->RegisterOatFile(oat_file);
- std::vector<const DexFile*> dex_files;
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
for (const OatFile::OatDexFile* odf : oat_file->GetOatDexFiles()) {
std::string error_msg;
- const DexFile* dex_file = odf->OpenDexFile(&error_msg);
+ std::unique_ptr<const DexFile> dex_file = odf->OpenDexFile(&error_msg);
CHECK(dex_file != nullptr) << error_msg;
class_linker->RegisterDexFile(*dex_file);
- dex_files.push_back(dex_file);
+ dex_files.push_back(std::move(dex_file));
}
// Need a class loader.
@@ -2098,7 +1983,11 @@
soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
jobject class_loader = soa.Env()->NewGlobalRef(class_loader_local.get());
// Fake that we're a compiler.
- runtime->SetCompileTimeClassPath(class_loader, dex_files);
+ std::vector<const DexFile*> class_path;
+ for (auto& dex_file : dex_files) {
+ class_path.push_back(dex_file.get());
+ }
+ runtime->SetCompileTimeClassPath(class_loader, class_path);
// Use the class loader while dumping.
StackHandleScope<1> scope(self);
@@ -2124,7 +2013,8 @@
static int DumpOat(Runtime* runtime, const char* oat_filename, OatDumperOptions* options,
std::ostream* os) {
std::string error_msg;
- OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false, &error_msg);
+ OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false,
+ &error_msg);
if (oat_file == nullptr) {
fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
return EXIT_FAILURE;
@@ -2139,7 +2029,8 @@
static int SymbolizeOat(const char* oat_filename, std::string& output_name) {
std::string error_msg;
- OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false, &error_msg);
+ OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false,
+ &error_msg);
if (oat_file == nullptr) {
fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
return EXIT_FAILURE;
@@ -2158,86 +2049,110 @@
return EXIT_SUCCESS;
}
-struct OatdumpArgs {
- bool Parse(int argc, char** argv) {
- // Skip over argv[0].
- argv++;
- argc--;
+struct OatdumpArgs : public CmdlineArgs {
+ protected:
+ using Base = CmdlineArgs;
- if (argc == 0) {
- fprintf(stderr, "No arguments specified\n");
- usage();
- return false;
- }
-
- for (int i = 0; i < argc; i++) {
- const StringPiece option(argv[i]);
- if (option.starts_with("--oat-file=")) {
- oat_filename_ = option.substr(strlen("--oat-file=")).data();
- } else if (option.starts_with("--image=")) {
- image_location_ = option.substr(strlen("--image=")).data();
- } else if (option.starts_with("--boot-image=")) {
- boot_image_location_ = option.substr(strlen("--boot-image=")).data();
- } else if (option.starts_with("--instruction-set=")) {
- StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
- instruction_set_ = GetInstructionSetFromString(instruction_set_str.data());
- if (instruction_set_ == kNone) {
- fprintf(stderr, "Unsupported instruction set %s\n", instruction_set_str.data());
- usage();
- return false;
- }
- } else if (option =="--dump:raw_mapping_table") {
- dump_raw_mapping_table_ = true;
- } else if (option == "--dump:raw_gc_map") {
- dump_raw_gc_map_ = true;
- } else if (option == "--no-dump:vmap") {
- dump_vmap_ = false;
- } else if (option == "--no-disassemble") {
- disassemble_code_ = false;
- } else if (option.starts_with("--output=")) {
- output_name_ = option.substr(strlen("--output=")).ToString();
- const char* filename = output_name_.c_str();
- out_.reset(new std::ofstream(filename));
- if (!out_->good()) {
- fprintf(stderr, "Failed to open output filename %s\n", filename);
- usage();
- return false;
- }
- os_ = out_.get();
- } else if (option.starts_with("--symbolize=")) {
- oat_filename_ = option.substr(strlen("--symbolize=")).data();
- symbolize_ = true;
- } else if (option.starts_with("--method-filter=")) {
- method_filter_ = option.substr(strlen("--method-filter=")).data();
- } else {
- fprintf(stderr, "Unknown argument %s\n", option.data());
- usage();
- return false;
+ virtual ParseStatus ParseCustom(const StringPiece& option,
+ std::string* error_msg) OVERRIDE {
+ {
+ ParseStatus base_parse = Base::ParseCustom(option, error_msg);
+ if (base_parse != kParseUnknownArgument) {
+ return base_parse;
}
}
- if (image_location_ == nullptr && oat_filename_ == nullptr) {
- fprintf(stderr, "Either --image or --oat must be specified\n");
- return false;
+ if (option.starts_with("--oat-file=")) {
+ oat_filename_ = option.substr(strlen("--oat-file=")).data();
+ } else if (option.starts_with("--image=")) {
+ image_location_ = option.substr(strlen("--image=")).data();
+ } else if (option =="--dump:raw_mapping_table") {
+ dump_raw_mapping_table_ = true;
+ } else if (option == "--dump:raw_gc_map") {
+ dump_raw_gc_map_ = true;
+ } else if (option == "--no-dump:vmap") {
+ dump_vmap_ = false;
+ } else if (option == "--no-disassemble") {
+ disassemble_code_ = false;
+ } else if (option.starts_with("--symbolize=")) {
+ oat_filename_ = option.substr(strlen("--symbolize=")).data();
+ symbolize_ = true;
+ } else if (option.starts_with("--method-filter=")) {
+ method_filter_ = option.substr(strlen("--method-filter=")).data();
+ } else {
+ return kParseUnknownArgument;
}
- if (image_location_ != nullptr && oat_filename_ != nullptr) {
- fprintf(stderr, "Either --image or --oat must be specified but not both\n");
- return false;
- }
-
- return true;
+ return kParseOk;
}
+ virtual ParseStatus ParseChecks(std::string* error_msg) OVERRIDE {
+ // Infer boot image location from the image location if possible.
+ if (boot_image_location_ == nullptr) {
+ boot_image_location_ = image_location_;
+ }
+
+ // Perform the parent checks.
+ ParseStatus parent_checks = Base::ParseChecks(error_msg);
+ if (parent_checks != kParseOk) {
+ return parent_checks;
+ }
+
+ // Perform our own checks.
+ if (image_location_ == nullptr && oat_filename_ == nullptr) {
+ *error_msg = "Either --image or --oat-file must be specified";
+ return kParseError;
+ } else if (image_location_ != nullptr && oat_filename_ != nullptr) {
+ *error_msg = "Either --image or --oat-file must be specified but not both";
+ return kParseError;
+ }
+
+ return kParseOk;
+ }
+
+ virtual std::string GetUsage() const {
+ std::string usage;
+
+ usage +=
+ "Usage: oatdump [options] ...\n"
+ " Example: oatdump --image=$ANDROID_PRODUCT_OUT/system/framework/boot.art\n"
+ " Example: adb shell oatdump --image=/system/framework/boot.art\n"
+ "\n"
+ // Either oat-file or image is required.
+ " --oat-file=<file.oat>: specifies an input oat filename.\n"
+ " Example: --oat-file=/system/framework/boot.oat\n"
+ "\n"
+ " --image=<file.art>: specifies an input image location.\n"
+ " Example: --image=/system/framework/boot.art\n"
+ "\n";
+
+ usage += Base::GetUsage();
+
+ usage += // Optional.
+ " --dump:raw_mapping_table enables dumping of the mapping table.\n"
+ " Example: --dump:raw_mapping_table\n"
+ "\n"
+ " --dump:raw_mapping_table enables dumping of the GC map.\n"
+ " Example: --dump:raw_gc_map\n"
+ "\n"
+ " --no-dump:vmap may be used to disable vmap dumping.\n"
+ " Example: --no-dump:vmap\n"
+ "\n"
+ " --no-disassemble may be used to disable disassembly.\n"
+ " Example: --no-disassemble\n"
+ "\n"
+ " --method-filter=<method name>: only dumps methods that contain the filter.\n"
+ " Example: --method-filter=foo\n"
+ "\n";
+
+ return usage;
+ }
+
+ public:
const char* oat_filename_ = nullptr;
const char* method_filter_ = "";
const char* image_location_ = nullptr;
- const char* boot_image_location_ = nullptr;
- InstructionSet instruction_set_ = kRuntimeISA;
std::string elf_filename_prefix_;
- std::ostream* os_ = &std::cout;
- std::unique_ptr<std::ofstream> out_;
- std::string output_name_;
bool dump_raw_mapping_table_ = false;
bool dump_raw_gc_map_ = false;
bool dump_vmap_ = true;
@@ -2245,55 +2160,61 @@
bool symbolize_ = false;
};
-static int oatdump(int argc, char** argv) {
- InitLogging(argv);
+struct OatdumpMain : public CmdlineMain<OatdumpArgs> {
+ virtual bool NeedsRuntime() OVERRIDE {
+ CHECK(args_ != nullptr);
- OatdumpArgs args;
- if (!args.Parse(argc, argv)) {
- return EXIT_FAILURE;
+ // If we are only doing the oat file, disable absolute_addresses. Keep them for image dumping.
+ bool absolute_addresses = (args_->oat_filename_ == nullptr);
+
+ oat_dumper_options_ = std::unique_ptr<OatDumperOptions>(new OatDumperOptions(
+ args_->dump_raw_mapping_table_,
+ args_->dump_raw_gc_map_,
+ args_->dump_vmap_,
+ args_->disassemble_code_,
+ absolute_addresses,
+ args_->method_filter_));
+
+ return (args_->boot_image_location_ != nullptr || args_->image_location_ != nullptr) &&
+ !args_->symbolize_;
}
- // If we are only doing the oat file, disable absolute_addresses. Keep them for image dumping.
- bool absolute_addresses = (args.oat_filename_ == nullptr);
+ virtual bool ExecuteWithoutRuntime() OVERRIDE {
+ CHECK(args_ != nullptr);
+ CHECK(args_->oat_filename_ != nullptr);
- std::unique_ptr<OatDumperOptions> oat_dumper_options(new OatDumperOptions(
- args.dump_raw_mapping_table_,
- args.dump_raw_gc_map_,
- args.dump_vmap_,
- args.disassemble_code_,
- absolute_addresses,
- args.method_filter_,
- nullptr));
-
- std::unique_ptr<Runtime> runtime;
- if ((args.boot_image_location_ != nullptr || args.image_location_ != nullptr) &&
- !args.symbolize_) {
- // If we have a boot image option, try to start the runtime; except when just symbolizing.
- runtime.reset(StartRuntime(args.boot_image_location_,
- args.image_location_,
- args.instruction_set_));
- } else {
MemMap::Init();
- }
- if (args.oat_filename_ != nullptr) {
- if (args.symbolize_) {
- return SymbolizeOat(args.oat_filename_, args.output_name_);
+ if (args_->symbolize_) {
+ return SymbolizeOat(args_->oat_filename_, args_->output_name_) == EXIT_SUCCESS;
} else {
- return DumpOat(runtime.get(), args.oat_filename_, oat_dumper_options.release(), args.os_);
+ return DumpOat(nullptr,
+ args_->oat_filename_,
+ oat_dumper_options_.release(),
+ args_->os_) == EXIT_SUCCESS;
}
}
- if (runtime.get() == nullptr) {
- // We need the runtime when printing an image.
- return EXIT_FAILURE;
+ virtual bool ExecuteWithRuntime(Runtime* runtime) {
+ CHECK(args_ != nullptr);
+
+ if (args_->oat_filename_ != nullptr) {
+ return DumpOat(runtime,
+ args_->oat_filename_,
+ oat_dumper_options_.release(),
+ args_->os_) == EXIT_SUCCESS;
+ }
+
+ return DumpImage(runtime, args_->image_location_, oat_dumper_options_.release(), args_->os_)
+ == EXIT_SUCCESS;
}
- return DumpImage(runtime.get(), args.image_location_, oat_dumper_options.release(), args.os_);
-}
+ std::unique_ptr<OatDumperOptions> oat_dumper_options_;
+};
} // namespace art
int main(int argc, char** argv) {
- return art::oatdump(argc, argv);
+ art::OatdumpMain main;
+ return main.Main(argc, argv);
}
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 68fd15b..2059a96 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -48,23 +48,6 @@
namespace art {
-static InstructionSet ElfISAToInstructionSet(Elf32_Word isa) {
- switch (isa) {
- case EM_ARM:
- return kArm;
- case EM_AARCH64:
- return kArm64;
- case EM_386:
- return kX86;
- case EM_X86_64:
- return kX86_64;
- case EM_MIPS:
- return kMips;
- default:
- return kNone;
- }
-}
-
static bool LocationToFilename(const std::string& location, InstructionSet isa,
std::string* filename) {
bool has_system = false;
@@ -212,7 +195,7 @@
LOG(ERROR) << "unable to read elf header";
return false;
}
- isa = ElfISAToInstructionSet(elf_hdr.e_machine);
+ isa = GetInstructionSetFromELF(elf_hdr.e_machine, elf_hdr.e_flags);
}
const char* isa_name = GetInstructionSetString(isa);
std::string image_filename;
@@ -540,12 +523,6 @@
const size_t pointer_size = InstructionSetPointerSize(isa_);
// Just update the entry points if it looks like we should.
// TODO: sanity check all the pointers' values
- uintptr_t portable = reinterpret_cast<uintptr_t>(
- object->GetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(pointer_size));
- if (portable != 0) {
- copy->SetEntryPointFromPortableCompiledCodePtrSize(reinterpret_cast<void*>(portable + delta_),
- pointer_size);
- }
uintptr_t quick= reinterpret_cast<uintptr_t>(
object->GetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(pointer_size));
if (quick != 0) {
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 7dfdb75..4714610 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -31,9 +31,7 @@
base/stringprintf.cc \
base/timing_logger.cc \
base/unix_file/fd_file.cc \
- base/unix_file/null_file.cc \
base/unix_file/random_access_file_utils.cc \
- base/unix_file/string_file.cc \
check_jni.cc \
class_linker.cc \
common_throws.cc \
@@ -66,9 +64,11 @@
gc/space/image_space.cc \
gc/space/large_object_space.cc \
gc/space/malloc_space.cc \
+ gc/space/region_space.cc \
gc/space/rosalloc_space.cc \
gc/space/space.cc \
gc/space/zygote_space.cc \
+ gc/task_processor.cc \
hprof/hprof.cc \
image.cc \
indirect_reference_table.cc \
@@ -139,6 +139,7 @@
reference_table.cc \
reflection.cc \
runtime.cc \
+ runtime_options.cc \
signal_catcher.cc \
stack.cc \
thread.cc \
@@ -171,6 +172,8 @@
arch/arm64/registers_arm64.cc \
arch/mips/instruction_set_features_mips.cc \
arch/mips/registers_mips.cc \
+ arch/mips64/instruction_set_features_mips64.cc \
+ arch/mips64/registers_mips64.cc \
arch/x86/instruction_set_features_x86.cc \
arch/x86/registers_x86.cc \
arch/x86_64/registers_x86_64.cc \
@@ -178,17 +181,6 @@
entrypoints/interpreter/interpreter_entrypoints.cc \
entrypoints/jni/jni_entrypoints.cc \
entrypoints/math_entrypoints.cc \
- entrypoints/portable/portable_alloc_entrypoints.cc \
- entrypoints/portable/portable_cast_entrypoints.cc \
- entrypoints/portable/portable_dexcache_entrypoints.cc \
- entrypoints/portable/portable_field_entrypoints.cc \
- entrypoints/portable/portable_fillarray_entrypoints.cc \
- entrypoints/portable/portable_invoke_entrypoints.cc \
- entrypoints/portable/portable_jni_entrypoints.cc \
- entrypoints/portable/portable_lock_entrypoints.cc \
- entrypoints/portable/portable_thread_entrypoints.cc \
- entrypoints/portable/portable_throw_entrypoints.cc \
- entrypoints/portable/portable_trampoline_entrypoints.cc \
entrypoints/quick/quick_alloc_entrypoints.cc \
entrypoints/quick/quick_cast_entrypoints.cc \
entrypoints/quick/quick_deoptimization_entrypoints.cc \
@@ -223,7 +215,6 @@
arch/arm/instruction_set_features_assembly_tests.S \
arch/arm/jni_entrypoints_arm.S \
arch/arm/memcmp16_arm.S \
- arch/arm/portable_entrypoints_arm.S \
arch/arm/quick_entrypoints_arm.S \
arch/arm/quick_entrypoints_cc_arm.cc \
arch/arm/thread_arm.cc \
@@ -234,7 +225,6 @@
arch/arm64/entrypoints_init_arm64.cc \
arch/arm64/jni_entrypoints_arm64.S \
arch/arm64/memcmp16_arm64.S \
- arch/arm64/portable_entrypoints_arm64.S \
arch/arm64/quick_entrypoints_arm64.S \
arch/arm64/thread_arm64.cc \
monitor_pool.cc \
@@ -245,7 +235,6 @@
arch/x86/entrypoints_init_x86.cc \
arch/x86/jni_entrypoints_x86.S \
arch/x86/memcmp16_x86.S \
- arch/x86/portable_entrypoints_x86.S \
arch/x86/quick_entrypoints_x86.S \
arch/x86/thread_x86.cc \
arch/x86/fault_handler_x86.cc
@@ -260,7 +249,6 @@
arch/x86_64/entrypoints_init_x86_64.cc \
arch/x86_64/jni_entrypoints_x86_64.S \
arch/x86_64/memcmp16_x86_64.S \
- arch/x86_64/portable_entrypoints_x86_64.S \
arch/x86_64/quick_entrypoints_x86_64.S \
arch/x86_64/thread_x86_64.cc \
monitor_pool.cc \
@@ -274,14 +262,19 @@
arch/mips/entrypoints_init_mips.cc \
arch/mips/jni_entrypoints_mips.S \
arch/mips/memcmp16_mips.S \
- arch/mips/portable_entrypoints_mips.S \
arch/mips/quick_entrypoints_mips.S \
arch/mips/thread_mips.cc \
arch/mips/fault_handler_mips.cc
-ifeq ($(TARGET_ARCH),mips64)
-$(info TODOMips64: $(LOCAL_PATH)/Android.mk Add mips64 specific runtime files)
-endif # TARGET_ARCH != mips64
+LIBART_TARGET_SRC_FILES_mips64 := \
+ arch/mips64/context_mips64.cc \
+ arch/mips64/entrypoints_init_mips64.cc \
+ arch/mips64/jni_entrypoints_mips64.S \
+ arch/mips64/memcmp16_mips64.S \
+ arch/mips64/quick_entrypoints_mips64.S \
+ arch/mips64/thread_mips64.cc \
+ monitor_pool.cc \
+ arch/mips64/fault_handler_mips64.cc
LIBART_HOST_SRC_FILES := \
$(LIBART_COMMON_SRC_FILES) \
@@ -303,10 +296,12 @@
base/unix_file/fd_file.h \
dex_file.h \
dex_instruction.h \
+ gc_root.h \
gc/allocator/rosalloc.h \
gc/collector/gc_type.h \
gc/allocator_type.h \
gc/collector_type.h \
+ gc/space/region_space.h \
gc/space/space.h \
gc/heap.h \
instrumentation.h \
@@ -327,9 +322,6 @@
verifier/method_verifier.h
LIBART_CFLAGS := -DBUILDING_LIBART=1
-ifeq ($(ART_USE_PORTABLE_COMPILER),true)
- LIBART_CFLAGS += -DART_USE_PORTABLE_COMPILER=1
-endif
ifeq ($(MALLOC_IMPL),dlmalloc)
LIBART_CFLAGS += -DUSE_DLMALLOC
@@ -467,10 +459,11 @@
endif
LOCAL_C_INCLUDES += $$(ART_C_INCLUDES)
+ LOCAL_C_INCLUDES += art/cmdline
LOCAL_C_INCLUDES += art/sigchainlib
+ LOCAL_C_INCLUDES += art
LOCAL_SHARED_LIBRARIES := libnativehelper libnativebridge libsigchain
- include external/libcxx/libcxx.mk
LOCAL_SHARED_LIBRARIES += libbacktrace
ifeq ($$(art_target_or_host),target)
LOCAL_SHARED_LIBRARIES += libdl
@@ -485,14 +478,6 @@
# For ashmem_create_region.
LOCAL_STATIC_LIBRARIES += libcutils
endif
- ifeq ($$(ART_USE_PORTABLE_COMPILER),true)
- include $$(LLVM_GEN_INTRINSICS_MK)
- ifeq ($$(art_target_or_host),target)
- include $$(LLVM_DEVICE_BUILD_MK)
- else # host
- include $$(LLVM_HOST_BUILD_MK)
- endif
- endif
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
LOCAL_ADDITIONAL_DEPENDENCIES += $$(LOCAL_PATH)/Android.mk
@@ -500,6 +485,8 @@
LOCAL_MODULE_TARGET_ARCH := $$(ART_TARGET_SUPPORTED_ARCH)
endif
+ LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
+
ifeq ($$(art_target_or_host),target)
ifneq ($$(art_ndebug_or_debug),debug)
# Leave the symbols in the shared library so that stack unwinders can
@@ -551,6 +538,7 @@
LIBART_TARGET_SRC_FILES_x86 :=
LIBART_TARGET_SRC_FILES_x86_64 :=
LIBART_TARGET_SRC_FILES_mips :=
+LIBART_TARGET_SRC_FILES_mips64 :=
LIBART_HOST_SRC_FILES :=
LIBART_HOST_SRC_FILES_32 :=
LIBART_HOST_SRC_FILES_64 :=
diff --git a/runtime/arch/arch_test.cc b/runtime/arch/arch_test.cc
index cac500c..ab6b00b 100644
--- a/runtime/arch/arch_test.cc
+++ b/runtime/arch/arch_test.cc
@@ -82,6 +82,16 @@
#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
}
+namespace mips64 {
+#include "arch/mips64/asm_support_mips64.h"
+static constexpr size_t kFrameSizeSaveAllCalleeSave = FRAME_SIZE_SAVE_ALL_CALLEE_SAVE;
+#undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+static constexpr size_t kFrameSizeRefsOnlyCalleeSave = FRAME_SIZE_REFS_ONLY_CALLEE_SAVE;
+#undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+static constexpr size_t kFrameSizeRefsAndArgsCalleeSave = FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE;
+#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+}
+
namespace x86 {
#include "arch/x86/asm_support_x86.h"
static constexpr size_t kFrameSizeSaveAllCalleeSave = FRAME_SIZE_SAVE_ALL_CALLEE_SAVE;
@@ -124,6 +134,13 @@
mips::kFrameSizeRefsAndArgsCalleeSave);
}
+TEST_F(ArchTest, MIPS64) {
+ CheckFrameSize(InstructionSet::kMips64, Runtime::kSaveAll, mips64::kFrameSizeSaveAllCalleeSave);
+ CheckFrameSize(InstructionSet::kMips64, Runtime::kRefsOnly, mips64::kFrameSizeRefsOnlyCalleeSave);
+ CheckFrameSize(InstructionSet::kMips64, Runtime::kRefsAndArgs,
+ mips64::kFrameSizeRefsAndArgsCalleeSave);
+}
+
TEST_F(ArchTest, X86) {
CheckFrameSize(InstructionSet::kX86, Runtime::kSaveAll, x86::kFrameSizeSaveAllCalleeSave);
CheckFrameSize(InstructionSet::kX86, Runtime::kRefsOnly, x86::kFrameSizeRefsOnlyCalleeSave);
diff --git a/runtime/arch/arm/context_arm.cc b/runtime/arch/arm/context_arm.cc
index 9e8d282..c181e43 100644
--- a/runtime/arch/arm/context_arm.cc
+++ b/runtime/arch/arm/context_arm.cc
@@ -67,26 +67,18 @@
}
}
-bool ArmContext::SetGPR(uint32_t reg, uintptr_t value) {
+void ArmContext::SetGPR(uint32_t reg, uintptr_t value) {
DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters));
+ DCHECK(IsAccessibleGPR(reg));
DCHECK_NE(gprs_[reg], &gZero); // Can't overwrite this static value since they are never reset.
- if (gprs_[reg] != nullptr) {
- *gprs_[reg] = value;
- return true;
- } else {
- return false;
- }
+ *gprs_[reg] = value;
}
-bool ArmContext::SetFPR(uint32_t reg, uintptr_t value) {
+void ArmContext::SetFPR(uint32_t reg, uintptr_t value) {
DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfSRegisters));
+ DCHECK(IsAccessibleFPR(reg));
DCHECK_NE(fprs_[reg], &gZero); // Can't overwrite this static value since they are never reset.
- if (fprs_[reg] != nullptr) {
- *fprs_[reg] = value;
- return true;
- } else {
- return false;
- }
+ *fprs_[reg] = value;
}
void ArmContext::SmashCallerSaves() {
diff --git a/runtime/arch/arm/context_arm.h b/runtime/arch/arm/context_arm.h
index e894f16..1ca973e 100644
--- a/runtime/arch/arm/context_arm.h
+++ b/runtime/arch/arm/context_arm.h
@@ -37,13 +37,16 @@
void FillCalleeSaves(const StackVisitor& fr) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetSP(uintptr_t new_sp) OVERRIDE {
- bool success = SetGPR(SP, new_sp);
- CHECK(success) << "Failed to set SP register";
+ SetGPR(SP, new_sp);
}
void SetPC(uintptr_t new_pc) OVERRIDE {
- bool success = SetGPR(PC, new_pc);
- CHECK(success) << "Failed to set PC register";
+ SetGPR(PC, new_pc);
+ }
+
+ bool IsAccessibleGPR(uint32_t reg) OVERRIDE {
+ DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters));
+ return gprs_[reg] != nullptr;
}
uintptr_t* GetGPRAddress(uint32_t reg) OVERRIDE {
@@ -51,31 +54,26 @@
return gprs_[reg];
}
- bool GetGPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+ uintptr_t GetGPR(uint32_t reg) OVERRIDE {
DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters));
- if (gprs_[reg] == nullptr) {
- return false;
- } else {
- DCHECK(val != nullptr);
- *val = *gprs_[reg];
- return true;
- }
+ DCHECK(IsAccessibleGPR(reg));
+ return *gprs_[reg];
}
- bool SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
+ void SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
- bool GetFPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+ bool IsAccessibleFPR(uint32_t reg) OVERRIDE {
DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfSRegisters));
- if (fprs_[reg] == nullptr) {
- return false;
- } else {
- DCHECK(val != nullptr);
- *val = *fprs_[reg];
- return true;
- }
+ return fprs_[reg] != nullptr;
}
- bool SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
+ uintptr_t GetFPR(uint32_t reg) OVERRIDE {
+ DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfSRegisters));
+ DCHECK(IsAccessibleFPR(reg));
+ return *fprs_[reg];
+ }
+
+ void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
void SmashCallerSaves() OVERRIDE;
void DoLongJump() OVERRIDE;
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index 85a0dd2..ce0e614 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -16,7 +16,6 @@
#include "entrypoints/interpreter/interpreter_entrypoints.h"
#include "entrypoints/jni/jni_entrypoints.h"
-#include "entrypoints/portable/portable_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_default_externs.h"
#include "entrypoints/quick/quick_entrypoints.h"
@@ -49,7 +48,7 @@
extern "C" int64_t __aeabi_ldivmod(int64_t, int64_t);
void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
- PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
+ QuickEntryPoints* qpoints) {
// Interpreter
ipoints->pInterpreterToInterpreterBridge = artInterpreterToInterpreterBridge;
ipoints->pInterpreterToCompiledCodeBridge = artInterpreterToCompiledCodeBridge;
@@ -57,10 +56,6 @@
// JNI
jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
- // Portable
- ppoints->pPortableResolutionTrampoline = art_portable_resolution_trampoline;
- ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge;
-
// Alloc
ResetQuickAllocEntryPoints(qpoints);
diff --git a/runtime/arch/arm/portable_entrypoints_arm.S b/runtime/arch/arm/portable_entrypoints_arm.S
deleted file mode 100644
index 89ac1f7..0000000
--- a/runtime/arch/arm/portable_entrypoints_arm.S
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "asm_support_arm.S"
-
- /*
- * Portable invocation stub.
- * On entry:
- * r0 = method pointer
- * r1 = argument array or NULL for no argument methods
- * r2 = size of argument array in bytes
- * r3 = (managed) thread pointer
- * [sp] = JValue* result
- * [sp + 4] = result type char
- */
-ENTRY art_portable_invoke_stub
- push {r0, r4, r5, r9, r11, lr} @ spill regs
- .save {r0, r4, r5, r9, r11, lr}
- .cfi_adjust_cfa_offset 24
- .cfi_rel_offset r0, 0
- .cfi_rel_offset r4, 4
- .cfi_rel_offset r5, 8
- .cfi_rel_offset r9, 12
- .cfi_rel_offset r11, 16
- .cfi_rel_offset lr, 20
- mov r11, sp @ save the stack pointer
- .cfi_def_cfa_register r11
- @.movsp r11
- mov r9, r3 @ move managed thread pointer into r9
- mov r4, #SUSPEND_CHECK_INTERVAL @ reset r4 to suspend check interval
- add r5, r2, #16 @ create space for method pointer in frame
- and r5, #0xFFFFFFF0 @ align frame size to 16 bytes
- sub sp, r5 @ reserve stack space for argument array
- add r0, sp, #4 @ pass stack pointer + method ptr as dest for memcpy
- bl memcpy @ memcpy (dest, src, bytes)
- ldr r0, [r11] @ restore method*
- ldr r1, [sp, #4] @ copy arg value for r1
- ldr r2, [sp, #8] @ copy arg value for r2
- ldr r3, [sp, #12] @ copy arg value for r3
- mov ip, #0 @ set ip to 0
- str ip, [sp] @ store NULL for method* at bottom of frame
- add sp, #16 @ first 4 args are not passed on stack for portable
- ldr ip, [r0, #MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32] @ get pointer to the code
- blx ip @ call the method
- mov sp, r11 @ restore the stack pointer
- ldr ip, [sp, #24] @ load the result pointer
- strd r0, [ip] @ store r0/r1 into result pointer
- pop {r0, r4, r5, r9, r11, lr} @ restore spill regs
- .cfi_adjust_cfa_offset -24
- bx lr
-END art_portable_invoke_stub
-
- .extern artPortableProxyInvokeHandler
-ENTRY art_portable_proxy_invoke_handler
- @ Fake callee save ref and args frame set up, note portable doesn't use callee save frames.
- @ TODO: just save the registers that are needed in artPortableProxyInvokeHandler.
- push {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves
- .save {r1-r3, r5-r8, r10-r11, lr}
- .cfi_adjust_cfa_offset 40
- .cfi_rel_offset r1, 0
- .cfi_rel_offset r2, 4
- .cfi_rel_offset r3, 8
- .cfi_rel_offset r5, 12
- .cfi_rel_offset r6, 16
- .cfi_rel_offset r7, 20
- .cfi_rel_offset r8, 24
- .cfi_rel_offset r10, 28
- .cfi_rel_offset r11, 32
- .cfi_rel_offset lr, 36
- sub sp, #8 @ 2 words of space, bottom word will hold Method*
- .pad #8
- .cfi_adjust_cfa_offset 8
- @ Begin argument set up.
- str r0, [sp, #0] @ place proxy method at bottom of frame
- mov r2, r9 @ pass Thread::Current
- mov r3, sp @ pass SP
- blx artPortableProxyInvokeHandler @ (Method* proxy method, receiver, Thread*, SP)
- ldr r12, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_
- ldr lr, [sp, #44] @ restore lr
- add sp, #48 @ pop frame
- .cfi_adjust_cfa_offset -48
- bx lr @ return
-END art_portable_proxy_invoke_handler
-
- .extern artPortableResolutionTrampoline
-ENTRY art_portable_resolution_trampoline
- @ Fake callee save ref and args frame set up, note portable doesn't use callee save frames.
- @ TODO: just save the registers that are needed in artPortableResolutionTrampoline.
- push {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves
- .save {r1-r3, r5-r8, r10-r11, lr}
- .cfi_adjust_cfa_offset 40
- .cfi_rel_offset r1, 0
- .cfi_rel_offset r2, 4
- .cfi_rel_offset r3, 8
- .cfi_rel_offset r5, 12
- .cfi_rel_offset r6, 16
- .cfi_rel_offset r7, 20
- .cfi_rel_offset r8, 24
- .cfi_rel_offset r10, 28
- .cfi_rel_offset r11, 32
- .cfi_rel_offset lr, 36
- sub sp, #8 @ 2 words of space, bottom word will hold Method*
- .pad #8
- .cfi_adjust_cfa_offset 8
- mov r2, r9 @ pass Thread::Current
- mov r3, sp @ pass SP
- blx artPortableResolutionTrampoline @ (Method* called, receiver, Thread*, SP)
- cmp r0, #0 @ is code pointer null?
- beq 1f @ goto exception
- mov r12, r0
- ldr r0, [sp, #0] @ load resolved method in r0
- ldr r1, [sp, #8] @ restore non-callee save r1
- ldrd r2, [sp, #12] @ restore non-callee saves r2-r3
- ldr lr, [sp, #44] @ restore lr
- add sp, #48 @ rewind sp
- .cfi_adjust_cfa_offset -48
- bx r12 @ tail-call into actual code
-1:
- ldr r1, [sp, #8] @ restore non-callee save r1
- ldrd r2, [sp, #12] @ restore non-callee saves r2-r3
- ldr lr, [sp, #44] @ restore lr
- add sp, #48 @ rewind sp
- .cfi_adjust_cfa_offset -48
- bx lr
-END art_portable_resolution_trampoline
-
- .extern artPortableToInterpreterBridge
-ENTRY art_portable_to_interpreter_bridge
- @ Fake callee save ref and args frame set up, note portable doesn't use callee save frames.
- @ TODO: just save the registers that are needed in artPortableToInterpreterBridge.
- push {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves
- .save {r1-r3, r5-r8, r10-r11, lr}
- .cfi_adjust_cfa_offset 40
- .cfi_rel_offset r1, 0
- .cfi_rel_offset r2, 4
- .cfi_rel_offset r3, 8
- .cfi_rel_offset r5, 12
- .cfi_rel_offset r6, 16
- .cfi_rel_offset r7, 20
- .cfi_rel_offset r8, 24
- .cfi_rel_offset r10, 28
- .cfi_rel_offset r11, 32
- .cfi_rel_offset lr, 36
- sub sp, #8 @ 2 words of space, bottom word will hold Method*
- .pad #8
- .cfi_adjust_cfa_offset 8
- mov r1, r9 @ pass Thread::Current
- mov r2, sp @ pass SP
- blx artPortableToInterpreterBridge @ (Method* method, Thread*, SP)
- ldr lr, [sp, #44] @ restore lr
- add sp, #48 @ pop frame
- .cfi_adjust_cfa_offset -48
- bx lr @ return
-END art_portable_to_interpreter_bridge
-
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 1782db5..fec1ce5 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -29,7 +29,6 @@
*/
.macro SETUP_SAVE_ALL_CALLEE_SAVE_FRAME rTemp1, rTemp2
push {r4-r11, lr} @ 9 words (36 bytes) of callee saves.
- .save {r4-r11, lr}
.cfi_adjust_cfa_offset 36
.cfi_rel_offset r4, 0
.cfi_rel_offset r5, 4
@@ -41,10 +40,8 @@
.cfi_rel_offset r11, 28
.cfi_rel_offset lr, 32
vpush {s16-s31} @ 16 words (64 bytes) of floats.
- .pad #64
.cfi_adjust_cfa_offset 64
sub sp, #12 @ 3 words of space, bottom word will hold Method*
- .pad #12
.cfi_adjust_cfa_offset 12
RUNTIME_CURRENT1 \rTemp1, \rTemp2 @ Load Runtime::Current into rTemp1.
THIS_LOAD_REQUIRES_READ_BARRIER
@@ -64,7 +61,6 @@
*/
.macro SETUP_REFS_ONLY_CALLEE_SAVE_FRAME rTemp1, rTemp2
push {r5-r8, r10-r11, lr} @ 7 words of callee saves
- .save {r5-r8, r10-r11, lr}
.cfi_adjust_cfa_offset 28
.cfi_rel_offset r5, 0
.cfi_rel_offset r6, 4
@@ -74,7 +70,6 @@
.cfi_rel_offset r11, 20
.cfi_rel_offset lr, 24
sub sp, #4 @ bottom word will hold Method*
- .pad #4
.cfi_adjust_cfa_offset 4
RUNTIME_CURRENT2 \rTemp1, \rTemp2 @ Load Runtime::Current into rTemp1.
THIS_LOAD_REQUIRES_READ_BARRIER
@@ -90,6 +85,7 @@
.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
add sp, #4 @ bottom word holds Method*
+ .cfi_adjust_cfa_offset -4
pop {r5-r8, r10-r11, lr} @ 7 words of callee saves
.cfi_restore r5
.cfi_restore r6
@@ -97,7 +93,8 @@
.cfi_restore r8
.cfi_restore r10
.cfi_restore r11
- .cfi_adjust_cfa_offset -FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+ .cfi_restore lr
+ .cfi_adjust_cfa_offset -28
.endm
.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
@@ -111,7 +108,6 @@
*/
.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_REGISTERS_ONLY
push {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves and args.
- .save {r1-r3, r5-r8, r10-r11, lr}
.cfi_adjust_cfa_offset 40
.cfi_rel_offset r1, 0
.cfi_rel_offset r2, 4
@@ -124,10 +120,8 @@
.cfi_rel_offset r11, 32
.cfi_rel_offset lr, 36
vpush {s0-s15} @ 16 words of float args.
- .pad #64
.cfi_adjust_cfa_offset 64
sub sp, #8 @ 2 words of space, bottom word will hold Method*
- .pad #8
.cfi_adjust_cfa_offset 8
// Ugly compile-time check, but we only have the preprocessor.
#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 40 + 64 + 8)
@@ -166,6 +160,7 @@
.cfi_restore r8
.cfi_restore r10
.cfi_restore r11
+ .cfi_restore lr
.cfi_adjust_cfa_offset -40
.endm
@@ -238,6 +233,11 @@
DELIVER_PENDING_EXCEPTION
.endm
+.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_NON_ZERO
+ DELIVER_PENDING_EXCEPTION
+.endm
+
// Macros taking opportunity of code similarities for downcalls with referrer for non-wide fields.
.macro ONE_ARG_REF_DOWNCALL name, entrypoint, return
.extern \entrypoint
@@ -263,13 +263,12 @@
END \name
.endm
-.macro THREE_ARG_REF_DOWNCALL name, entrypoint, return
+.macro THREE_ARG_REF_DOWNCALL name, entrypoint, return
.extern \entrypoint
ENTRY \name
SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12 @ save callee saves in case of GC
ldr r3, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer
str r9, [sp, #-16]! @ expand the frame and pass Thread::Current
- .pad #16
.cfi_adjust_cfa_offset 16
bl \entrypoint @ (field_idx, Object*, new_val, referrer, Thread*)
add sp, #16 @ release out args
@@ -335,7 +334,6 @@
mov r3, r9 @ pass Thread::Current
mov r12, sp
str r12, [sp, #-16]! @ expand the frame and pass SP
- .pad #16
.cfi_adjust_cfa_offset 16
bl \cxx_name @ (method_idx, this, caller, Thread*, SP)
add sp, #16 @ strip the extra frame
@@ -386,14 +384,17 @@
* +-------------------------+
*/
ENTRY art_quick_invoke_stub_internal
- push {r4, r9, r11, lr} @ spill regs
- .save {r4, r9, r11, lr}
- .pad #16
+ push {r4, r5, r6, r7, r8, r9, r10, r11, lr} @ spill regs
.cfi_adjust_cfa_offset 16
.cfi_rel_offset r4, 0
- .cfi_rel_offset r9, 4
- .cfi_rel_offset r11, 8
- .cfi_rel_offset lr, 12
+ .cfi_rel_offset r5, 4
+ .cfi_rel_offset r6, 8
+ .cfi_rel_offset r7, 12
+ .cfi_rel_offset r8, 16
+ .cfi_rel_offset r9, 20
+ .cfi_rel_offset r10, 24
+ .cfi_rel_offset r11, 28
+ .cfi_rel_offset lr, 32
mov r11, sp @ save the stack pointer
.cfi_def_cfa_register r11
@@ -410,10 +411,10 @@
mov ip, #0 @ set ip to 0
str ip, [sp] @ store NULL for method* at bottom of frame
- ldr ip, [r11, #28] @ load fp register argument array pointer
+ ldr ip, [r11, #48] @ load fp register argument array pointer
vldm ip, {s0-s15} @ copy s0 - s15
- ldr ip, [r11, #24] @ load core register argument array pointer
+ ldr ip, [r11, #44] @ load core register argument array pointer
mov r0, r4 @ restore method*
add ip, ip, #4 @ skip r0
ldm ip, {r1-r3} @ copy r1 - r3
@@ -428,20 +429,14 @@
mov sp, r11 @ restore the stack pointer
.cfi_def_cfa_register sp
- ldr r4, [sp, #20] @ load result_is_float
- ldr r9, [sp, #16] @ load the result pointer
+ ldr r4, [sp, #40] @ load result_is_float
+ ldr r9, [sp, #36] @ load the result pointer
cmp r4, #0
ite eq
strdeq r0, [r9] @ store r0/r1 into result pointer
vstrne d0, [r9] @ store s0-s1/d0 into result pointer
- pop {r4, r9, r11, lr} @ restore spill regs
- .cfi_restore r4
- .cfi_restore r9
- .cfi_restore r11
- .cfi_restore lr
- .cfi_adjust_cfa_offset -16
- bx lr
+ pop {r4, r5, r6, r7, r8, r9, r10, r11, pc} @ restore spill regs
END art_quick_invoke_stub_internal
/*
@@ -544,25 +539,26 @@
.extern artThrowClassCastException
ENTRY art_quick_check_cast
push {r0-r1, lr} @ save arguments, link register and pad
- .save {r0-r1, lr}
.cfi_adjust_cfa_offset 12
.cfi_rel_offset r0, 0
.cfi_rel_offset r1, 4
.cfi_rel_offset lr, 8
sub sp, #4
- .pad #4
.cfi_adjust_cfa_offset 4
bl artIsAssignableFromCode
cbz r0, .Lthrow_class_cast_exception
add sp, #4
.cfi_adjust_cfa_offset -4
pop {r0-r1, pc}
+ .cfi_adjust_cfa_offset 4 @ Reset unwind info so following code unwinds.
.Lthrow_class_cast_exception:
add sp, #4
.cfi_adjust_cfa_offset -4
pop {r0-r1, lr}
+ .cfi_adjust_cfa_offset -12
.cfi_restore r0
.cfi_restore r1
+ .cfi_restore lr
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r2, r3 // save all registers as basis for long jump context
mov r2, r9 @ pass Thread::Current
b artThrowClassCastException @ (Class*, Class*, Thread*)
@@ -611,7 +607,6 @@
blx lr
.Lcheck_assignability:
push {r0-r2, lr} @ save arguments
- .save {r0-r2, lr}
.cfi_adjust_cfa_offset 16
.cfi_rel_offset r0, 0
.cfi_rel_offset r1, 4
@@ -635,11 +630,7 @@
blx lr
.Lthrow_array_store_exception:
pop {r0-r2, lr}
- .cfi_restore r0
- .cfi_restore r1
- .cfi_restore r2
- .cfi_restore lr
- .cfi_adjust_cfa_offset -16
+ /* No need to repeat restore cfi directives, the ones above apply here. */
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r3, ip
mov r1, r2
mov r2, r9 @ pass Thread::Current
@@ -647,50 +638,35 @@
bkpt @ unreached
END art_quick_aput_obj
- /*
- * Entry from managed code when uninitialized static storage, this stub will run the class
- * initializer and deliver the exception on error. On success the static storage base is
- * returned.
- */
- .extern artInitializeStaticStorageFromCode
-ENTRY art_quick_initialize_static_storage
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC
- mov r2, r9 @ pass Thread::Current
- @ artInitializeStaticStorageFromCode(uint32_t type_idx, Method* referrer, Thread*)
- bl artInitializeStaticStorageFromCode
+// Macro to facilitate adding new allocation entrypoints.
+.macro TWO_ARG_DOWNCALL name, entrypoint, return
+ .extern \entrypoint
+ENTRY \name
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC
+ mov r2, r9 @ pass Thread::Current
+ bl \entrypoint @ (uint32_t type_idx, Method* method, Thread*)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
- RETURN_IF_RESULT_IS_NON_ZERO
- DELIVER_PENDING_EXCEPTION
-END art_quick_initialize_static_storage
+ \return
+END \name
+.endm
- /*
- * Entry from managed code when dex cache misses for a type_idx
- */
- .extern artInitializeTypeFromCode
-ENTRY art_quick_initialize_type
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC
- mov r2, r9 @ pass Thread::Current
- @ artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*)
- bl artInitializeTypeFromCode
+// Macro to facilitate adding new array allocation entrypoints.
+.macro THREE_ARG_DOWNCALL name, entrypoint, return
+ .extern \entrypoint
+ENTRY \name
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12 @ save callee saves in case of GC
+ mov r3, r9 @ pass Thread::Current
+ @ (uint32_t type_idx, Method* method, int32_t component_count, Thread*)
+ bl \entrypoint
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
- RETURN_IF_RESULT_IS_NON_ZERO
- DELIVER_PENDING_EXCEPTION
-END art_quick_initialize_type
+ \return
+END \name
+.endm
- /*
- * Entry from managed code when type_idx needs to be checked for access and dex cache may also
- * miss.
- */
- .extern artInitializeTypeAndVerifyAccessFromCode
-ENTRY art_quick_initialize_type_and_verify_access
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC
- mov r2, r9 @ pass Thread::Current
- @ artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx, Method* referrer, Thread*)
- bl artInitializeTypeAndVerifyAccessFromCode
- RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
- RETURN_IF_RESULT_IS_NON_ZERO
- DELIVER_PENDING_EXCEPTION
-END art_quick_initialize_type_and_verify_access
+TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+
+TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
/*
* Called by managed code to resolve a static field and load a non-wide value.
@@ -762,7 +738,6 @@
mov r2, r1 @ pass other half of wide argument
ldr r1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer
str r9, [sp, #-16]! @ expand the frame and pass Thread::Current
- .pad #16
.cfi_adjust_cfa_offset 16
bl artSet64StaticFromCode @ (field_idx, referrer, new_val, Thread*)
add sp, #16 @ release out args
@@ -787,10 +762,8 @@
SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r12, lr @ save callee saves in case of GC
ldr r12, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer
str r9, [sp, #-12]! @ expand the frame and pass Thread::Current
- .pad #12
.cfi_adjust_cfa_offset 12
str r12, [sp, #-4]! @ expand the frame and pass the referrer
- .pad #4
.cfi_adjust_cfa_offset 4
bl artSet64InstanceFromCode @ (field_idx, Object*, new_val, Method* referrer, Thread*)
add sp, #16 @ release out args
@@ -806,43 +779,7 @@
* R1 holds the string index. The fast path check for hit in strings cache has already been
* performed.
*/
- .extern artResolveStringFromCode
-ENTRY art_quick_resolve_string
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC
- mov r2, r9 @ pass Thread::Current
- @ artResolveStringFromCode(Method* referrer, uint32_t string_idx, Thread*)
- bl artResolveStringFromCode
- RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
- RETURN_IF_RESULT_IS_NON_ZERO
- DELIVER_PENDING_EXCEPTION
-END art_quick_resolve_string
-
-// Macro to facilitate adding new allocation entrypoints.
-.macro TWO_ARG_DOWNCALL name, entrypoint, return
- .extern \entrypoint
-ENTRY \name
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC
- mov r2, r9 @ pass Thread::Current
- bl \entrypoint @ (uint32_t type_idx, Method* method, Thread*)
- RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
- \return
- DELIVER_PENDING_EXCEPTION
-END \name
-.endm
-
-// Macro to facilitate adding new array allocation entrypoints.
-.macro THREE_ARG_DOWNCALL name, entrypoint, return
- .extern \entrypoint
-ENTRY \name
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12 @ save callee saves in case of GC
- mov r3, r9 @ pass Thread::Current
- @ (uint32_t type_idx, Method* method, int32_t component_count, Thread*)
- bl \entrypoint
- RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
- \return
- DELIVER_PENDING_EXCEPTION
-END \name
-.endm
+TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Generate the allocation entrypoints for each allocator.
GENERATE_ALL_ALLOC_ENTRYPOINTS
@@ -1002,6 +939,9 @@
// store into fpr, for when it's a fpr return...
vmov d0, r0, r1
bx lr // ret
+ // Undo the unwinding information from above since it doesn't apply below.
+ .cfi_def_cfa_register r10
+ .cfi_adjust_cfa_offset FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE-FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
.Lentry_error:
mov sp, r10
@@ -1056,12 +996,10 @@
SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ set up frame knowing r2 and r3 must be dead on exit
mov r12, sp @ remember bottom of caller's frame
push {r0-r1} @ save return value
- .save {r0-r1}
.cfi_adjust_cfa_offset 8
.cfi_rel_offset r0, 0
.cfi_rel_offset r1, 4
sub sp, #8 @ space for return value argument
- .pad #8
.cfi_adjust_cfa_offset 8
strd r0, [sp] @ r0/r1 -> [sp] for fpr_res
mov r2, r0 @ pass return value as gpr_res
@@ -1075,6 +1013,7 @@
mov r2, r0 @ link register saved by instrumentation
mov lr, r1 @ r1 is holding link register if we're to bounce to deoptimize
pop {r0, r1} @ restore return value
+ .cfi_adjust_cfa_offset -8
.cfi_restore r0
.cfi_restore r1
add sp, #32 @ remove callee save frame
@@ -1114,7 +1053,6 @@
/* mul-long vAA, vBB, vCC */
ENTRY art_quick_mul_long
push {r9 - r10}
- .save {r9 - r10}
.cfi_adjust_cfa_offset 8
.cfi_rel_offset r9, 0
.cfi_rel_offset r10, 4
@@ -1207,7 +1145,6 @@
*/
ENTRY art_quick_indexof
push {r4, r10-r11, lr} @ 4 words of callee saves
- .save {r4, r10-r11, lr}
.cfi_adjust_cfa_offset 16
.cfi_rel_offset r4, 0
.cfi_rel_offset r10, 4
@@ -1324,7 +1261,6 @@
1: @ Same strings, return.
push {r4, r7-r12, lr} @ 8 words - keep alignment
- .save {r4, r7-r12, lr}
.cfi_adjust_cfa_offset 32
.cfi_rel_offset r4, 0
.cfi_rel_offset r7, 4
@@ -1465,7 +1401,6 @@
add sp, #4
.cfi_adjust_cfa_offset -4
pop {pc}
- .cfi_adjust_cfa_offset -4
END art_quick_fmod
/* float fmodf(float a, float b) */
@@ -1482,7 +1417,6 @@
add sp, #4
.cfi_adjust_cfa_offset -4
pop {pc}
- .cfi_adjust_cfa_offset -4
END art_quick_fmod
/* int64_t art_d2l(double d) */
diff --git a/runtime/arch/arm/quick_entrypoints_cc_arm.cc b/runtime/arch/arm/quick_entrypoints_cc_arm.cc
index e21e6c1..a3acd7e 100644
--- a/runtime/arch/arm/quick_entrypoints_cc_arm.cc
+++ b/runtime/arch/arm/quick_entrypoints_cc_arm.cc
@@ -75,7 +75,14 @@
}
break;
case 'J':
+ if (gpr_index == 1 && !kArm32QuickCodeUseSoftFloat) {
+ // Don't use r1-r2 as a register pair, move to r2-r3 instead.
+ gpr_index++;
+ }
if (gpr_index < arraysize(core_reg_args)) {
+ // Note that we don't need to do this if two registers are not available
+ // when !kArm32QuickCodeUseSoftFloat. We do it anyway to leave this
+ // code simple.
core_reg_args[gpr_index++] = args[arg_index];
}
++arg_index;
diff --git a/runtime/arch/arm64/context_arm64.cc b/runtime/arch/arm64/context_arm64.cc
index 0a31480..7fc0555 100644
--- a/runtime/arch/arm64/context_arm64.cc
+++ b/runtime/arch/arm64/context_arm64.cc
@@ -70,27 +70,19 @@
}
}
-bool Arm64Context::SetGPR(uint32_t reg, uintptr_t value) {
+void Arm64Context::SetGPR(uint32_t reg, uintptr_t value) {
DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfXRegisters));
DCHECK_NE(reg, static_cast<uint32_t>(XZR));
+ DCHECK(IsAccessibleGPR(reg));
DCHECK_NE(gprs_[reg], &gZero); // Can't overwrite this static value since they are never reset.
- if (gprs_[reg] != nullptr) {
- *gprs_[reg] = value;
- return true;
- } else {
- return false;
- }
+ *gprs_[reg] = value;
}
-bool Arm64Context::SetFPR(uint32_t reg, uintptr_t value) {
+void Arm64Context::SetFPR(uint32_t reg, uintptr_t value) {
DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfDRegisters));
+ DCHECK(IsAccessibleFPR(reg));
DCHECK_NE(fprs_[reg], &gZero); // Can't overwrite this static value since they are never reset.
- if (fprs_[reg] != nullptr) {
- *fprs_[reg] = value;
- return true;
- } else {
- return false;
- }
+ *fprs_[reg] = value;
}
void Arm64Context::SmashCallerSaves() {
diff --git a/runtime/arch/arm64/context_arm64.h b/runtime/arch/arm64/context_arm64.h
index d9a433b..6a4485b 100644
--- a/runtime/arch/arm64/context_arm64.h
+++ b/runtime/arch/arm64/context_arm64.h
@@ -37,13 +37,16 @@
void FillCalleeSaves(const StackVisitor& fr) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetSP(uintptr_t new_sp) OVERRIDE {
- bool success = SetGPR(SP, new_sp);
- CHECK(success) << "Failed to set SP register";
+ SetGPR(SP, new_sp);
}
void SetPC(uintptr_t new_lr) OVERRIDE {
- bool success = SetGPR(LR, new_lr);
- CHECK(success) << "Failed to set LR register";
+ SetGPR(LR, new_lr);
+ }
+
+ bool IsAccessibleGPR(uint32_t reg) OVERRIDE {
+ DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfXRegisters));
+ return gprs_[reg] != nullptr;
}
uintptr_t* GetGPRAddress(uint32_t reg) OVERRIDE {
@@ -51,31 +54,26 @@
return gprs_[reg];
}
- bool GetGPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+ uintptr_t GetGPR(uint32_t reg) OVERRIDE {
DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfXRegisters));
- if (gprs_[reg] == nullptr) {
- return false;
- } else {
- DCHECK(val != nullptr);
- *val = *gprs_[reg];
- return true;
- }
+ DCHECK(IsAccessibleGPR(reg));
+ return *gprs_[reg];
}
- bool SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
+ void SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
- bool GetFPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+ bool IsAccessibleFPR(uint32_t reg) OVERRIDE {
DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfDRegisters));
- if (fprs_[reg] == nullptr) {
- return false;
- } else {
- DCHECK(val != nullptr);
- *val = *fprs_[reg];
- return true;
- }
+ return fprs_[reg] != nullptr;
}
- bool SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
+ uintptr_t GetFPR(uint32_t reg) OVERRIDE {
+ DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfDRegisters));
+ DCHECK(IsAccessibleFPR(reg));
+ return *fprs_[reg];
+ }
+
+ void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
void SmashCallerSaves() OVERRIDE;
void DoLongJump() OVERRIDE;
diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc
index 2d26c03..e68d41d 100644
--- a/runtime/arch/arm64/entrypoints_init_arm64.cc
+++ b/runtime/arch/arm64/entrypoints_init_arm64.cc
@@ -16,7 +16,6 @@
#include "entrypoints/interpreter/interpreter_entrypoints.h"
#include "entrypoints/jni/jni_entrypoints.h"
-#include "entrypoints/portable/portable_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_default_externs.h"
#include "entrypoints/quick/quick_entrypoints.h"
@@ -39,7 +38,7 @@
void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
- PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
+ QuickEntryPoints* qpoints) {
// Interpreter
ipoints->pInterpreterToInterpreterBridge = artInterpreterToInterpreterBridge;
ipoints->pInterpreterToCompiledCodeBridge = artInterpreterToCompiledCodeBridge;
@@ -47,10 +46,6 @@
// JNI
jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
- // Portable
- ppoints->pPortableResolutionTrampoline = art_portable_resolution_trampoline;
- ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge;
-
// Alloc
ResetQuickAllocEntryPoints(qpoints);
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 4415935..770073b5 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -499,7 +499,7 @@
.macro INVOKE_STUB_CREATE_FRAME
-SAVE_SIZE=6*8 // x4, x5, xSUSPEND, SP, LR & FP saved.
+SAVE_SIZE=15*8 // x4, x5, x20, x21, x22, x23, x24, x25, x26, x27, x28, xSUSPEND, SP, LR, FP saved.
SAVE_SIZE_AND_METHOD=SAVE_SIZE+STACK_REFERENCE_SIZE
@@ -515,6 +515,25 @@
.cfi_def_cfa_register x10 // before this.
.cfi_adjust_cfa_offset SAVE_SIZE
+ str x28, [x10, #112]
+ .cfi_rel_offset x28, 112
+
+ stp x26, x27, [x10, #96]
+ .cfi_rel_offset x26, 96
+ .cfi_rel_offset x27, 104
+
+ stp x24, x25, [x10, #80]
+ .cfi_rel_offset x24, 80
+ .cfi_rel_offset x25, 88
+
+ stp x22, x23, [x10, #64]
+ .cfi_rel_offset x22, 64
+ .cfi_rel_offset x23, 72
+
+ stp x20, x21, [x10, #48]
+ .cfi_rel_offset x20, 48
+ .cfi_rel_offset x21, 56
+
stp x9, xSUSPEND, [x10, #32] // Save old stack pointer and xSUSPEND
.cfi_rel_offset sp, 32
.cfi_rel_offset x19, 40
@@ -573,6 +592,25 @@
.cfi_restore x4
.cfi_restore x5
+ ldr x28, [xFP, #112]
+ .cfi_restore x28
+
+ ldp x26, x27, [xFP, #96]
+ .cfi_restore x26
+ .cfi_restore x27
+
+ ldp x24, x25, [xFP, #80]
+ .cfi_restore x24
+ .cfi_restore x25
+
+ ldp x22, x23, [xFP, #64]
+ .cfi_restore x22
+ .cfi_restore x23
+
+ ldp x20, x21, [xFP, #48]
+ .cfi_restore x20
+ .cfi_restore x21
+
// Store result (w0/x0/s0/d0) appropriately, depending on resultType.
ldrb w10, [x5]
@@ -1191,7 +1229,6 @@
bl \entrypoint // (uint32_t type_idx, Method* method, Thread*)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
\return
- DELIVER_PENDING_EXCEPTION
END \name
.endm
@@ -1204,7 +1241,6 @@
bl \entrypoint
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
\return
- DELIVER_PENDING_EXCEPTION
END \name
.endm
@@ -1245,6 +1281,13 @@
END \name
.endm
+.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ cbz w0, 1f // result zero branch over
+ ret // return
+1:
+ DELIVER_PENDING_EXCEPTION
+.endm
+
/*
* Entry from managed code that calls artHandleFillArrayDataFromCode and delivers exception on
* failure.
@@ -1256,10 +1299,10 @@
* initializer and deliver the exception on error. On success the static storage base is
* returned.
*/
-TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO
+TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO
-TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO
+TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
@@ -1307,7 +1350,7 @@
* w1 holds the string index. The fast path check for hit in strings cache has already been
* performed.
*/
-TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO
+TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Generate the allocation entrypoints for each allocator.
GENERATE_ALL_ALLOC_ENTRYPOINTS
diff --git a/runtime/arch/context.cc b/runtime/arch/context.cc
index b1700bb..bf40a3f 100644
--- a/runtime/arch/context.cc
+++ b/runtime/arch/context.cc
@@ -20,8 +20,10 @@
#include "arm/context_arm.h"
#elif defined(__aarch64__)
#include "arm64/context_arm64.h"
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__)
#include "mips/context_mips.h"
+#elif defined(__mips__) && defined(__LP64__)
+#include "mips64/context_mips64.h"
#elif defined(__i386__)
#include "x86/context_x86.h"
#elif defined(__x86_64__)
@@ -37,8 +39,10 @@
return new arm::ArmContext();
#elif defined(__aarch64__)
return new arm64::Arm64Context();
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__)
return new mips::MipsContext();
+#elif defined(__mips__) && defined(__LP64__)
+ return new mips64::Mips64Context();
#elif defined(__i386__)
return new x86::X86Context();
#elif defined(__x86_64__)
diff --git a/runtime/arch/context.h b/runtime/arch/context.h
index 20a84dd..ed8cab0 100644
--- a/runtime/arch/context.h
+++ b/runtime/arch/context.h
@@ -49,24 +49,30 @@
// Sets the program counter value.
virtual void SetPC(uintptr_t new_pc) = 0;
+ // Returns whether the given GPR is accessible (read or write).
+ virtual bool IsAccessibleGPR(uint32_t reg) = 0;
+
// Gets the given GPRs address.
virtual uintptr_t* GetGPRAddress(uint32_t reg) = 0;
- // Reads the given GPR. Returns true if we successfully read the register and
- // set its value into 'val', returns false otherwise.
- virtual bool GetGPR(uint32_t reg, uintptr_t* val) = 0;
+ // Reads the given GPR. The caller is responsible for checking the register
+ // is accessible with IsAccessibleGPR.
+ virtual uintptr_t GetGPR(uint32_t reg) = 0;
- // Sets the given GPR. Returns true if we successfully write the given value
- // into the register, returns false otherwise.
- virtual bool SetGPR(uint32_t reg, uintptr_t value) = 0;
+ // Sets the given GPR. The caller is responsible for checking the register
+ // is accessible with IsAccessibleGPR.
+ virtual void SetGPR(uint32_t reg, uintptr_t value) = 0;
- // Reads the given FPR. Returns true if we successfully read the register and
- // set its value into 'val', returns false otherwise.
- virtual bool GetFPR(uint32_t reg, uintptr_t* val) = 0;
+ // Returns whether the given FPR is accessible (read or write).
+ virtual bool IsAccessibleFPR(uint32_t reg) = 0;
- // Sets the given FPR. Returns true if we successfully write the given value
- // into the register, returns false otherwise.
- virtual bool SetFPR(uint32_t reg, uintptr_t value) = 0;
+ // Reads the given FPR. The caller is responsible for checking the register
+ // is accessible with IsAccessibleFPR.
+ virtual uintptr_t GetFPR(uint32_t reg) = 0;
+
+ // Sets the given FPR. The caller is responsible for checking the register
+ // is accessible with IsAccessibleFPR.
+ virtual void SetFPR(uint32_t reg, uintptr_t value) = 0;
// Smashes the caller save registers. If we're throwing, we don't want to return bogus values.
virtual void SmashCallerSaves() = 0;
diff --git a/runtime/arch/instruction_set.cc b/runtime/arch/instruction_set.cc
index 92fa727..81ca010 100644
--- a/runtime/arch/instruction_set.cc
+++ b/runtime/arch/instruction_set.cc
@@ -16,6 +16,8 @@
#include "instruction_set.h"
+// Explicitly include our own elf.h to avoid Linux and other dependencies.
+#include "../elf.h"
#include "globals.h"
namespace art {
@@ -57,12 +59,35 @@
} else if (strcmp("mips", isa_str) == 0) {
return kMips;
} else if (strcmp("mips64", isa_str) == 0) {
- return kMips;
+ return kMips64;
}
return kNone;
}
+InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags) {
+ switch (e_machine) {
+ case EM_ARM:
+ return kArm;
+ case EM_AARCH64:
+ return kArm64;
+ case EM_386:
+ return kX86;
+ case EM_X86_64:
+ return kX86_64;
+ case EM_MIPS: {
+ if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R2 ||
+ (e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) {
+ return kMips;
+ } else if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_64R6) {
+ return kMips64;
+ }
+ break;
+ }
+ }
+ return kNone;
+}
+
size_t GetInstructionSetAlignment(InstructionSet isa) {
switch (isa) {
case kArm:
@@ -76,6 +101,8 @@
case kX86_64:
return kX86Alignment;
case kMips:
+ // Fall-through.
+ case kMips64:
return kMipsAlignment;
case kNone:
LOG(FATAL) << "ISA kNone does not have alignment.";
@@ -88,6 +115,7 @@
static constexpr size_t kDefaultStackOverflowReservedBytes = 16 * KB;
static constexpr size_t kMipsStackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
+static constexpr size_t kMips64StackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
static constexpr size_t kArmStackOverflowReservedBytes = 8 * KB;
static constexpr size_t kArm64StackOverflowReservedBytes = 8 * KB;
@@ -106,6 +134,9 @@
case kMips:
return kMipsStackOverflowReservedBytes;
+ case kMips64:
+ return kMips64StackOverflowReservedBytes;
+
case kX86:
return kX86StackOverflowReservedBytes;
diff --git a/runtime/arch/instruction_set.h b/runtime/arch/instruction_set.h
index e413880..9cfd2eb 100644
--- a/runtime/arch/instruction_set.h
+++ b/runtime/arch/instruction_set.h
@@ -40,8 +40,10 @@
static constexpr InstructionSet kRuntimeISA = kArm;
#elif defined(__aarch64__)
static constexpr InstructionSet kRuntimeISA = kArm64;
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__)
static constexpr InstructionSet kRuntimeISA = kMips;
+#elif defined(__mips__) && defined(__LP64__)
+static constexpr InstructionSet kRuntimeISA = kMips64;
#elif defined(__i386__)
static constexpr InstructionSet kRuntimeISA = kX86;
#elif defined(__x86_64__)
@@ -78,6 +80,8 @@
// Note: Returns kNone when the string cannot be parsed to a known value.
InstructionSet GetInstructionSetFromString(const char* instruction_set);
+InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags);
+
static inline size_t GetInstructionSetPointerSize(InstructionSet isa) {
switch (isa) {
case kArm:
@@ -145,6 +149,8 @@
return 8;
case kMips:
return 4;
+ case kMips64:
+ return 8;
case kNone:
LOG(FATAL) << "ISA kNone does not have spills.";
UNREACHABLE();
@@ -168,6 +174,8 @@
return 8;
case kMips:
return 4;
+ case kMips64:
+ return 8;
case kNone:
LOG(FATAL) << "ISA kNone does not have spills.";
UNREACHABLE();
@@ -186,8 +194,8 @@
// On x86, ARM32 and MIPS, this is given for a *scalar* 64bit value. The definition thus *must* be
// uint64_t or long long int.
//
-// On x86_64 and ARM64, structs are decomposed for allocation, so we can create a structs of two
-// size_t-sized values.
+// On x86_64, ARM64 and MIPS64, structs are decomposed for allocation, so we can create a structs of
+// two size_t-sized values.
//
// We need two operations:
//
@@ -202,7 +210,7 @@
// when the garbage collector can move objects concurrently. Ensure that required locks
// are held when using!
-#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+#if defined(__i386__) || defined(__arm__) || (defined(__mips__) && !defined(__LP64__))
typedef uint64_t TwoWordReturn;
// Encodes method_ptr==nullptr and code_ptr==nullptr
@@ -218,7 +226,7 @@
return ((hi64 << 32) | lo32);
}
-#elif defined(__x86_64__) || defined(__aarch64__)
+#elif defined(__x86_64__) || defined(__aarch64__) || (defined(__mips__) && defined(__LP64__))
struct TwoWordReturn {
uintptr_t lo;
uintptr_t hi;
diff --git a/runtime/arch/instruction_set_features.cc b/runtime/arch/instruction_set_features.cc
index 1072562..1fd1dea 100644
--- a/runtime/arch/instruction_set_features.cc
+++ b/runtime/arch/instruction_set_features.cc
@@ -23,6 +23,7 @@
#include "arm/instruction_set_features_arm.h"
#include "arm64/instruction_set_features_arm64.h"
#include "mips/instruction_set_features_mips.h"
+#include "mips64/instruction_set_features_mips64.h"
#include "x86/instruction_set_features_x86.h"
#include "x86_64/instruction_set_features_x86_64.h"
@@ -43,6 +44,9 @@
case kMips:
result = MipsInstructionSetFeatures::FromVariant(variant, error_msg);
break;
+ case kMips64:
+ result = Mips64InstructionSetFeatures::FromVariant(variant, error_msg);
+ break;
case kX86:
result = X86InstructionSetFeatures::FromVariant(variant, error_msg);
break;
@@ -71,6 +75,9 @@
case kMips:
result = MipsInstructionSetFeatures::FromBitmap(bitmap);
break;
+ case kMips64:
+ result = Mips64InstructionSetFeatures::FromBitmap(bitmap);
+ break;
case kX86:
result = X86InstructionSetFeatures::FromBitmap(bitmap);
break;
@@ -98,6 +105,9 @@
case kMips:
result = MipsInstructionSetFeatures::FromCppDefines();
break;
+ case kMips64:
+ result = Mips64InstructionSetFeatures::FromCppDefines();
+ break;
case kX86:
result = X86InstructionSetFeatures::FromCppDefines();
break;
@@ -125,6 +135,9 @@
case kMips:
result = MipsInstructionSetFeatures::FromCpuInfo();
break;
+ case kMips64:
+ result = Mips64InstructionSetFeatures::FromCpuInfo();
+ break;
case kX86:
result = X86InstructionSetFeatures::FromCpuInfo();
break;
@@ -151,6 +164,9 @@
case kMips:
result = MipsInstructionSetFeatures::FromHwcap();
break;
+ case kMips64:
+ result = Mips64InstructionSetFeatures::FromHwcap();
+ break;
case kX86:
result = X86InstructionSetFeatures::FromHwcap();
break;
@@ -177,6 +193,9 @@
case kMips:
result = MipsInstructionSetFeatures::FromAssembly();
break;
+ case kMips64:
+ result = Mips64InstructionSetFeatures::FromAssembly();
+ break;
case kX86:
result = X86InstructionSetFeatures::FromAssembly();
break;
@@ -250,6 +269,11 @@
return down_cast<const MipsInstructionSetFeatures*>(this);
}
+const Mips64InstructionSetFeatures* InstructionSetFeatures::AsMips64InstructionSetFeatures() const {
+ DCHECK_EQ(kMips64, GetInstructionSet());
+ return down_cast<const Mips64InstructionSetFeatures*>(this);
+}
+
const X86InstructionSetFeatures* InstructionSetFeatures::AsX86InstructionSetFeatures() const {
DCHECK(kX86 == GetInstructionSet() || kX86_64 == GetInstructionSet());
return down_cast<const X86InstructionSetFeatures*>(this);
diff --git a/runtime/arch/instruction_set_features.h b/runtime/arch/instruction_set_features.h
index 2c6e699..e4513ef 100644
--- a/runtime/arch/instruction_set_features.h
+++ b/runtime/arch/instruction_set_features.h
@@ -28,6 +28,7 @@
class ArmInstructionSetFeatures;
class Arm64InstructionSetFeatures;
class MipsInstructionSetFeatures;
+class Mips64InstructionSetFeatures;
class X86InstructionSetFeatures;
class X86_64InstructionSetFeatures;
@@ -87,6 +88,9 @@
// Down cast this MipsInstructionFeatures.
const MipsInstructionSetFeatures* AsMipsInstructionSetFeatures() const;
+ // Down cast this Mips64InstructionFeatures.
+ const Mips64InstructionSetFeatures* AsMips64InstructionSetFeatures() const;
+
// Down cast this X86InstructionFeatures.
const X86InstructionSetFeatures* AsX86InstructionSetFeatures() const;
diff --git a/runtime/arch/instruction_set_test.cc b/runtime/arch/instruction_set_test.cc
index 932ef32..2f3cf18 100644
--- a/runtime/arch/instruction_set_test.cc
+++ b/runtime/arch/instruction_set_test.cc
@@ -28,6 +28,7 @@
EXPECT_EQ(kX86, GetInstructionSetFromString("x86"));
EXPECT_EQ(kX86_64, GetInstructionSetFromString("x86_64"));
EXPECT_EQ(kMips, GetInstructionSetFromString("mips"));
+ EXPECT_EQ(kMips64, GetInstructionSetFromString("mips64"));
EXPECT_EQ(kNone, GetInstructionSetFromString("none"));
EXPECT_EQ(kNone, GetInstructionSetFromString("random-string"));
}
@@ -39,6 +40,7 @@
EXPECT_STREQ("x86", GetInstructionSetString(kX86));
EXPECT_STREQ("x86_64", GetInstructionSetString(kX86_64));
EXPECT_STREQ("mips", GetInstructionSetString(kMips));
+ EXPECT_STREQ("mips64", GetInstructionSetString(kMips64));
EXPECT_STREQ("none", GetInstructionSetString(kNone));
}
diff --git a/runtime/arch/memcmp16.h b/runtime/arch/memcmp16.h
index 4b9fb8e..c449a14 100644
--- a/runtime/arch/memcmp16.h
+++ b/runtime/arch/memcmp16.h
@@ -30,7 +30,7 @@
//
// In both cases, MemCmp16 is declared.
-#if defined(__aarch64__) || defined(__arm__) || defined(__mips) || defined(__i386__) || defined(__x86_64__)
+#if defined(__aarch64__) || defined(__arm__) || defined(__mips__) || defined(__i386__) || defined(__x86_64__)
extern "C" uint32_t __memcmp16(const uint16_t* s0, const uint16_t* s1, size_t count);
#define MemCmp16 __memcmp16
diff --git a/runtime/arch/mips/asm_support_mips.S b/runtime/arch/mips/asm_support_mips.S
index 0d18f1a..eea6537 100644
--- a/runtime/arch/mips/asm_support_mips.S
+++ b/runtime/arch/mips/asm_support_mips.S
@@ -66,5 +66,54 @@
END \name
.endm
+#if defined(__mips_isa_rev) && __mips_isa_rev > 2
+ /* mips32r5 & mips32r6 have mthc1 op, and have 64-bit fp regs,
+ and in FPXX abi we avoid referring to odd-numbered fp regs */
+
+/* LDu: Load 64-bit floating-point value to float reg feven,
+ from unaligned (mod-4-aligned) mem location disp(base) */
+.macro LDu feven,fodd,disp,base,temp
+ l.s \feven, \disp(\base)
+ lw \temp, \disp+4(\base)
+ mthc1 \temp, \feven
+.endm
+
+/* SDu: Store 64-bit floating-point value from float reg feven,
+ to unaligned (mod-4-aligned) mem location disp(base) */
+.macro SDu feven,fodd,disp,base,temp
+ mfhc1 \temp, \feven
+ s.s \feven, \disp(\base)
+ sw \temp, \disp+4(\base)
+.endm
+
+/* MTD: Move double, from general regpair (reven,rodd)
+ to float regpair (feven,fodd) */
+.macro MTD reven,rodd,feven,fodd
+ mtc1 \reven, \feven
+ mthc1 \rodd, \feven
+.endm
+
+#else
+ /* mips32r1 has no mthc1 op;
+ mips32r1 and mips32r2 use 32-bit floating point register mode (FR=0),
+ and always hold doubles as (feven, fodd) fp reg pair */
+
+.macro LDu feven,fodd,disp,base,temp
+ l.s \feven, \disp(\base)
+ l.s \fodd, \disp+4(\base)
+.endm
+
+.macro SDu feven,fodd,disp,base,temp
+ s.s \feven, \disp(\base)
+ s.s \fodd, \disp+4(\base)
+.endm
+
+.macro MTD reven,rodd,feven,fodd
+ mtc1 \reven, \feven
+ mtc1 \rodd, \fodd
+.endm
+
+#endif /* mips_isa_rev */
+
#endif // ART_RUNTIME_ARCH_MIPS_ASM_SUPPORT_MIPS_S_
diff --git a/runtime/arch/mips/context_mips.cc b/runtime/arch/mips/context_mips.cc
index e1f6c06..6c0ab98 100644
--- a/runtime/arch/mips/context_mips.cc
+++ b/runtime/arch/mips/context_mips.cc
@@ -67,26 +67,18 @@
}
}
-bool MipsContext::SetGPR(uint32_t reg, uintptr_t value) {
+void MipsContext::SetGPR(uint32_t reg, uintptr_t value) {
CHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters));
+ DCHECK(IsAccessibleGPR(reg));
CHECK_NE(gprs_[reg], &gZero); // Can't overwrite this static value since they are never reset.
- if (gprs_[reg] != nullptr) {
- *gprs_[reg] = value;
- return true;
- } else {
- return false;
- }
+ *gprs_[reg] = value;
}
-bool MipsContext::SetFPR(uint32_t reg, uintptr_t value) {
+void MipsContext::SetFPR(uint32_t reg, uintptr_t value) {
CHECK_LT(reg, static_cast<uint32_t>(kNumberOfFRegisters));
+ DCHECK(IsAccessibleFPR(reg));
CHECK_NE(fprs_[reg], &gZero); // Can't overwrite this static value since they are never reset.
- if (fprs_[reg] != nullptr) {
- *fprs_[reg] = value;
- return true;
- } else {
- return false;
- }
+ *fprs_[reg] = value;
}
void MipsContext::SmashCallerSaves() {
diff --git a/runtime/arch/mips/context_mips.h b/runtime/arch/mips/context_mips.h
index f2ee335..d8a0b67 100644
--- a/runtime/arch/mips/context_mips.h
+++ b/runtime/arch/mips/context_mips.h
@@ -36,13 +36,16 @@
void FillCalleeSaves(const StackVisitor& fr) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetSP(uintptr_t new_sp) OVERRIDE {
- bool success = SetGPR(SP, new_sp);
- CHECK(success) << "Failed to set SP register";
+ SetGPR(SP, new_sp);
}
void SetPC(uintptr_t new_pc) OVERRIDE {
- bool success = SetGPR(RA, new_pc);
- CHECK(success) << "Failed to set RA register";
+ SetGPR(RA, new_pc);
+ }
+
+ bool IsAccessibleGPR(uint32_t reg) OVERRIDE {
+ CHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters));
+ return gprs_[reg] != nullptr;
}
uintptr_t* GetGPRAddress(uint32_t reg) OVERRIDE {
@@ -50,31 +53,26 @@
return gprs_[reg];
}
- bool GetGPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+ uintptr_t GetGPR(uint32_t reg) OVERRIDE {
CHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters));
- if (gprs_[reg] == nullptr) {
- return false;
- } else {
- DCHECK(val != nullptr);
- *val = *gprs_[reg];
- return true;
- }
+ DCHECK(IsAccessibleGPR(reg));
+ return *gprs_[reg];
}
- bool SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
+ void SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
- bool GetFPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+ bool IsAccessibleFPR(uint32_t reg) OVERRIDE {
CHECK_LT(reg, static_cast<uint32_t>(kNumberOfFRegisters));
- if (fprs_[reg] == nullptr) {
- return false;
- } else {
- DCHECK(val != nullptr);
- *val = *fprs_[reg];
- return true;
- }
+ return fprs_[reg] != nullptr;
}
- bool SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
+ uintptr_t GetFPR(uint32_t reg) OVERRIDE {
+ CHECK_LT(reg, static_cast<uint32_t>(kNumberOfFRegisters));
+ DCHECK(IsAccessibleFPR(reg));
+ return *fprs_[reg];
+ }
+
+ void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
void SmashCallerSaves() OVERRIDE;
void DoLongJump() OVERRIDE;
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index e86aa1c..1a661c4 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -17,7 +17,6 @@
#include "atomic.h"
#include "entrypoints/interpreter/interpreter_entrypoints.h"
#include "entrypoints/jni/jni_entrypoints.h"
-#include "entrypoints/portable/portable_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_default_externs.h"
#include "entrypoints/quick/quick_entrypoints.h"
@@ -60,7 +59,7 @@
extern "C" int64_t __moddi3(int64_t, int64_t);
void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
- PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
+ QuickEntryPoints* qpoints) {
// Interpreter
ipoints->pInterpreterToInterpreterBridge = artInterpreterToInterpreterBridge;
ipoints->pInterpreterToCompiledCodeBridge = artInterpreterToCompiledCodeBridge;
@@ -68,10 +67,6 @@
// JNI
jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
- // Portable
- ppoints->pPortableResolutionTrampoline = art_portable_resolution_trampoline;
- ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge;
-
// Alloc
ResetQuickAllocEntryPoints(qpoints);
diff --git a/runtime/arch/mips/instruction_set_features_mips.cc b/runtime/arch/mips/instruction_set_features_mips.cc
index 11be2a8..00ab613 100644
--- a/runtime/arch/mips/instruction_set_features_mips.cc
+++ b/runtime/arch/mips/instruction_set_features_mips.cc
@@ -25,52 +25,82 @@
namespace art {
const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromVariant(
- const std::string& variant ATTRIBUTE_UNUSED, std::string* error_msg ATTRIBUTE_UNUSED) {
- if (variant != "default") {
- std::ostringstream os;
- LOG(WARNING) << "Unexpected CPU variant for Mips using defaults: " << variant;
- }
+ const std::string& variant, std::string* error_msg ATTRIBUTE_UNUSED) {
+
bool smp = true; // Conservative default.
bool fpu_32bit = true;
- bool mips_isa_gte2 = true;
- return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2);
+ bool mips_isa_gte2 = false;
+ bool r6 = false;
+
+ // Override defaults based on variant string.
+ // Only care if it is R1, R2 or R6 and we assume all CPUs will have a FP unit.
+ constexpr const char* kMips32Prefix = "mips32r";
+ const size_t kPrefixLength = strlen(kMips32Prefix);
+ if (variant.compare(0, kPrefixLength, kMips32Prefix, kPrefixLength) == 0 &&
+ variant.size() > kPrefixLength) {
+ if (variant[kPrefixLength] >= '6') {
+ fpu_32bit = false;
+ r6 = true;
+ }
+ if (variant[kPrefixLength] >= '2') {
+ mips_isa_gte2 = true;
+ }
+ } else if (variant == "default") {
+ // Default variant is: smp = true, has fpu, is gte2, is not r6. This is the traditional
+ // setting.
+ mips_isa_gte2 = true;
+ } else {
+ LOG(WARNING) << "Unexpected CPU variant for Mips32 using defaults: " << variant;
+ }
+
+ return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6);
}
const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromBitmap(uint32_t bitmap) {
bool smp = (bitmap & kSmpBitfield) != 0;
bool fpu_32bit = (bitmap & kFpu32Bitfield) != 0;
bool mips_isa_gte2 = (bitmap & kIsaRevGte2Bitfield) != 0;
- return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2);
+ bool r6 = (bitmap & kR6) != 0;
+ return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6);
}
const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromCppDefines() {
+ // Assume conservative defaults.
const bool smp = true;
+ bool fpu_32bit = true;
+ bool mips_isa_gte2 = false;
+ bool r6 = false;
- // TODO: here we assume the FPU is always 32-bit.
- const bool fpu_32bit = true;
-
-#if __mips_isa_rev >= 2
- const bool mips_isa_gte2 = true;
-#else
- const bool mips_isa_gte2 = false;
+ // Override defaults based on compiler flags.
+#if (_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS32R5) || defined(_MIPS_ARCH_MIPS32R6)
+ mips_isa_gte2 = true;
#endif
- return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2);
+#if defined(_MIPS_ARCH_MIPS32R6)
+ r6 = true;
+ fpu_32bit = false;
+#endif
+
+ return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6);
}
const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromCpuInfo() {
// Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that
// the kernel puts the appropriate feature flags in here. Sometimes it doesn't.
+ // Assume conservative defaults.
bool smp = false;
+ bool fpu_32bit = true;
+ bool mips_isa_gte2 = false;
+ bool r6 = false;
- // TODO: here we assume the FPU is always 32-bit.
- const bool fpu_32bit = true;
+ // Override defaults based on compiler flags.
+#if (_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS32R5) || defined(_MIPS_ARCH_MIPS32R6)
+ mips_isa_gte2 = true;
+#endif
- // TODO: here we assume all MIPS processors are >= v2.
-#if __mips_isa_rev >= 2
- const bool mips_isa_gte2 = true;
-#else
- const bool mips_isa_gte2 = false;
+#if defined(_MIPS_ARCH_MIPS32R6)
+ r6 = true;
+ fpu_32bit = false;
#endif
std::ifstream in("/proc/cpuinfo");
@@ -89,7 +119,7 @@
} else {
LOG(ERROR) << "Failed to open /proc/cpuinfo";
}
- return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2);
+ return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6);
}
const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromHwcap() {
@@ -109,13 +139,15 @@
const MipsInstructionSetFeatures* other_as_mips = other->AsMipsInstructionSetFeatures();
return (IsSmp() == other->IsSmp()) &&
(fpu_32bit_ == other_as_mips->fpu_32bit_) &&
- (mips_isa_gte2_ == other_as_mips->mips_isa_gte2_);
+ (mips_isa_gte2_ == other_as_mips->mips_isa_gte2_) &&
+ (r6_ == other_as_mips->r6_);
}
uint32_t MipsInstructionSetFeatures::AsBitmap() const {
return (IsSmp() ? kSmpBitfield : 0) |
(fpu_32bit_ ? kFpu32Bitfield : 0) |
- (mips_isa_gte2_ ? kIsaRevGte2Bitfield : 0);
+ (mips_isa_gte2_ ? kIsaRevGte2Bitfield : 0) |
+ (r6_ ? kR6 : 0);
}
std::string MipsInstructionSetFeatures::GetFeatureString() const {
@@ -135,6 +167,9 @@
} else {
result += ",-mips2";
}
+ if (r6_) {
+ result += ",r6";
+ } // Suppress non-r6.
return result;
}
@@ -142,6 +177,7 @@
const bool smp, const std::vector<std::string>& features, std::string* error_msg) const {
bool fpu_32bit = fpu_32bit_;
bool mips_isa_gte2 = mips_isa_gte2_;
+ bool r6 = r6_;
for (auto i = features.begin(); i != features.end(); i++) {
std::string feature = Trim(*i);
if (feature == "fpu32") {
@@ -152,12 +188,16 @@
mips_isa_gte2 = true;
} else if (feature == "-mips2") {
mips_isa_gte2 = false;
+ } else if (feature == "r6") {
+ r6 = true;
+ } else if (feature == "-r6") {
+ r6 = false;
} else {
*error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str());
return nullptr;
}
}
- return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2);
+ return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6);
}
} // namespace art
diff --git a/runtime/arch/mips/instruction_set_features_mips.h b/runtime/arch/mips/instruction_set_features_mips.h
index f7c64fe..aac436e 100644
--- a/runtime/arch/mips/instruction_set_features_mips.h
+++ b/runtime/arch/mips/instruction_set_features_mips.h
@@ -67,6 +67,10 @@
return fpu_32bit_;
}
+ bool IsR6() const {
+ return r6_;
+ }
+
virtual ~MipsInstructionSetFeatures() {}
protected:
@@ -76,19 +80,21 @@
std::string* error_msg) const OVERRIDE;
private:
- MipsInstructionSetFeatures(bool smp, bool fpu_32bit, bool mips_isa_gte2)
- : InstructionSetFeatures(smp), fpu_32bit_(fpu_32bit), mips_isa_gte2_(mips_isa_gte2) {
- }
+ MipsInstructionSetFeatures(bool smp, bool fpu_32bit, bool mips_isa_gte2, bool r6)
+ : InstructionSetFeatures(smp), fpu_32bit_(fpu_32bit), mips_isa_gte2_(mips_isa_gte2), r6_(r6)
+ {}
// Bitmap positions for encoding features as a bitmap.
enum {
kSmpBitfield = 1,
kFpu32Bitfield = 2,
kIsaRevGte2Bitfield = 4,
+ kR6 = 8,
};
const bool fpu_32bit_;
const bool mips_isa_gte2_;
+ const bool r6_;
DISALLOW_COPY_AND_ASSIGN(MipsInstructionSetFeatures);
};
diff --git a/runtime/arch/mips/jni_entrypoints_mips.S b/runtime/arch/mips/jni_entrypoints_mips.S
index 9a79467..fbc81d5 100644
--- a/runtime/arch/mips/jni_entrypoints_mips.S
+++ b/runtime/arch/mips/jni_entrypoints_mips.S
@@ -47,9 +47,9 @@
addiu $sp, $sp, 32 # restore the stack
.cfi_adjust_cfa_offset -32
move $t9, $v0 # put method code result in $t9
- jr $t9 # leaf call to method's code
+ jalr $zero, $t9 # leaf call to method's code
nop
.Lno_native_code_found:
- jr $ra
+ jalr $zero, $ra
nop
END art_jni_dlsym_lookup_stub
diff --git a/runtime/arch/mips/portable_entrypoints_mips.S b/runtime/arch/mips/portable_entrypoints_mips.S
deleted file mode 100644
index 8d418e8..0000000
--- a/runtime/arch/mips/portable_entrypoints_mips.S
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "asm_support_mips.S"
-
- .set noreorder
- .balign 4
-
- .extern artPortableProxyInvokeHandler
-ENTRY art_portable_proxy_invoke_handler
- # Fake callee save ref and args frame set up, note portable doesn't use callee save frames.
- # TODO: just save the registers that are needed in artPortableProxyInvokeHandler.
- addiu $sp, $sp, -64
- .cfi_adjust_cfa_offset 64
- sw $ra, 60($sp)
- .cfi_rel_offset 31, 60
- sw $s8, 56($sp)
- .cfi_rel_offset 30, 56
- sw $gp, 52($sp)
- .cfi_rel_offset 28, 52
- sw $s7, 48($sp)
- .cfi_rel_offset 23, 48
- sw $s6, 44($sp)
- .cfi_rel_offset 22, 44
- sw $s5, 40($sp)
- .cfi_rel_offset 21, 40
- sw $s4, 36($sp)
- .cfi_rel_offset 20, 36
- sw $s3, 32($sp)
- .cfi_rel_offset 19, 32
- sw $s2, 28($sp)
- .cfi_rel_offset 18, 28
- sw $a3, 12($sp)
- .cfi_rel_offset 7, 12
- sw $a2, 8($sp)
- .cfi_rel_offset 6, 8
- sw $a1, 4($sp)
- .cfi_rel_offset 5, 4
- # Begin argument set up.
- sw $a0, 0($sp) # place proxy method at bottom of frame
- move $a2, rSELF # pass Thread::Current
- jal artPortableProxyInvokeHandler # (Method* proxy method, receiver, Thread*, SP)
- move $a3, $sp # pass $sp
- lw $ra, 60($sp) # restore $ra
- jr $ra
- addiu $sp, $sp, 64 # pop frame
- .cfi_adjust_cfa_offset -64
-END art_portable_proxy_invoke_handler
-
- /*
- * Invocation stub for portable code.
- * On entry:
- * a0 = method pointer
- * a1 = argument array or NULL for no argument methods
- * a2 = size of argument array in bytes
- * a3 = (managed) thread pointer
- * [sp + 16] = JValue* result
- * [sp + 20] = result type char
- */
-ENTRY art_portable_invoke_stub
- sw $a0, 0($sp) # save out a0
- addiu $sp, $sp, -16 # spill s0, s1, fp, ra
- .cfi_adjust_cfa_offset 16
- sw $ra, 12($sp)
- .cfi_rel_offset 31, 12
- sw $fp, 8($sp)
- .cfi_rel_offset 30, 8
- sw $s1, 4($sp)
- .cfi_rel_offset 17, 4
- sw $s0, 0($sp)
- .cfi_rel_offset 16, 0
- move $fp, $sp # save sp in fp
- .cfi_def_cfa_register 30
- move $s1, $a3 # move managed thread pointer into s1
- addiu $s0, $zero, SUSPEND_CHECK_INTERVAL # reset s0 to suspend check interval. TODO: unused?
- addiu $t0, $a2, 16 # create space for method pointer in frame
- srl $t0, $t0, 3 # shift the frame size right 3
- sll $t0, $t0, 3 # shift the frame size left 3 to align to 16 bytes
- subu $sp, $sp, $t0 # reserve stack space for argument array
- addiu $a0, $sp, 4 # pass stack pointer + method ptr as dest for memcpy
- jal memcpy # (dest, src, bytes)
- addiu $sp, $sp, -16 # make space for argument slots for memcpy
- addiu $sp, $sp, 16 # restore stack after memcpy
- lw $a0, 16($fp) # restore method*
- lw $a1, 4($sp) # copy arg value for a1
- lw $a2, 8($sp) # copy arg value for a2
- lw $a3, 12($sp) # copy arg value for a3
- lw $t9, MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32($a0) # get pointer to the code
- jalr $t9 # call the method
- sw $zero, 0($sp) # store NULL for method* at bottom of frame
- move $sp, $fp # restore the stack
- lw $s0, 0($sp)
- .cfi_restore 16
- lw $s1, 4($sp)
- .cfi_restore 17
- lw $fp, 8($sp)
- .cfi_restore 30
- lw $ra, 12($sp)
- .cfi_restore 31
- addiu $sp, $sp, 16
- .cfi_adjust_cfa_offset -16
- lw $t0, 16($sp) # get result pointer
- lw $t1, 20($sp) # get result type char
- li $t2, 68 # put char 'D' into t2
- beq $t1, $t2, 1f # branch if result type char == 'D'
- li $t3, 70 # put char 'F' into t3
- beq $t1, $t3, 1f # branch if result type char == 'F'
- sw $v0, 0($t0) # store the result
- jr $ra
- sw $v1, 4($t0) # store the other half of the result
-1:
- s.s $f0, 0($t0) # store floating point result
- jr $ra
- s.s $f1, 4($t0) # store other half of floating point result
-END art_portable_invoke_stub
-
-UNIMPLEMENTED art_portable_resolution_trampoline
-UNIMPLEMENTED art_portable_to_interpreter_bridge
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 44feee6..df2feb7 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -154,7 +154,7 @@
.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
- jr $ra
+ jalr $zero, $ra
nop
.endm
@@ -190,12 +190,12 @@
.cfi_rel_offset 19, 32
sw $s2, 28($sp)
.cfi_rel_offset 18, 28
- sw $a3, 12($sp)
- .cfi_rel_offset 7, 12
- sw $a2, 8($sp)
- .cfi_rel_offset 6, 8
- sw $a1, 4($sp)
- .cfi_rel_offset 5, 4
+ sw $a3, 24($sp)
+ .cfi_rel_offset 7, 24
+ sw $a2, 20($sp)
+ .cfi_rel_offset 6, 20
+ sw $a1, 16($sp)
+ .cfi_rel_offset 5, 16
# bottom will hold Method*
.endm
@@ -257,11 +257,11 @@
.cfi_restore 19
lw $s2, 28($sp)
.cfi_restore 18
- lw $a3, 12($sp)
+ lw $a3, 24($sp)
.cfi_restore 7
- lw $a2, 8($sp)
+ lw $a2, 20($sp)
.cfi_restore 6
- lw $a1, 4($sp)
+ lw $a1, 16($sp)
.cfi_restore 5
addiu $sp, $sp, 64 # pop frame
.cfi_adjust_cfa_offset -64
@@ -274,7 +274,7 @@
.macro DELIVER_PENDING_EXCEPTION
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME # save callee saves for throw
la $t9, artDeliverPendingExceptionFromCode
- jr $t9 # artDeliverPendingExceptionFromCode(Thread*)
+ jalr $zero, $t9 # artDeliverPendingExceptionFromCode(Thread*)
move $a0, rSELF # pass Thread::Current
.endm
@@ -283,7 +283,7 @@
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
bnez $t0, 1f # success if no exception is pending
nop
- jr $ra
+ jalr $zero, $ra
nop
1:
DELIVER_PENDING_EXCEPTION
@@ -293,17 +293,17 @@
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
bnez $v0, 1f # success?
nop
- jr $ra # return on success
+ jalr $zero, $ra # return on success
nop
1:
DELIVER_PENDING_EXCEPTION
.endm
-.macro RETURN_IF_RESULT_IS_NON_ZERO
+.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
beqz $v0, 1f # success?
nop
- jr $ra # return on success
+ jalr $zero, $ra # return on success
nop
1:
DELIVER_PENDING_EXCEPTION
@@ -314,38 +314,23 @@
* FIXME: just guessing about the shape of the jmpbuf. Where will pc be?
*/
ENTRY art_quick_do_long_jump
- l.s $f0, 0($a1)
- l.s $f1, 4($a1)
- l.s $f2, 8($a1)
- l.s $f3, 12($a1)
- l.s $f4, 16($a1)
- l.s $f5, 20($a1)
- l.s $f6, 24($a1)
- l.s $f7, 28($a1)
- l.s $f8, 32($a1)
- l.s $f9, 36($a1)
- l.s $f10, 40($a1)
- l.s $f11, 44($a1)
- l.s $f12, 48($a1)
- l.s $f13, 52($a1)
- l.s $f14, 56($a1)
- l.s $f15, 60($a1)
- l.s $f16, 64($a1)
- l.s $f17, 68($a1)
- l.s $f18, 72($a1)
- l.s $f19, 76($a1)
- l.s $f20, 80($a1)
- l.s $f21, 84($a1)
- l.s $f22, 88($a1)
- l.s $f23, 92($a1)
- l.s $f24, 96($a1)
- l.s $f25, 100($a1)
- l.s $f26, 104($a1)
- l.s $f27, 108($a1)
- l.s $f28, 112($a1)
- l.s $f29, 116($a1)
- l.s $f30, 120($a1)
- l.s $f31, 124($a1)
+ LDu $f0, $f1, 0*8, $a1, $t1
+ LDu $f2, $f3, 1*8, $a1, $t1
+ LDu $f4, $f5, 2*8, $a1, $t1
+ LDu $f6, $f7, 3*8, $a1, $t1
+ LDu $f8, $f9, 4*8, $a1, $t1
+ LDu $f10, $f11, 5*8, $a1, $t1
+ LDu $f12, $f13, 6*8, $a1, $t1
+ LDu $f14, $f15, 7*8, $a1, $t1
+ LDu $f16, $f17, 8*8, $a1, $t1
+ LDu $f18, $f19, 9*8, $a1, $t1
+ LDu $f20, $f21, 10*8, $a1, $t1
+ LDu $f22, $f23, 11*8, $a1, $t1
+ LDu $f24, $f25, 12*8, $a1, $t1
+ LDu $f26, $f27, 13*8, $a1, $t1
+ LDu $f28, $f29, 14*8, $a1, $t1
+ LDu $f30, $f31, 15*8, $a1, $t1
+
.set push
.set nomacro
.set noat
@@ -380,7 +365,7 @@
lw $ra, 124($a0)
lw $a0, 16($a0)
move $v0, $zero # clear result registers r0 and r1
- jr $ra # do long jump
+ jalr $zero, $ra # do long jump
move $v1, $zero
END art_quick_do_long_jump
@@ -392,7 +377,7 @@
ENTRY art_quick_deliver_exception
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
la $t9, artDeliverExceptionFromCode
- jr $t9 # artDeliverExceptionFromCode(Throwable*, Thread*)
+ jalr $zero, $t9 # artDeliverExceptionFromCode(Throwable*, Thread*)
move $a1, rSELF # pass Thread::Current
END art_quick_deliver_exception
@@ -403,7 +388,7 @@
ENTRY art_quick_throw_null_pointer_exception
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
la $t9, artThrowNullPointerExceptionFromCode
- jr $t9 # artThrowNullPointerExceptionFromCode(Thread*)
+ jalr $zero, $t9 # artThrowNullPointerExceptionFromCode(Thread*)
move $a0, rSELF # pass Thread::Current
END art_quick_throw_null_pointer_exception
@@ -414,7 +399,7 @@
ENTRY art_quick_throw_div_zero
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
la $t9, artThrowDivZeroFromCode
- jr $t9 # artThrowDivZeroFromCode(Thread*)
+ jalr $zero, $t9 # artThrowDivZeroFromCode(Thread*)
move $a0, rSELF # pass Thread::Current
END art_quick_throw_div_zero
@@ -425,7 +410,7 @@
ENTRY art_quick_throw_array_bounds
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
la $t9, artThrowArrayBoundsFromCode
- jr $t9 # artThrowArrayBoundsFromCode(index, limit, Thread*)
+ jalr $zero, $t9 # artThrowArrayBoundsFromCode(index, limit, Thread*)
move $a2, rSELF # pass Thread::Current
END art_quick_throw_array_bounds
@@ -436,7 +421,7 @@
ENTRY art_quick_throw_stack_overflow
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
la $t9, artThrowStackOverflowFromCode
- jr $t9 # artThrowStackOverflowFromCode(Thread*)
+ jalr $zero, $t9 # artThrowStackOverflowFromCode(Thread*)
move $a0, rSELF # pass Thread::Current
END art_quick_throw_stack_overflow
@@ -447,7 +432,7 @@
ENTRY art_quick_throw_no_such_method
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
la $t9, artThrowNoSuchMethodFromCode
- jr $t9 # artThrowNoSuchMethodFromCode(method_idx, Thread*)
+ jalr $zero, $t9 # artThrowNoSuchMethodFromCode(method_idx, Thread*)
move $a1, rSELF # pass Thread::Current
END art_quick_throw_no_such_method
@@ -480,7 +465,7 @@
RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
beqz $v0, 1f
move $t9, $v1 # save $v0->code_
- jr $t9
+ jalr $zero, $t9
nop
1:
DELIVER_PENDING_EXCEPTION
@@ -555,12 +540,12 @@
li $t3, 70 # put char 'F' into t3
beq $t1, $t3, 1f # branch if result type char == 'F'
sw $v0, 0($t0) # store the result
- jr $ra
+ jalr $zero, $ra
sw $v1, 4($t0) # store the other half of the result
1:
- s.s $f0, 0($t0) # store floating point result
- jr $ra
- s.s $f1, 4($t0) # store other half of floating point result
+ SDu $f0, $f1, 0, $t0, $t1 # store floating point result
+ jalr $zero, $ra
+ nop
END art_quick_invoke_stub
/*
@@ -619,7 +604,7 @@
addiu $sp, $sp, 16
beqz $v0, .Lthrow_class_cast_exception
lw $ra, 12($sp)
- jr $ra
+ jalr $zero, $ra
addiu $sp, $sp, 16
.cfi_adjust_cfa_offset -16
.Lthrow_class_cast_exception:
@@ -630,7 +615,7 @@
.cfi_adjust_cfa_offset -16
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
la $t9, artThrowClassCastException
- jr $t9 # artThrowClassCastException (Class*, Class*, Thread*)
+ jalr $zero, $t9 # artThrowClassCastException (Class*, Class*, Thread*)
move $a2, rSELF # pass Thread::Current
END art_quick_check_cast
@@ -672,13 +657,13 @@
srl $t1, $a0, 7
add $t1, $t1, $t0
sb $t0, ($t1)
- jr $ra
+ jalr $zero, $ra
nop
.Ldo_aput_null:
sll $a1, $a1, 2
add $t0, $a0, $a1
sw $a2, MIRROR_OBJECT_ARRAY_DATA_OFFSET($t0)
- jr $ra
+ jalr $zero, $ra
nop
.Lcheck_assignability:
addiu $sp, $sp, -32
@@ -699,56 +684,18 @@
lw $a2, 8($sp)
lw $a1, 4($sp)
lw $a0, 0($sp)
- add $sp, 32
+ addiu $sp, 32
.cfi_adjust_cfa_offset -32
bnez $v0, .Ldo_aput
nop
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
move $a1, $a2
la $t9, artThrowArrayStoreException
- jr $t9 # artThrowArrayStoreException(Class*, Class*, Thread*)
+ jalr $zero, $t9 # artThrowArrayStoreException(Class*, Class*, Thread*)
move $a2, rSELF # pass Thread::Current
END art_quick_aput_obj
/*
- * Entry from managed code when uninitialized static storage, this stub will run the class
- * initializer and deliver the exception on error. On success the static storage base is
- * returned.
- */
- .extern artInitializeStaticStorageFromCode
-ENTRY art_quick_initialize_static_storage
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
- # artInitializeStaticStorageFromCode(uint32_t type_idx, Method* referrer, Thread*)
- jal artInitializeStaticStorageFromCode
- move $a2, rSELF # pass Thread::Current
- RETURN_IF_RESULT_IS_NON_ZERO
-END art_quick_initialize_static_storage
-
- /*
- * Entry from managed code when dex cache misses for a type_idx.
- */
- .extern artInitializeTypeFromCode
-ENTRY art_quick_initialize_type
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
- # artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*)
- jal artInitializeTypeFromCode
- move $a2, rSELF # pass Thread::Current
- RETURN_IF_RESULT_IS_NON_ZERO
-END art_quick_initialize_type
-
- /*
- * Entry from managed code when type_idx needs to be checked for access and dex cache may also
- * miss.
- */
- .extern artInitializeTypeAndVerifyAccessFromCode
-ENTRY art_quick_initialize_type_and_verify_access
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
- # artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*)
- jal artInitializeTypeAndVerifyAccessFromCode
- move $a2, rSELF # pass Thread::Current
- RETURN_IF_RESULT_IS_NON_ZERO
-END art_quick_initialize_type_and_verify_access
- /*
* Called by managed code to resolve a static field and load a boolean primitive value.
*/
.extern artGetBooleanStaticFromCode
@@ -954,6 +901,7 @@
.extern artSet64StaticFromCode
ENTRY art_quick_set64_static
lw $a1, 0($sp) # pass referrer's Method*
+ # 64 bit new_val is in a2:a3 pair
SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
jal artSet64StaticFromCode # (field_idx, referrer, new_val, Thread*)
sw rSELF, 16($sp) # pass Thread::Current
@@ -1014,6 +962,7 @@
.extern artSet64InstanceFromCode
ENTRY art_quick_set64_instance
lw $t1, 0($sp) # load referrer's Method*
+ # 64 bit new_val is in a2:a3 pair
SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
sw rSELF, 20($sp) # pass Thread::Current
jal artSet64InstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*)
@@ -1033,22 +982,6 @@
RETURN_IF_ZERO
END art_quick_set_obj_instance
- /*
- * Entry from managed code to resolve a string, this stub will allocate a String and deliver an
- * exception on error. On success the String is returned. R0 holds the referring method,
- * R1 holds the string index. The fast path check for hit in strings cache has already been
- * performed.
- */
- .extern artResolveStringFromCode
-ENTRY art_quick_resolve_string
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
- # artResolveStringFromCode(Method* referrer, uint32_t string_idx, Thread*)
- jal artResolveStringFromCode
- move $a2, rSELF # pass Thread::Current
- RETURN_IF_RESULT_IS_NON_ZERO
-END art_quick_resolve_string
-
-
// Macro to facilitate adding new allocation entrypoints.
.macro TWO_ARG_DOWNCALL name, entrypoint, return
.extern \entrypoint
@@ -1074,14 +1007,40 @@
GENERATE_ALL_ALLOC_ENTRYPOINTS
/*
+ * Entry from managed code to resolve a string, this stub will allocate a String and deliver an
+ * exception on error. On success the String is returned. R0 holds the referring method,
+ * R1 holds the string index. The fast path check for hit in strings cache has already been
+ * performed.
+ */
+TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+
+ /*
+ * Entry from managed code when uninitialized static storage, this stub will run the class
+ * initializer and deliver the exception on error. On success the static storage base is
+ * returned.
+ */
+TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+
+ /*
+ * Entry from managed code when dex cache misses for a type_idx.
+ */
+TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+
+ /*
+ * Entry from managed code when type_idx needs to be checked for access and dex cache may also
+ * miss.
+ */
+TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+
+ /*
* Called by managed code when the value in rSUSPEND has been decremented to 0.
*/
.extern artTestSuspendFromCode
ENTRY art_quick_test_suspend
lh $a0, THREAD_FLAGS_OFFSET(rSELF)
bnez $a0, 1f
- addi rSUSPEND, $zero, SUSPEND_CHECK_INTERVAL # reset rSUSPEND to SUSPEND_CHECK_INTERVAL
- jr $ra
+ addiu rSUSPEND, $zero, SUSPEND_CHECK_INTERVAL # reset rSUSPEND to SUSPEND_CHECK_INTERVAL
+ jalr $zero, $ra
nop
1:
SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves for stack crawl
@@ -1103,9 +1062,10 @@
lw $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
bnez $t0, 1f
- mtc1 $v0, $f0 # place return value to FP return value
- jr $ra
- mtc1 $v1, $f1 # place return value to FP return value
+ # don't care if $v0 and/or $v1 are modified, when exception branch taken
+ MTD $v0, $v1, $f0, $f1 # move float value to return value
+ jalr $zero, $ra
+ nop
1:
DELIVER_PENDING_EXCEPTION
END art_quick_proxy_invoke_handler
@@ -1121,7 +1081,7 @@
add $a0, $t0 # get address of target method
lw $a0, MIRROR_OBJECT_ARRAY_DATA_OFFSET($a0) # load the target method
la $t9, art_quick_invoke_interface_trampoline
- jr $t9
+ jalr $zero, $t9
END art_quick_imt_conflict_trampoline
.extern artQuickResolutionTrampoline
@@ -1134,7 +1094,7 @@
lw $a0, ARG_SLOT_SIZE($sp) # load resolved method to $a0
RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
move $t9, $v0 # code pointer must be in $t9 to generate the global pointer
- jr $v0 # tail call to method
+ jalr $zero, $v0 # tail call to method
nop
1:
RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
@@ -1191,9 +1151,9 @@
# tear dpown the callee-save frame
RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
- mtc1 $v0, $f0 # place return value to FP return value
- jr $ra
- mtc1 $v1, $f1 # place return value to FP return value
+ MTD $v0, $v1, $f0, $f1 # move float value to return value
+ jalr $zero, $ra
+ nop
1:
move $sp, $s8 # tear down the alloca
@@ -1211,9 +1171,10 @@
lw $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
bnez $t0, 1f
- mtc1 $v0, $f0 # place return value to FP return value
- jr $ra
- mtc1 $v1, $f1 # place return value to FP return value
+ # don't care if $v0 and/or $v1 are modified, when exception branch taken
+ MTD $v0, $v1, $f0, $f1 # move float value to return value
+ jalr $zero, $ra
+ nop
1:
DELIVER_PENDING_EXCEPTION
END art_quick_to_interpreter_bridge
@@ -1248,12 +1209,10 @@
sw $v0, 12($sp)
.cfi_rel_offset 2, 32
sw $v1, 8($sp)
- .cfi_rel_offset 3, 36
- s.s $f0, 4($sp)
- s.s $f1, 0($sp)
+ .cfi_rel_offset 3, 36
+ s.d $f0, 0($sp)
SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
- s.s $f0, 16($sp) # pass fpr result
- s.s $f1, 20($sp)
+ s.d $f0, 16($sp) # pass fpr result
move $a2, $v0 # pass gpr result
move $a3, $v1
addiu $a1, $sp, ARG_SLOT_SIZE # pass $sp (remove arg slots)
@@ -1264,9 +1223,8 @@
addiu $sp, $sp, ARG_SLOT_SIZE+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE # args slot + refs_only callee save frame
lw $v0, 12($sp) # restore return values
lw $v1, 8($sp)
- l.s $f0, 4($sp)
- l.s $f1, 0($sp)
- jr $t0 # return
+ l.d $f0, 0($sp)
+ jalr $zero, $t0 # return
addiu $sp, $sp, 16 # remove temp storage from stack
.cfi_adjust_cfa_offset -16
END art_quick_instrumentation_exit
@@ -1300,11 +1258,15 @@
srl $a0, 1
srl $a0, $v1 # alo<- alo >> (32-(shift&31))
sll $v1, $a1, $a2 # rhi<- ahi << (shift&31)
- or $v1, $a0 # rhi<- rhi | alo
andi $a2, 0x20 # shift< shift & 0x20
- movn $v1, $v0, $a2 # rhi<- rlo (if shift&0x20)
- jr $ra
- movn $v0, $zero, $a2 # rlo<- 0 (if shift&0x20)
+ beqz $a2, 1f
+ or $v1, $a0 # rhi<- rhi | alo
+
+ move $v1, $v0 # rhi<- rlo (if shift&0x20)
+ move $v0, $zero # rlo<- 0 (if shift&0x20)
+
+1: jalr $zero, $ra
+ nop
END art_quick_shl_long
/*
@@ -1324,11 +1286,15 @@
not $a0, $a2 # alo<- 31-shift (shift is 5b)
sll $a1, 1
sll $a1, $a0 # ahi<- ahi << (32-(shift&31))
- or $v0, $a1 # rlo<- rlo | ahi
andi $a2, 0x20 # shift & 0x20
- movn $v0, $v1, $a2 # rlo<- rhi (if shift&0x20)
- jr $ra
- movn $v1, $a3, $a2 # rhi<- sign(ahi) (if shift&0x20)
+ beqz $a2, 1f
+ or $v0, $a1 # rlo<- rlo | ahi
+
+ move $v0, $v1 # rlo<- rhi (if shift&0x20)
+ move $v1, $a3 # rhi<- sign(ahi) (if shift&0x20)
+
+1: jalr $zero, $ra
+ nop
END art_quick_shr_long
/*
@@ -1348,11 +1314,15 @@
not $a0, $a2 # alo<- 31-shift (shift is 5b)
sll $a1, 1
sll $a1, $a0 # ahi<- ahi << (32-(shift&31))
- or $v0, $a1 # rlo<- rlo | ahi
andi $a2, 0x20 # shift & 0x20
- movn $v0, $v1, $a2 # rlo<- rhi (if shift&0x20)
- jr $ra
- movn $v1, $zero, $a2 # rhi<- 0 (if shift&0x20)
+ beqz $a2, 1f
+ or $v0, $a1 # rlo<- rlo | ahi
+
+ move $v0, $v1 # rlo<- rhi (if shift&0x20)
+ move $v1, $zero # rhi<- 0 (if shift&0x20)
+
+1: jalr $zero, $ra
+ nop
END art_quick_ushr_long
UNIMPLEMENTED art_quick_indexof
diff --git a/runtime/arch/mips64/asm_support_mips64.S b/runtime/arch/mips64/asm_support_mips64.S
new file mode 100644
index 0000000..10976bb
--- /dev/null
+++ b/runtime/arch/mips64/asm_support_mips64.S
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_ARCH_MIPS64_ASM_SUPPORT_MIPS64_S_
+#define ART_RUNTIME_ARCH_MIPS64_ASM_SUPPORT_MIPS64_S_
+
+#include "asm_support_mips64.h"
+
+// Define special registers.
+
+// Register holding suspend check count down.
+#define rSUSPEND $s0
+// Register holding Thread::Current().
+#define rSELF $s1
+
+
+ // Declare a function called name, sets up $gp.
+.macro ENTRY name
+ .type \name, %function
+ .global \name
+ // Cache alignment for function entry.
+ .balign 16
+\name:
+ .cfi_startproc
+ // Ensure we get a sane starting CFA.
+ .cfi_def_cfa $sp,0
+ // Load $gp. We expect that ".set noreorder" is in effect.
+ .cpload $t9
+ // Declare a local convenience label to be branched to when $gp is already set up.
+.L\name\()_gp_set:
+.endm
+
+ // Declare a function called name, doesn't set up $gp.
+.macro ENTRY_NO_GP name
+ .type \name, %function
+ .global \name
+ // Cache alignment for function entry.
+ .balign 16
+\name:
+ .cfi_startproc
+ // Ensure we get a sane starting CFA.
+ .cfi_def_cfa $sp,0
+.endm
+
+.macro END name
+ .cfi_endproc
+ .size \name, .-\name
+.endm
+
+.macro UNIMPLEMENTED name
+ ENTRY \name
+ break
+ break
+ END \name
+.endm
+
+
+#endif // ART_RUNTIME_ARCH_MIPS64_ASM_SUPPORT_MIPS64_S_
diff --git a/runtime/arch/mips64/asm_support_mips64.h b/runtime/arch/mips64/asm_support_mips64.h
new file mode 100644
index 0000000..995fcf3
--- /dev/null
+++ b/runtime/arch/mips64/asm_support_mips64.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_ARCH_MIPS64_ASM_SUPPORT_MIPS64_H_
+#define ART_RUNTIME_ARCH_MIPS64_ASM_SUPPORT_MIPS64_H_
+
+#include "asm_support.h"
+
+// 64 ($f24-$f31) + 64 ($s0-$s7) + 8 ($gp) + 8 ($s8) + 8 ($ra) + 1x8 bytes padding
+#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 160
+// 48 ($s2-$s7) + 8 ($gp) + 8 ($s8) + 8 ($ra) + 1x8 bytes padding
+#define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 80
+// $f12-$f19, $a1-$a7, $s2-$s7 + $gp + $s8 + $ra, 16 total + 1x8 bytes padding + method*
+#define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 208
+
+#endif // ART_RUNTIME_ARCH_MIPS64_ASM_SUPPORT_MIPS64_H_
diff --git a/runtime/arch/mips64/context_mips64.cc b/runtime/arch/mips64/context_mips64.cc
new file mode 100644
index 0000000..1c96bd4
--- /dev/null
+++ b/runtime/arch/mips64/context_mips64.cc
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "context_mips64.h"
+
+#include "mirror/art_method-inl.h"
+#include "quick/quick_method_frame_info.h"
+#include "util.h"
+
+namespace art {
+namespace mips64 {
+
+static constexpr uintptr_t gZero = 0;
+
+void Mips64Context::Reset() {
+ for (size_t i = 0; i < kNumberOfGpuRegisters; i++) {
+ gprs_[i] = nullptr;
+ }
+ for (size_t i = 0; i < kNumberOfFpuRegisters; i++) {
+ fprs_[i] = nullptr;
+ }
+ gprs_[SP] = &sp_;
+ gprs_[RA] = &ra_;
+ // Initialize registers with easy to spot debug values.
+ sp_ = Mips64Context::kBadGprBase + SP;
+ ra_ = Mips64Context::kBadGprBase + RA;
+}
+
+void Mips64Context::FillCalleeSaves(const StackVisitor& fr) {
+ mirror::ArtMethod* method = fr.GetMethod();
+ const QuickMethodFrameInfo frame_info = method->GetQuickFrameInfo();
+ size_t spill_count = POPCOUNT(frame_info.CoreSpillMask());
+ size_t fp_spill_count = POPCOUNT(frame_info.FpSpillMask());
+ if (spill_count > 0) {
+ // Lowest number spill is farthest away, walk registers and fill into context.
+ int j = 1;
+ for (size_t i = 0; i < kNumberOfGpuRegisters; i++) {
+ if (((frame_info.CoreSpillMask() >> i) & 1) != 0) {
+ gprs_[i] = fr.CalleeSaveAddress(spill_count - j, frame_info.FrameSizeInBytes());
+ j++;
+ }
+ }
+ }
+ if (fp_spill_count > 0) {
+ // Lowest number spill is farthest away, walk registers and fill into context.
+ int j = 1;
+ for (size_t i = 0; i < kNumberOfFpuRegisters; i++) {
+ if (((frame_info.FpSpillMask() >> i) & 1) != 0) {
+ fprs_[i] = fr.CalleeSaveAddress(spill_count + fp_spill_count - j,
+ frame_info.FrameSizeInBytes());
+ j++;
+ }
+ }
+ }
+}
+
+void Mips64Context::SetGPR(uint32_t reg, uintptr_t value) {
+ CHECK_LT(reg, static_cast<uint32_t>(kNumberOfGpuRegisters));
+ DCHECK(IsAccessibleGPR(reg));
+ CHECK_NE(gprs_[reg], &gZero); // Can't overwrite this static value since they are never reset.
+ *gprs_[reg] = value;
+}
+
+void Mips64Context::SetFPR(uint32_t reg, uintptr_t value) {
+ CHECK_LT(reg, static_cast<uint32_t>(kNumberOfFpuRegisters));
+ DCHECK(IsAccessibleFPR(reg));
+ CHECK_NE(fprs_[reg], &gZero); // Can't overwrite this static value since they are never reset.
+ *fprs_[reg] = value;
+}
+
+void Mips64Context::SmashCallerSaves() {
+ // This needs to be 0 because we want a null/zero return value.
+ gprs_[V0] = const_cast<uintptr_t*>(&gZero);
+ gprs_[V1] = const_cast<uintptr_t*>(&gZero);
+ gprs_[A1] = nullptr;
+ gprs_[A0] = nullptr;
+ gprs_[A2] = nullptr;
+ gprs_[A3] = nullptr;
+ gprs_[A4] = nullptr;
+ gprs_[A5] = nullptr;
+ gprs_[A6] = nullptr;
+ gprs_[A7] = nullptr;
+
+ // f0-f23 are caller-saved; f24-f31 are callee-saved.
+ fprs_[F0] = nullptr;
+ fprs_[F1] = nullptr;
+ fprs_[F2] = nullptr;
+ fprs_[F3] = nullptr;
+ fprs_[F4] = nullptr;
+ fprs_[F5] = nullptr;
+ fprs_[F6] = nullptr;
+ fprs_[F7] = nullptr;
+ fprs_[F8] = nullptr;
+ fprs_[F9] = nullptr;
+ fprs_[F10] = nullptr;
+ fprs_[F11] = nullptr;
+ fprs_[F12] = nullptr;
+ fprs_[F13] = nullptr;
+ fprs_[F14] = nullptr;
+ fprs_[F15] = nullptr;
+ fprs_[F16] = nullptr;
+ fprs_[F17] = nullptr;
+ fprs_[F18] = nullptr;
+ fprs_[F19] = nullptr;
+ fprs_[F20] = nullptr;
+ fprs_[F21] = nullptr;
+ fprs_[F22] = nullptr;
+ fprs_[F23] = nullptr;
+}
+
+extern "C" void art_quick_do_long_jump(uintptr_t*, uintptr_t*);
+
+void Mips64Context::DoLongJump() {
+ uintptr_t gprs[kNumberOfGpuRegisters];
+ uintptr_t fprs[kNumberOfFpuRegisters];
+ for (size_t i = 0; i < kNumberOfGpuRegisters; ++i) {
+ gprs[i] = gprs_[i] != nullptr ? *gprs_[i] : Mips64Context::kBadGprBase + i;
+ }
+ for (size_t i = 0; i < kNumberOfFpuRegisters; ++i) {
+ fprs[i] = fprs_[i] != nullptr ? *fprs_[i] : Mips64Context::kBadFprBase + i;
+ }
+ art_quick_do_long_jump(gprs, fprs);
+}
+
+} // namespace mips64
+} // namespace art
diff --git a/runtime/arch/mips64/context_mips64.h b/runtime/arch/mips64/context_mips64.h
new file mode 100644
index 0000000..1046723
--- /dev/null
+++ b/runtime/arch/mips64/context_mips64.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_ARCH_MIPS64_CONTEXT_MIPS64_H_
+#define ART_RUNTIME_ARCH_MIPS64_CONTEXT_MIPS64_H_
+
+#include "arch/context.h"
+#include "base/logging.h"
+#include "registers_mips64.h"
+
+namespace art {
+namespace mips64 {
+
+class Mips64Context : public Context {
+ public:
+ Mips64Context() {
+ Reset();
+ }
+ virtual ~Mips64Context() {}
+
+ void Reset() OVERRIDE;
+
+ void FillCalleeSaves(const StackVisitor& fr) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void SetSP(uintptr_t new_sp) OVERRIDE {
+ SetGPR(SP, new_sp);
+ }
+
+ void SetPC(uintptr_t new_pc) OVERRIDE {
+ SetGPR(RA, new_pc);
+ }
+
+ bool IsAccessibleGPR(uint32_t reg) OVERRIDE {
+ DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfGpuRegisters));
+ return gprs_[reg] != nullptr;
+ }
+
+ uintptr_t* GetGPRAddress(uint32_t reg) OVERRIDE {
+ DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfGpuRegisters));
+ return gprs_[reg];
+ }
+
+ uintptr_t GetGPR(uint32_t reg) OVERRIDE {
+ CHECK_LT(reg, static_cast<uint32_t>(kNumberOfGpuRegisters));
+ DCHECK(IsAccessibleGPR(reg));
+ return *gprs_[reg];
+ }
+
+ void SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
+
+ bool IsAccessibleFPR(uint32_t reg) OVERRIDE {
+ CHECK_LT(reg, static_cast<uint32_t>(kNumberOfFpuRegisters));
+ return fprs_[reg] != nullptr;
+ }
+
+ uintptr_t GetFPR(uint32_t reg) OVERRIDE {
+ CHECK_LT(reg, static_cast<uint32_t>(kNumberOfFpuRegisters));
+ DCHECK(IsAccessibleFPR(reg));
+ return *fprs_[reg];
+ }
+
+ void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
+
+ void SmashCallerSaves() OVERRIDE;
+ void DoLongJump() OVERRIDE;
+
+ private:
+ // Pointers to registers in the stack, initialized to NULL except for the special cases below.
+ uintptr_t* gprs_[kNumberOfGpuRegisters];
+ uint64_t* fprs_[kNumberOfFpuRegisters];
+ // Hold values for sp and ra (return address) if they are not located within a stack frame.
+ uintptr_t sp_, ra_;
+};
+} // namespace mips64
+} // namespace art
+
+#endif // ART_RUNTIME_ARCH_MIPS64_CONTEXT_MIPS64_H_
diff --git a/runtime/arch/mips64/entrypoints_init_mips64.cc b/runtime/arch/mips64/entrypoints_init_mips64.cc
new file mode 100644
index 0000000..4a3bf02
--- /dev/null
+++ b/runtime/arch/mips64/entrypoints_init_mips64.cc
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "atomic.h"
+#include "entrypoints/interpreter/interpreter_entrypoints.h"
+#include "entrypoints/jni/jni_entrypoints.h"
+#include "entrypoints/quick/quick_alloc_entrypoints.h"
+#include "entrypoints/quick/quick_default_externs.h"
+#include "entrypoints/quick/quick_entrypoints.h"
+#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/math_entrypoints.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
+#include "interpreter/interpreter.h"
+
+namespace art {
+
+// Cast entrypoints.
+extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass,
+ const mirror::Class* ref_class);
+// Math entrypoints.
+extern int32_t CmpgDouble(double a, double b);
+extern int32_t CmplDouble(double a, double b);
+extern int32_t CmpgFloat(float a, float b);
+extern int32_t CmplFloat(float a, float b);
+extern "C" int64_t artLmul(int64_t a, int64_t b);
+extern "C" int64_t artLdiv(int64_t a, int64_t b);
+extern "C" int64_t artLmod(int64_t a, int64_t b);
+
+// Math conversions.
+extern "C" int32_t __fixsfsi(float op1); // FLOAT_TO_INT
+extern "C" int32_t __fixdfsi(double op1); // DOUBLE_TO_INT
+extern "C" float __floatdisf(int64_t op1); // LONG_TO_FLOAT
+extern "C" double __floatdidf(int64_t op1); // LONG_TO_DOUBLE
+extern "C" int64_t __fixsfdi(float op1); // FLOAT_TO_LONG
+extern "C" int64_t __fixdfdi(double op1); // DOUBLE_TO_LONG
+
+// Single-precision FP arithmetics.
+extern "C" float fmodf(float a, float b); // REM_FLOAT[_2ADDR]
+
+// Double-precision FP arithmetics.
+extern "C" double fmod(double a, double b); // REM_DOUBLE[_2ADDR]
+
+// Long long arithmetics - REM_LONG[_2ADDR] and DIV_LONG[_2ADDR]
+extern "C" int64_t __divdi3(int64_t, int64_t);
+extern "C" int64_t __moddi3(int64_t, int64_t);
+
+void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
+ QuickEntryPoints* qpoints) {
+ // Interpreter
+ ipoints->pInterpreterToInterpreterBridge = artInterpreterToInterpreterBridge;
+ ipoints->pInterpreterToCompiledCodeBridge = artInterpreterToCompiledCodeBridge;
+
+ // JNI
+ jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
+
+ // Alloc
+ ResetQuickAllocEntryPoints(qpoints);
+
+ // Cast
+ qpoints->pInstanceofNonTrivial = artIsAssignableFromCode;
+ qpoints->pCheckCast = art_quick_check_cast;
+
+ // DexCache
+ qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
+ qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
+ qpoints->pInitializeType = art_quick_initialize_type;
+ qpoints->pResolveString = art_quick_resolve_string;
+
+ // Field
+ qpoints->pSet8Instance = art_quick_set8_instance;
+ qpoints->pSet8Static = art_quick_set8_static;
+ qpoints->pSet16Instance = art_quick_set16_instance;
+ qpoints->pSet16Static = art_quick_set16_static;
+ qpoints->pSet32Instance = art_quick_set32_instance;
+ qpoints->pSet32Static = art_quick_set32_static;
+ qpoints->pSet64Instance = art_quick_set64_instance;
+ qpoints->pSet64Static = art_quick_set64_static;
+ qpoints->pSetObjInstance = art_quick_set_obj_instance;
+ qpoints->pSetObjStatic = art_quick_set_obj_static;
+ qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
+ qpoints->pGetByteInstance = art_quick_get_byte_instance;
+ qpoints->pGetCharInstance = art_quick_get_char_instance;
+ qpoints->pGetShortInstance = art_quick_get_short_instance;
+ qpoints->pGet32Instance = art_quick_get32_instance;
+ qpoints->pGet64Instance = art_quick_get64_instance;
+ qpoints->pGetObjInstance = art_quick_get_obj_instance;
+ qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
+ qpoints->pGetByteStatic = art_quick_get_byte_static;
+ qpoints->pGetCharStatic = art_quick_get_char_static;
+ qpoints->pGetShortStatic = art_quick_get_short_static;
+ qpoints->pGet32Static = art_quick_get32_static;
+ qpoints->pGet64Static = art_quick_get64_static;
+ qpoints->pGetObjStatic = art_quick_get_obj_static;
+
+ // Array
+ qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
+ qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
+ qpoints->pAputObject = art_quick_aput_obj;
+ qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
+
+ // JNI
+ qpoints->pJniMethodStart = JniMethodStart;
+ qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized;
+ qpoints->pJniMethodEnd = JniMethodEnd;
+ qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
+ qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
+ qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
+ qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
+
+ // Locks
+ qpoints->pLockObject = art_quick_lock_object;
+ qpoints->pUnlockObject = art_quick_unlock_object;
+
+ // Math
+ qpoints->pCmpgDouble = CmpgDouble;
+ qpoints->pCmpgFloat = CmpgFloat;
+ qpoints->pCmplDouble = CmplDouble;
+ qpoints->pCmplFloat = CmplFloat;
+ qpoints->pFmod = fmod;
+ qpoints->pL2d = art_l2d;
+ qpoints->pFmodf = fmodf;
+ qpoints->pL2f = art_l2f;
+ qpoints->pD2iz = art_d2i;
+ qpoints->pF2iz = art_f2i;
+ qpoints->pIdivmod = NULL;
+ qpoints->pD2l = art_d2l;
+ qpoints->pF2l = art_f2l;
+ qpoints->pLdiv = artLdiv;
+ qpoints->pLmod = artLmod;
+ qpoints->pLmul = artLmul;
+ qpoints->pShlLong = NULL;
+ qpoints->pShrLong = NULL;
+ qpoints->pUshrLong = NULL;
+
+ // Intrinsics
+ qpoints->pIndexOf = art_quick_indexof;
+ qpoints->pStringCompareTo = art_quick_string_compareto;
+ qpoints->pMemcpy = memcpy;
+
+ // Invocation
+ qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
+ qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
+ qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
+ qpoints->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check;
+ qpoints->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check;
+ qpoints->pInvokeStaticTrampolineWithAccessCheck = art_quick_invoke_static_trampoline_with_access_check;
+ qpoints->pInvokeSuperTrampolineWithAccessCheck = art_quick_invoke_super_trampoline_with_access_check;
+ qpoints->pInvokeVirtualTrampolineWithAccessCheck = art_quick_invoke_virtual_trampoline_with_access_check;
+
+ // Thread
+ qpoints->pTestSuspend = art_quick_test_suspend;
+
+ // Throws
+ qpoints->pDeliverException = art_quick_deliver_exception;
+ qpoints->pThrowArrayBounds = art_quick_throw_array_bounds;
+ qpoints->pThrowDivZero = art_quick_throw_div_zero;
+ qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
+ qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
+ qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
+
+ // TODO - use lld/scd instructions for Mips64
+ // Atomic 64-bit load/store
+ qpoints->pA64Load = QuasiAtomic::Read64;
+ qpoints->pA64Store = QuasiAtomic::Write64;
+};
+
+} // namespace art
diff --git a/runtime/arch/mips64/fault_handler_mips64.cc b/runtime/arch/mips64/fault_handler_mips64.cc
new file mode 100644
index 0000000..7b5cd49
--- /dev/null
+++ b/runtime/arch/mips64/fault_handler_mips64.cc
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "fault_handler.h"
+#include <sys/ucontext.h>
+#include "base/macros.h"
+#include "globals.h"
+#include "base/logging.h"
+#include "base/hex_dump.h"
+
+
+//
+// Mips64 specific fault handler functions.
+//
+
+namespace art {
+
+void FaultManager::HandleNestedSignal(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+ void* context ATTRIBUTE_UNUSED) {
+}
+
+void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo ATTRIBUTE_UNUSED,
+ void* context ATTRIBUTE_UNUSED,
+ mirror::ArtMethod** out_method ATTRIBUTE_UNUSED,
+ uintptr_t* out_return_pc ATTRIBUTE_UNUSED,
+ uintptr_t* out_sp ATTRIBUTE_UNUSED) {
+}
+
+bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+ void* context ATTRIBUTE_UNUSED) {
+ return false;
+}
+
+bool SuspensionHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+ void* context ATTRIBUTE_UNUSED) {
+ return false;
+}
+
+bool StackOverflowHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+ void* context ATTRIBUTE_UNUSED) {
+ return false;
+}
+} // namespace art
diff --git a/runtime/arch/mips64/instruction_set_features_mips64.cc b/runtime/arch/mips64/instruction_set_features_mips64.cc
new file mode 100644
index 0000000..26478cb
--- /dev/null
+++ b/runtime/arch/mips64/instruction_set_features_mips64.cc
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "instruction_set_features_mips64.h"
+
+#include <fstream>
+#include <sstream>
+
+#include "base/stringprintf.h"
+#include "utils.h" // For Trim.
+
+namespace art {
+
+const Mips64InstructionSetFeatures* Mips64InstructionSetFeatures::FromVariant(
+ const std::string& variant, std::string* error_msg ATTRIBUTE_UNUSED) {
+ // TODO: r6 variant.
+ if (variant != "default") {
+ std::ostringstream os;
+ LOG(WARNING) << "Unexpected CPU variant for Mips64 using defaults: " << variant;
+ }
+ bool smp = true; // Conservative default.
+ return new Mips64InstructionSetFeatures(smp);
+}
+
+const Mips64InstructionSetFeatures* Mips64InstructionSetFeatures::FromBitmap(uint32_t bitmap) {
+ bool smp = (bitmap & kSmpBitfield) != 0;
+ return new Mips64InstructionSetFeatures(smp);
+}
+
+const Mips64InstructionSetFeatures* Mips64InstructionSetFeatures::FromCppDefines() {
+ const bool smp = true;
+
+ return new Mips64InstructionSetFeatures(smp);
+}
+
+const Mips64InstructionSetFeatures* Mips64InstructionSetFeatures::FromCpuInfo() {
+ // Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that
+ // the kernel puts the appropriate feature flags in here. Sometimes it doesn't.
+ bool smp = false;
+
+ std::ifstream in("/proc/cpuinfo");
+ if (!in.fail()) {
+ while (!in.eof()) {
+ std::string line;
+ std::getline(in, line);
+ if (!in.eof()) {
+ LOG(INFO) << "cpuinfo line: " << line;
+ if (line.find("processor") != std::string::npos && line.find(": 1") != std::string::npos) {
+ smp = true;
+ }
+ }
+ }
+ in.close();
+ } else {
+ LOG(ERROR) << "Failed to open /proc/cpuinfo";
+ }
+ return new Mips64InstructionSetFeatures(smp);
+}
+
+const Mips64InstructionSetFeatures* Mips64InstructionSetFeatures::FromHwcap() {
+ UNIMPLEMENTED(WARNING);
+ return FromCppDefines();
+}
+
+const Mips64InstructionSetFeatures* Mips64InstructionSetFeatures::FromAssembly() {
+ UNIMPLEMENTED(WARNING);
+ return FromCppDefines();
+}
+
+bool Mips64InstructionSetFeatures::Equals(const InstructionSetFeatures* other) const {
+ if (kMips64 != other->GetInstructionSet()) {
+ return false;
+ }
+ return (IsSmp() == other->IsSmp());
+}
+
+uint32_t Mips64InstructionSetFeatures::AsBitmap() const {
+ return (IsSmp() ? kSmpBitfield : 0);
+}
+
+std::string Mips64InstructionSetFeatures::GetFeatureString() const {
+ std::string result;
+ if (IsSmp()) {
+ result += "smp";
+ } else {
+ result += "-smp";
+ }
+ return result;
+}
+
+const InstructionSetFeatures* Mips64InstructionSetFeatures::AddFeaturesFromSplitString(
+ const bool smp, const std::vector<std::string>& features, std::string* error_msg) const {
+ auto i = features.begin();
+ if (i != features.end()) {
+ // We don't have any features.
+ std::string feature = Trim(*i);
+ *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str());
+ return nullptr;
+ }
+ return new Mips64InstructionSetFeatures(smp);
+}
+
+} // namespace art
diff --git a/runtime/arch/mips64/instruction_set_features_mips64.h b/runtime/arch/mips64/instruction_set_features_mips64.h
new file mode 100644
index 0000000..d5d6012
--- /dev/null
+++ b/runtime/arch/mips64/instruction_set_features_mips64.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_ARCH_MIPS64_INSTRUCTION_SET_FEATURES_MIPS64_H_
+#define ART_RUNTIME_ARCH_MIPS64_INSTRUCTION_SET_FEATURES_MIPS64_H_
+
+#include "arch/instruction_set_features.h"
+
+namespace art {
+
+// Instruction set features relevant to the MIPS64 architecture.
+class Mips64InstructionSetFeatures FINAL : public InstructionSetFeatures {
+ public:
+ // Process a CPU variant string like "r4000" and create InstructionSetFeatures.
+ static const Mips64InstructionSetFeatures* FromVariant(const std::string& variant,
+ std::string* error_msg);
+
+ // Parse a bitmap and create an InstructionSetFeatures.
+ static const Mips64InstructionSetFeatures* FromBitmap(uint32_t bitmap);
+
+ // Turn C pre-processor #defines into the equivalent instruction set features.
+ static const Mips64InstructionSetFeatures* FromCppDefines();
+
+ // Process /proc/cpuinfo and use kRuntimeISA to produce InstructionSetFeatures.
+ static const Mips64InstructionSetFeatures* FromCpuInfo();
+
+ // Process the auxiliary vector AT_HWCAP entry and use kRuntimeISA to produce
+ // InstructionSetFeatures.
+ static const Mips64InstructionSetFeatures* FromHwcap();
+
+ // Use assembly tests of the current runtime (ie kRuntimeISA) to determine the
+ // InstructionSetFeatures. This works around kernel bugs in AT_HWCAP and /proc/cpuinfo.
+ static const Mips64InstructionSetFeatures* FromAssembly();
+
+ bool Equals(const InstructionSetFeatures* other) const OVERRIDE;
+
+ InstructionSet GetInstructionSet() const OVERRIDE {
+ return kMips64;
+ }
+
+ uint32_t AsBitmap() const OVERRIDE;
+
+ std::string GetFeatureString() const OVERRIDE;
+
+ virtual ~Mips64InstructionSetFeatures() {}
+
+ protected:
+ // Parse a vector of the form "fpu32", "mips2" adding these to a new Mips64InstructionSetFeatures.
+ virtual const InstructionSetFeatures*
+ AddFeaturesFromSplitString(const bool smp, const std::vector<std::string>& features,
+ std::string* error_msg) const OVERRIDE;
+
+ private:
+ explicit Mips64InstructionSetFeatures(bool smp) : InstructionSetFeatures(smp) {
+ }
+
+ // Bitmap positions for encoding features as a bitmap.
+ enum {
+ kSmpBitfield = 1,
+ };
+
+ DISALLOW_COPY_AND_ASSIGN(Mips64InstructionSetFeatures);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_ARCH_MIPS64_INSTRUCTION_SET_FEATURES_MIPS64_H_
diff --git a/runtime/arch/mips64/instruction_set_features_mips64_test.cc b/runtime/arch/mips64/instruction_set_features_mips64_test.cc
new file mode 100644
index 0000000..dc34506
--- /dev/null
+++ b/runtime/arch/mips64/instruction_set_features_mips64_test.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "instruction_set_features_mips64.h"
+
+#include <gtest/gtest.h>
+
+namespace art {
+
+TEST(Mips64InstructionSetFeaturesTest, Mips64Features) {
+ std::string error_msg;
+ std::unique_ptr<const InstructionSetFeatures> mips64_features(
+ InstructionSetFeatures::FromVariant(kMips64, "default", &error_msg));
+ ASSERT_TRUE(mips64_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(mips64_features->GetInstructionSet(), kMips64);
+ EXPECT_TRUE(mips64_features->Equals(mips64_features.get()));
+ EXPECT_STREQ("smp", mips64_features->GetFeatureString().c_str());
+ EXPECT_EQ(mips64_features->AsBitmap(), 1U);
+}
+
+} // namespace art
diff --git a/runtime/arch/mips64/jni_entrypoints_mips64.S b/runtime/arch/mips64/jni_entrypoints_mips64.S
new file mode 100644
index 0000000..90fd3ee
--- /dev/null
+++ b/runtime/arch/mips64/jni_entrypoints_mips64.S
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "asm_support_mips64.S"
+
+ .set noreorder
+ .balign 16
+
+ /*
+ * Jni dlsym lookup stub.
+ */
+ .extern artFindNativeMethod
+ENTRY art_jni_dlsym_lookup_stub
+ daddiu $sp, $sp, -80 # save a0-a7 and $ra
+ .cfi_adjust_cfa_offset 80
+ sd $ra, 64($sp)
+ .cfi_rel_offset 31, 64
+ sw $a7, 56($sp)
+ .cfi_rel_offset 11, 56
+ sw $a6, 48($sp)
+ .cfi_rel_offset 10, 48
+ sw $a5, 40($sp)
+ .cfi_rel_offset 9, 40
+ sw $a4, 32($sp)
+ .cfi_rel_offset 8, 32
+ sw $a3, 24($sp)
+ .cfi_rel_offset 7, 24
+ sw $a2, 16($sp)
+ .cfi_rel_offset 6, 16
+ sw $a1, 8($sp)
+ .cfi_rel_offset 5, 8
+ sw $a0, 0($sp)
+ .cfi_rel_offset 4, 0
+ jal artFindNativeMethod # (Thread*)
+ move $a0, $s1 # pass Thread::Current()
+ ld $a0, 0($sp) # restore registers from stack
+ .cfi_restore 4
+ ld $a1, 8($sp)
+ .cfi_restore 5
+ ld $a2, 16($sp)
+ .cfi_restore 6
+ ld $a3, 24($sp)
+ .cfi_restore 7
+ ld $a4, 32($sp)
+ .cfi_restore 8
+ ld $a5, 40($sp)
+ .cfi_restore 9
+ ld $a6, 48($sp)
+ .cfi_restore 10
+ ld $a7, 56($sp)
+ .cfi_restore 11
+ ld $ra, 64($sp)
+ .cfi_restore 31
+ beq $v0, $zero, .Lno_native_code_found
+ daddiu $sp, $sp, 80 # restore the stack
+ .cfi_adjust_cfa_offset -80
+ move $t9, $v0 # put method code result in $t9
+ jalr $zero, $t9 # leaf call to method's code
+ nop
+.Lno_native_code_found:
+ jalr $zero, $ra
+ nop
+END art_jni_dlsym_lookup_stub
diff --git a/runtime/arch/mips64/memcmp16_mips64.S b/runtime/arch/mips64/memcmp16_mips64.S
new file mode 100644
index 0000000..962977e
--- /dev/null
+++ b/runtime/arch/mips64/memcmp16_mips64.S
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_ARCH_MIPS64_MEMCMP16_MIPS64_S_
+#define ART_RUNTIME_ARCH_MIPS64_MEMCMP16_MIPS64_S_
+
+#include "asm_support_mips64.S"
+
+.set noreorder
+
+// u4 __memcmp16(const u2*, const u2*, size_t);
+ENTRY_NO_GP __memcmp16
+ move $t0, $zero
+ move $t1, $zero
+ beqz $a2, done /* 0 length string */
+ nop
+ beq $a0, $a1, done /* addresses are identical */
+ nop
+
+1:
+ lhu $t0, 0($a0)
+ lhu $t1, 0($a1)
+ bne $t0, $t1, done
+ nop
+ daddu $a0, 2
+ daddu $a1, 2
+ dsubu $a2, 1
+ bnez $a2, 1b
+ nop
+
+done:
+ dsubu $v0, $t0, $t1
+ j $ra
+ nop
+END __memcmp16
+
+#endif // ART_RUNTIME_ARCH_MIPS64_MEMCMP16_MIPS64_S_
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
new file mode 100644
index 0000000..60e692b
--- /dev/null
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -0,0 +1,936 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "asm_support_mips64.S"
+
+#include "arch/quick_alloc_entrypoints.S"
+
+ .set noreorder
+ .balign 16
+
+ /* Deliver the given exception */
+ .extern artDeliverExceptionFromCode
+ /* Deliver an exception pending on a thread */
+ .extern artDeliverPendingExceptionFromCode
+
+ /*
+ * Macro that sets up the callee save frame to conform with
+ * Runtime::CreateCalleeSaveMethod(kSaveAll)
+ * callee-save: padding + $f24-$f31 + $s0-$s7 + $gp + $ra + $s8 = 19 total + 1x8 bytes padding
+ */
+.macro SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
+ daddiu $sp, $sp, -160
+ .cfi_adjust_cfa_offset 160
+
+ // Ugly compile-time check, but we only have the preprocessor.
+#if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 160)
+#error "SAVE_ALL_CALLEE_SAVE_FRAME(MIPS64) size not as expected."
+#endif
+
+ sd $ra, 152($sp)
+ .cfi_rel_offset 31, 152
+ sd $s8, 144($sp)
+ .cfi_rel_offset 30, 144
+ sd $gp, 136($sp)
+ .cfi_rel_offset 28, 136
+ sd $s7, 128($sp)
+ .cfi_rel_offset 23, 128
+ sd $s6, 120($sp)
+ .cfi_rel_offset 22, 120
+ sd $s5, 112($sp)
+ .cfi_rel_offset 21, 112
+ sd $s4, 104($sp)
+ .cfi_rel_offset 20, 104
+ sd $s3, 96($sp)
+ .cfi_rel_offset 19, 96
+ sd $s2, 88($sp)
+ .cfi_rel_offset 18, 88
+ sd $s1, 80($sp)
+ .cfi_rel_offset 17, 80
+ sd $s0, 72($sp)
+ .cfi_rel_offset 16, 72
+
+ // FP callee-saves
+ s.d $f31, 64($sp)
+ s.d $f30, 56($sp)
+ s.d $f29, 48($sp)
+ s.d $f28, 40($sp)
+ s.d $f27, 32($sp)
+ s.d $f26, 24($sp)
+ s.d $f25, 16($sp)
+ s.d $f24, 8($sp)
+
+ # load appropriate callee-save-method
+ ld $v0, %got(_ZN3art7Runtime9instance_E)($gp)
+ ld $v0, 0($v0)
+ THIS_LOAD_REQUIRES_READ_BARRIER
+ ld $v0, RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET($v0)
+ sw $v0, 0($sp) # Place Method* at bottom of stack.
+ sd $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame.
+.endm
+
+ /*
+ * Macro that sets up the callee save frame to conform with
+ * Runtime::CreateCalleeSaveMethod(kRefsOnly). Restoration assumes
+ * non-moving GC.
+ * Does not include rSUSPEND or rSELF
+ * callee-save: padding + $s2-$s7 + $gp + $ra + $s8 = 9 total + 1x8 bytes padding
+ */
+.macro SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
+ daddiu $sp, $sp, -80
+ .cfi_adjust_cfa_offset 80
+
+ // Ugly compile-time check, but we only have the preprocessor.
+#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 80)
+#error "REFS_ONLY_CALLEE_SAVE_FRAME(MIPS64) size not as expected."
+#endif
+
+ sd $ra, 72($sp)
+ .cfi_rel_offset 31, 72
+ sd $s8, 64($sp)
+ .cfi_rel_offset 30, 64
+ sd $gp, 56($sp)
+ .cfi_rel_offset 28, 56
+ sd $s7, 48($sp)
+ .cfi_rel_offset 23, 48
+ sd $s6, 40($sp)
+ .cfi_rel_offset 22, 40
+ sd $s5, 32($sp)
+ .cfi_rel_offset 21, 32
+ sd $s4, 24($sp)
+ .cfi_rel_offset 20, 24
+ sd $s3, 16($sp)
+ .cfi_rel_offset 19, 16
+ sd $s2, 8($sp)
+ .cfi_rel_offset 18, 8
+ # load appropriate callee-save-method
+ ld $v0, %got(_ZN3art7Runtime9instance_E)($gp)
+ ld $v0, 0($v0)
+ THIS_LOAD_REQUIRES_READ_BARRIER
+ ld $v0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET($v0)
+ sw $v0, 0($sp) # Place Method* at bottom of stack.
+ sd $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame.
+.endm
+
+.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+ ld $ra, 72($sp)
+ .cfi_restore 31
+ ld $s8, 64($sp)
+ .cfi_restore 30
+ ld $gp, 56($sp)
+ .cfi_restore 28
+ ld $s7, 48($sp)
+ .cfi_restore 23
+ ld $s6, 40($sp)
+ .cfi_restore 22
+ ld $s5, 32($sp)
+ .cfi_restore 21
+ ld $s4, 24($sp)
+ .cfi_restore 20
+ ld $s3, 16($sp)
+ .cfi_restore 19
+ ld $s2, 8($sp)
+ .cfi_restore 18
+ daddiu $sp, $sp, 80
+ .cfi_adjust_cfa_offset -80
+.endm
+
+.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
+ ld $ra, 72($sp)
+ .cfi_restore 31
+ ld $s8, 64($sp)
+ .cfi_restore 30
+ ld $gp, 56($sp)
+ .cfi_restore 28
+ ld $s7, 48($sp)
+ .cfi_restore 23
+ ld $s6, 40($sp)
+ .cfi_restore 22
+ ld $s5, 32($sp)
+ .cfi_restore 21
+ ld $s4, 24($sp)
+ .cfi_restore 20
+ ld $s3, 16($sp)
+ .cfi_restore 19
+ ld $s2, 8($sp)
+ .cfi_restore 18
+ jalr $zero, $ra
+ daddiu $sp, $sp, 80
+ .cfi_adjust_cfa_offset -80
+.endm
+
+// This assumes the top part of these stack frame types are identical.
+#define REFS_AND_ARGS_MINUS_REFS_SIZE (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE)
+
+ /*
+ * Macro that sets up the callee save frame to conform with
+ * Runtime::CreateCalleeSaveMethod(kRefsAndArgs). Restoration assumes
+ * non-moving GC.
+ * callee-save: padding + $f12-$f19 + $a1-$a7 + $s2-$s7 + $gp + $ra + $s8 = 24 total + 1 words padding + Method*
+ */
+.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL
+ daddiu $sp, $sp, -208
+ .cfi_adjust_cfa_offset 208
+
+ // Ugly compile-time check, but we only have the preprocessor.
+#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 208)
+#error "REFS_AND_ARGS_CALLEE_SAVE_FRAME(MIPS64) size not as expected."
+#endif
+
+ sd $ra, 200($sp) # = kQuickCalleeSaveFrame_RefAndArgs_LrOffset
+ .cfi_rel_offset 31, 200
+ sd $s8, 192($sp)
+ .cfi_rel_offset 30, 192
+ sd $gp, 184($sp)
+ .cfi_rel_offset 28, 184
+ sd $s7, 176($sp)
+ .cfi_rel_offset 23, 176
+ sd $s6, 168($sp)
+ .cfi_rel_offset 22, 168
+ sd $s5, 160($sp)
+ .cfi_rel_offset 21, 160
+ sd $s4, 152($sp)
+ .cfi_rel_offset 20, 152
+ sd $s3, 144($sp)
+ .cfi_rel_offset 19, 144
+ sd $s2, 136($sp)
+ .cfi_rel_offset 18, 136
+
+ sd $a7, 128($sp)
+ .cfi_rel_offset 11, 128
+ sd $a6, 120($sp)
+ .cfi_rel_offset 10, 120
+ sd $a5, 112($sp)
+ .cfi_rel_offset 9, 112
+ sd $a4, 104($sp)
+ .cfi_rel_offset 8, 104
+ sd $a3, 96($sp)
+ .cfi_rel_offset 7, 96
+ sd $a2, 88($sp)
+ .cfi_rel_offset 6, 88
+ sd $a1, 80($sp) # = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset
+ .cfi_rel_offset 5, 80
+
+ s.d $f19, 72($sp)
+ s.d $f18, 64($sp)
+ s.d $f17, 56($sp)
+ s.d $f16, 48($sp)
+ s.d $f15, 40($sp)
+ s.d $f14, 32($sp)
+ s.d $f13, 24($sp) # = kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset
+ s.d $f12, 16($sp) # This isn't necessary to store.
+
+ # 1x8 bytes paddig + Method*
+ ld $v0, %got(_ZN3art7Runtime9instance_E)($gp)
+ ld $v0, 0($v0)
+ THIS_LOAD_REQUIRES_READ_BARRIER
+ ld $v0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET($v0)
+ sw $v0, 0($sp) # Place Method* at bottom of stack.
+ sd $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame.
+.endm
+
+.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+ SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL
+ # load appropriate callee-save-method
+ ld $v0, %got(_ZN3art7Runtime9instance_E)($gp)
+ ld $v0, 0($v0)
+ THIS_LOAD_REQUIRES_READ_BARRIER
+ ld $v0, RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET($v0)
+ sw $v0, 0($sp) # Place Method* at bottom of stack.
+ sd $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame.
+.endm
+
+.macro RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+ ld $ra, 200($sp)
+ .cfi_restore 31
+ ld $s8, 192($sp)
+ .cfi_restore 30
+ ld $gp, 184($sp)
+ .cfi_restore 28
+ ld $s7, 176($sp)
+ .cfi_restore 23
+ ld $s6, 168($sp)
+ .cfi_restore 22
+ ld $s5, 160($sp)
+ .cfi_restore 21
+ ld $s4, 152($sp)
+ .cfi_restore 20
+ ld $s3, 144($sp)
+ .cfi_restore 19
+ ld $s2, 136($sp)
+ .cfi_restore 18
+
+ ld $a7, 128($sp)
+ .cfi_restore 11
+ ld $a6, 120($sp)
+ .cfi_restore 10
+ ld $a5, 112($sp)
+ .cfi_restore 9
+ ld $a4, 104($sp)
+ .cfi_restore 8
+ ld $a3, 96($sp)
+ .cfi_restore 7
+ ld $a2, 88($sp)
+ .cfi_restore 6
+ ld $a1, 80($sp)
+ .cfi_restore 5
+
+ l.d $f19, 72($sp)
+ l.d $f18, 64($sp)
+ l.d $f17, 56($sp)
+ l.d $f16, 48($sp)
+ l.d $f15, 40($sp)
+ l.d $f14, 32($sp)
+ l.d $f13, 24($sp)
+ l.d $f12, 16($sp)
+
+ daddiu $sp, $sp, 208
+ .cfi_adjust_cfa_offset -208
+.endm
+
+ /*
+ * Macro that set calls through to artDeliverPendingExceptionFromCode,
+ * where the pending
+ * exception is Thread::Current()->exception_
+ */
+.macro DELIVER_PENDING_EXCEPTION
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME # save callee saves for throw
+ dla $t9, artDeliverPendingExceptionFromCode
+ jalr $zero, $t9 # artDeliverPendingExceptionFromCode(Thread*)
+ move $a0, rSELF # pass Thread::Current
+.endm
+
+.macro RETURN_IF_NO_EXCEPTION
+ ld $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+ bne $t0, $zero, 1f # success if no exception is pending
+ nop
+ jalr $zero, $ra
+ nop
+1:
+ DELIVER_PENDING_EXCEPTION
+.endm
+
+.macro RETURN_IF_ZERO
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+ bne $v0, $zero, 1f # success?
+ nop
+ jalr $zero, $ra # return on success
+ nop
+1:
+ DELIVER_PENDING_EXCEPTION
+.endm
+
+.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+ beq $v0, $zero, 1f # success?
+ nop
+ jalr $zero, $ra # return on success
+ nop
+1:
+ DELIVER_PENDING_EXCEPTION
+.endm
+
+ /*
+ * On entry $a0 is uint32_t* gprs_ and $a1 is uint32_t* fprs_
+ * FIXME: just guessing about the shape of the jmpbuf. Where will pc be?
+ */
+ENTRY art_quick_do_long_jump
+ l.d $f0, 0($a1)
+ l.d $f1, 8($a1)
+ l.d $f2, 16($a1)
+ l.d $f3, 24($a1)
+ l.d $f4, 32($a1)
+ l.d $f5, 40($a1)
+ l.d $f6, 48($a1)
+ l.d $f7, 56($a1)
+ l.d $f8, 64($a1)
+ l.d $f9, 72($a1)
+ l.d $f10, 80($a1)
+ l.d $f11, 88($a1)
+ l.d $f12, 96($a1)
+ l.d $f13, 104($a1)
+ l.d $f14, 112($a1)
+ l.d $f15, 120($a1)
+ l.d $f16, 128($a1)
+ l.d $f17, 136($a1)
+ l.d $f18, 144($a1)
+ l.d $f19, 152($a1)
+ l.d $f20, 160($a1)
+ l.d $f21, 168($a1)
+ l.d $f22, 176($a1)
+ l.d $f23, 184($a1)
+ l.d $f24, 192($a1)
+ l.d $f25, 200($a1)
+ l.d $f26, 208($a1)
+ l.d $f27, 216($a1)
+ l.d $f28, 224($a1)
+ l.d $f29, 232($a1)
+ l.d $f30, 240($a1)
+ l.d $f31, 248($a1)
+ .set push
+ .set nomacro
+ .set noat
+# no need to load zero
+ ld $at, 8($a0)
+ .set pop
+ ld $v0, 16($a0)
+ ld $v1, 24($a0)
+# a0 has to be loaded last
+ ld $a1, 40($a0)
+ ld $a2, 48($a0)
+ ld $a3, 56($a0)
+ ld $a4, 64($a0)
+ ld $a5, 72($a0)
+ ld $a6, 80($a0)
+ ld $a7, 88($a0)
+ ld $t0, 96($a0)
+ ld $t1, 104($a0)
+ ld $t2, 112($a0)
+ ld $t3, 120($a0)
+ ld $s0, 128($a0)
+ ld $s1, 136($a0)
+ ld $s2, 144($a0)
+ ld $s3, 152($a0)
+ ld $s4, 160($a0)
+ ld $s5, 168($a0)
+ ld $s6, 176($a0)
+ ld $s7, 184($a0)
+ ld $t8, 192($a0)
+ ld $t9, 200($a0)
+# no need to load k0, k1
+ ld $gp, 224($a0)
+ ld $sp, 232($a0)
+ ld $s8, 240($a0)
+ ld $ra, 248($a0)
+ ld $a0, 32($a0)
+ move $v0, $zero # clear result registers v0 and v1
+ jalr $zero, $ra # do long jump
+ move $v1, $zero
+END art_quick_do_long_jump
+
+UNIMPLEMENTED art_quick_deliver_exception
+UNIMPLEMENTED art_quick_throw_null_pointer_exception
+UNIMPLEMENTED art_quick_throw_div_zero
+UNIMPLEMENTED art_quick_throw_array_bounds
+UNIMPLEMENTED art_quick_throw_stack_overflow
+UNIMPLEMENTED art_quick_throw_no_such_method
+
+UNIMPLEMENTED art_quick_invoke_interface_trampoline
+UNIMPLEMENTED art_quick_invoke_interface_trampoline_with_access_check
+
+UNIMPLEMENTED art_quick_invoke_static_trampoline_with_access_check
+UNIMPLEMENTED art_quick_invoke_direct_trampoline_with_access_check
+UNIMPLEMENTED art_quick_invoke_super_trampoline_with_access_check
+UNIMPLEMENTED art_quick_invoke_virtual_trampoline_with_access_check
+
+ # On entry:
+ # t0 = shorty
+ # t1 = ptr to arg_array
+ # t2 = number of argument bytes remain
+ # v0 = ptr to stack frame where to copy arg_array
+ # This macro modifies t3, t9 and v0
+.macro LOOP_OVER_SHORTY_LOADING_REG gpu, fpu, label
+ lbu $t3, 0($t0) # get argument type from shorty
+ beqz $t3, \label
+ daddiu $t0, 1
+ li $t9, 68 # put char 'D' into t9
+ beq $t9, $t3, 1f # branch if result type char == 'D'
+ li $t9, 70 # put char 'F' into t9
+ beq $t9, $t3, 2f # branch if result type char == 'F'
+ li $t9, 74 # put char 'J' into t9
+ beq $t9, $t3, 3f # branch if result type char == 'J'
+ nop
+ lwu $\gpu, 0($t1)
+ sw $\gpu, 0($v0)
+ daddiu $v0, 4
+ daddiu $t1, 4
+ b 4f
+ daddiu $t2, -4 # delay slot
+
+1: # found double
+ lwu $t3, 0($t1)
+ mtc1 $t3, $\fpu
+ sw $t3, 0($v0)
+ lwu $t3, 4($t1)
+ mthc1 $t3, $\fpu
+ sw $t3, 4($v0)
+ daddiu $v0, 8
+ daddiu $t1, 8
+ b 4f
+ daddiu $t2, -8 # delay slot
+
+2: # found float
+ lwu $t3, 0($t1)
+ mtc1 $t3, $\fpu
+ sw $t3, 0($v0)
+ daddiu $v0, 4
+ daddiu $t1, 4
+ b 4f
+ daddiu $t2, -4 # delay slot
+
+3: # found long (8 bytes)
+ lwu $t3, 0($t1)
+ sw $t3, 0($v0)
+ lwu $t9, 4($t1)
+ sw $t9, 4($v0)
+ dsll $t9, $t9, 32
+ or $\gpu, $t9, $t3
+ daddiu $v0, 8
+ daddiu $t1, 8
+ daddiu $t2, -8
+4:
+.endm
+
+ /*
+ * Invocation stub for quick code.
+ * On entry:
+ * a0 = method pointer
+ * a1 = argument array that must at least contain the this ptr.
+ * a2 = size of argument array in bytes
+ * a3 = (managed) thread pointer
+ * a4 = JValue* result
+ * a5 = shorty
+ */
+ENTRY art_quick_invoke_stub
+ # push a4, a5, s0(rSUSPEND), s1(rSELF), s8, ra onto the stack
+ daddiu $sp, $sp, -48
+ .cfi_adjust_cfa_offset 48
+ sd $ra, 40($sp)
+ .cfi_rel_offset 31, 40
+ sd $s8, 32($sp)
+ .cfi_rel_offset 30, 32
+ sd $s1, 24($sp)
+ .cfi_rel_offset 17, 24
+ sd $s0, 16($sp)
+ .cfi_rel_offset 16, 16
+ sd $a5, 8($sp)
+ .cfi_rel_offset 9, 8
+ sd $a4, 0($sp)
+ .cfi_rel_offset 8, 0
+
+ daddiu $s0, $zero, SUSPEND_CHECK_INTERVAL # reset rSUSPEND to SUSPEND_CHECK_INTERVAL
+ move $s1, $a3 # move managed thread pointer into s1 (rSELF)
+ move $s8, $sp # save sp in s8 (fp)
+
+ daddiu $t3, $a2, 20 # add 4 for method* and 16 for stack alignment
+ dsrl $t3, $t3, 4 # shift the frame size right 4
+ dsll $t3, $t3, 4 # shift the frame size left 4 to align to 16 bytes
+ dsubu $sp, $sp, $t3 # reserve stack space for argument array
+
+ daddiu $t0, $a5, 1 # t0 = shorty[1] (skip 1 for return type)
+ daddiu $t1, $a1, 4 # t1 = ptr to arg_array[4] (skip this ptr)
+ daddiu $t2, $a2, -4 # t2 = number of argument bytes remain (skip this ptr)
+ daddiu $v0, $sp, 8 # v0 points to where to copy arg_array
+ LOOP_OVER_SHORTY_LOADING_REG a2, f14, call_fn
+ LOOP_OVER_SHORTY_LOADING_REG a3, f15, call_fn
+ LOOP_OVER_SHORTY_LOADING_REG a4, f16, call_fn
+ LOOP_OVER_SHORTY_LOADING_REG a5, f17, call_fn
+ LOOP_OVER_SHORTY_LOADING_REG a6, f18, call_fn
+ LOOP_OVER_SHORTY_LOADING_REG a7, f19, call_fn
+
+ # copy arguments onto stack (t2 should be multiples of 4)
+ ble $t2, $zero, call_fn # t2 = number of argument bytes remain
+1:
+ lw $t3, 0($t1) # load from argument array
+ daddiu $t1, $t1, 4
+ sw $t3, 0($v0) # save to stack
+ daddiu $t2, -4
+ bgt $t2, $zero, 1b # t2 = number of argument bytes remain
+ daddiu $v0, $v0, 4
+
+call_fn:
+ # call method (a0 and a1 have been untouched)
+ lwu $a1, 0($a1) # make a1 = this ptr
+ sw $a1, 4($sp) # copy this ptr (skip 4 bytes for method*)
+ sw $zero, 0($sp) # store NULL for method* at bottom of frame
+ ld $t9, MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64($a0) # get pointer to the code
+ jalr $t9 # call the method
+ nop
+ move $sp, $s8 # restore sp
+
+ # pop a4, a5, s1(rSELF), s8, ra off of the stack
+ ld $a4, 0($sp)
+ .cfi_restore 8
+ ld $a5, 8($sp)
+ .cfi_restore 9
+ ld $s0, 16($sp)
+ .cfi_restore 16
+ ld $s1, 24($sp)
+ .cfi_restore 17
+ ld $s8, 32($sp)
+ .cfi_restore 30
+ ld $ra, 40($sp)
+ .cfi_restore 31
+ daddiu $sp, $sp, 48
+ .cfi_adjust_cfa_offset -48
+
+ # a4 = JValue* result
+ # a5 = shorty string
+ lbu $t1, 0($a5) # get result type from shorty
+ li $t2, 68 # put char 'D' into t2
+ beq $t1, $t2, 1f # branch if result type char == 'D'
+ li $t3, 70 # put char 'F' into t3
+ beq $t1, $t3, 1f # branch if result type char == 'F'
+ sw $v0, 0($a4) # store the result
+ dsrl $v1, $v0, 32
+ jalr $zero, $ra
+ sw $v1, 4($a4) # store the other half of the result
+1:
+ mfc1 $v0, $f0
+ mfhc1 $v1, $f0
+ sw $v0, 0($a4) # store the result
+ jalr $zero, $ra
+ sw $v1, 4($a4) # store the other half of the result
+END art_quick_invoke_stub
+
+ /*
+ * Invocation static stub for quick code.
+ * On entry:
+ * a0 = method pointer
+ * a1 = argument array that must at least contain the this ptr.
+ * a2 = size of argument array in bytes
+ * a3 = (managed) thread pointer
+ * a4 = JValue* result
+ * a5 = shorty
+ */
+ENTRY art_quick_invoke_static_stub
+
+ # push a4, a5, s0(rSUSPEND), s1(rSELF), s8, ra, onto the stack
+ daddiu $sp, $sp, -48
+ .cfi_adjust_cfa_offset 48
+ sd $ra, 40($sp)
+ .cfi_rel_offset 31, 40
+ sd $s8, 32($sp)
+ .cfi_rel_offset 30, 32
+ sd $s1, 24($sp)
+ .cfi_rel_offset 17, 24
+ sd $s0, 16($sp)
+ .cfi_rel_offset 16, 16
+ sd $a5, 8($sp)
+ .cfi_rel_offset 9, 8
+ sd $a4, 0($sp)
+ .cfi_rel_offset 8, 0
+
+ daddiu $s0, $zero, SUSPEND_CHECK_INTERVAL # reset rSUSPEND to SUSPEND_CHECK_INTERVAL
+ move $s1, $a3 # move managed thread pointer into s1 (rSELF)
+ move $s8, $sp # save sp in s8 (fp)
+
+ daddiu $t3, $a2, 20 # add 4 for method* and 16 for stack alignment
+ dsrl $t3, $t3, 4 # shift the frame size right 4
+ dsll $t3, $t3, 4 # shift the frame size left 4 to align to 16 bytes
+ dsubu $sp, $sp, $t3 # reserve stack space for argument array
+
+ daddiu $t0, $a5, 1 # t0 = shorty[1] (skip 1 for return type)
+ move $t1, $a1 # t1 = arg_array
+ move $t2, $a2 # t2 = number of argument bytes remain
+ daddiu $v0, $sp, 4 # v0 points to where to copy arg_array
+ LOOP_OVER_SHORTY_LOADING_REG a1, f13, call_sfn
+ LOOP_OVER_SHORTY_LOADING_REG a2, f14, call_sfn
+ LOOP_OVER_SHORTY_LOADING_REG a3, f15, call_sfn
+ LOOP_OVER_SHORTY_LOADING_REG a4, f16, call_sfn
+ LOOP_OVER_SHORTY_LOADING_REG a5, f17, call_sfn
+ LOOP_OVER_SHORTY_LOADING_REG a6, f18, call_sfn
+ LOOP_OVER_SHORTY_LOADING_REG a7, f19, call_sfn
+
+ # copy arguments onto stack (t2 should be multiples of 4)
+ ble $t2, $zero, call_sfn # t2 = number of argument bytes remain
+1:
+ lw $t3, 0($t1) # load from argument array
+ daddiu $t1, $t1, 4
+ sw $t3, 0($v0) # save to stack
+ daddiu $t2, -4
+ bgt $t2, $zero, 1b # t2 = number of argument bytes remain
+ daddiu $v0, $v0, 4
+
+call_sfn:
+ # call method (a0 has been untouched)
+ sw $zero, 0($sp) # store NULL for method* at bottom of frame
+ ld $t9, MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64($a0) # get pointer to the code
+ jalr $t9 # call the method
+ nop
+ move $sp, $s8 # restore sp
+
+ # pop a4, a5, s0(rSUSPEND), s1(rSELF), s8, ra off of the stack
+ ld $a4, 0($sp)
+ .cfi_restore 8
+ ld $a5, 8($sp)
+ .cfi_restore 9
+ ld $s0, 16($sp)
+ .cfi_restore 16
+ ld $s1, 24($sp)
+ .cfi_restore 17
+ ld $s8, 32($sp)
+ .cfi_restore 30
+ ld $ra, 40($sp)
+ .cfi_restore 31
+ daddiu $sp, $sp, 48
+ .cfi_adjust_cfa_offset -48
+
+ # a4 = JValue* result
+ # a5 = shorty string
+ lbu $t1, 0($a5) # get result type from shorty
+ li $t2, 68 # put char 'D' into t2
+ beq $t1, $t2, 1f # branch if result type char == 'D'
+ li $t3, 70 # put char 'F' into t3
+ beq $t1, $t3, 1f # branch if result type char == 'F'
+ sw $v0, 0($a4) # store the result
+ dsrl $v1, $v0, 32
+ jalr $zero, $ra
+ sw $v1, 4($a4) # store the other half of the result
+1:
+ mfc1 $v0, $f0
+ mfhc1 $v1, $f0
+ sw $v0, 0($a4) # store the result
+ jalr $zero, $ra
+ sw $v1, 4($a4) # store the other half of the result
+END art_quick_invoke_static_stub
+
+
+
+UNIMPLEMENTED art_quick_handle_fill_data
+UNIMPLEMENTED art_quick_lock_object
+UNIMPLEMENTED art_quick_unlock_object
+UNIMPLEMENTED art_quick_check_cast
+UNIMPLEMENTED art_quick_aput_obj_with_null_and_bound_check
+UNIMPLEMENTED art_quick_aput_obj_with_bound_check
+UNIMPLEMENTED art_quick_aput_obj
+UNIMPLEMENTED art_quick_initialize_static_storage
+UNIMPLEMENTED art_quick_initialize_type
+UNIMPLEMENTED art_quick_initialize_type_and_verify_access
+UNIMPLEMENTED art_quick_get_boolean_static
+UNIMPLEMENTED art_quick_get_byte_static
+UNIMPLEMENTED art_quick_get_char_static
+UNIMPLEMENTED art_quick_get_short_static
+UNIMPLEMENTED art_quick_get32_static
+UNIMPLEMENTED art_quick_get64_static
+UNIMPLEMENTED art_quick_get_obj_static
+UNIMPLEMENTED art_quick_get_boolean_instance
+UNIMPLEMENTED art_quick_get_byte_instance
+UNIMPLEMENTED art_quick_get_char_instance
+UNIMPLEMENTED art_quick_get_short_instance
+UNIMPLEMENTED art_quick_get32_instance
+UNIMPLEMENTED art_quick_get64_instance
+UNIMPLEMENTED art_quick_get_obj_instance
+UNIMPLEMENTED art_quick_set8_static
+UNIMPLEMENTED art_quick_set16_static
+UNIMPLEMENTED art_quick_set32_static
+UNIMPLEMENTED art_quick_set64_static
+UNIMPLEMENTED art_quick_set_obj_static
+UNIMPLEMENTED art_quick_set8_instance
+UNIMPLEMENTED art_quick_set16_instance
+UNIMPLEMENTED art_quick_set32_instance
+UNIMPLEMENTED art_quick_set64_instance
+UNIMPLEMENTED art_quick_set_obj_instance
+UNIMPLEMENTED art_quick_resolve_string
+
+// Macro to facilitate adding new allocation entrypoints.
+.macro TWO_ARG_DOWNCALL name, entrypoint, return
+ENTRY \name
+ break
+ break
+END \name
+.endm
+
+.macro THREE_ARG_DOWNCALL name, entrypoint, return
+ENTRY \name
+ break
+ break
+END \name
+.endm
+
+// Generate the allocation entrypoints for each allocator.
+GENERATE_ALL_ALLOC_ENTRYPOINTS
+
+UNIMPLEMENTED art_quick_test_suspend
+
+ /*
+ * Called by managed code that is attempting to call a method on a proxy class. On entry
+ * r0 holds the proxy method; r1, r2 and r3 may contain arguments.
+ */
+ .extern artQuickProxyInvokeHandler
+ENTRY art_quick_proxy_invoke_handler
+ SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+ sd $a0, 0($sp) # place proxy method at bottom of frame
+ move $a2, rSELF # pass Thread::Current
+ jal artQuickProxyInvokeHandler # (Method* proxy method, receiver, Thread*, SP)
+ move $a3, $sp # pass $sp
+ ld $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
+ daddiu $sp, $sp, REFS_AND_ARGS_MINUS_REFS_SIZE # skip a0-a7 and f12-f19
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+ bne $t0, $zero, 1f
+ dmtc1 $v0, $f0 # place return value to FP return value
+ jalr $zero, $ra
+ dmtc1 $v1, $f1 # place return value to FP return value
+1:
+ DELIVER_PENDING_EXCEPTION
+END art_quick_proxy_invoke_handler
+
+UNIMPLEMENTED art_quick_imt_conflict_trampoline
+
+ .extern artQuickResolutionTrampoline
+ENTRY art_quick_resolution_trampoline
+ SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+ move $a2, rSELF # pass Thread::Current
+ jal artQuickResolutionTrampoline # (Method* called, receiver, Thread*, SP)
+ move $a3, $sp # pass $sp
+ beq $v0, $zero, 1f
+ lwu $a0, 0($sp) # load resolved method in $a0
+ # artQuickResolutionTrampoline puts resolved method in *SP
+ RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+ move $t9, $v0 # code pointer must be in $t9 to generate the global pointer
+ jalr $zero, $t9 # tail call to method
+ nop
+1:
+ RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+ DELIVER_PENDING_EXCEPTION
+END art_quick_resolution_trampoline
+
+ .extern artQuickGenericJniTrampoline
+ .extern artQuickGenericJniEndTrampoline
+ENTRY art_quick_generic_jni_trampoline
+ SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL
+ sd $a0, 0($sp) # store native ArtMethod* to bottom of stack
+ move $s8, $sp # save $sp
+
+ # prepare for call to artQuickGenericJniTrampoline(Thread*, SP)
+ move $a0, rSELF # pass Thread::Current
+ move $a1, $sp # pass $sp
+ jal artQuickGenericJniTrampoline # (Thread*, SP)
+ daddiu $sp, $sp, -5120 # reserve space on the stack
+
+ # The C call will have registered the complete save-frame on success.
+ # The result of the call is:
+ # v0: ptr to native code, 0 on error.
+ # v1: ptr to the bottom of the used area of the alloca, can restore stack till here.
+ beq $v0, $zero, 1f # check entry error
+ move $t9, $v0 # save the code ptr
+ move $sp, $v1 # release part of the alloca
+
+ # Load parameters from stack into registers
+ ld $a0, 0($sp)
+ ld $a1, 8($sp)
+ ld $a2, 16($sp)
+ ld $a3, 24($sp)
+ ld $a4, 32($sp)
+ ld $a5, 40($sp)
+ ld $a6, 48($sp)
+ ld $a7, 56($sp)
+ # Load FPRs the same as GPRs. Look at BuildNativeCallFrameStateMachine.
+ l.d $f12, 0($sp)
+ l.d $f13, 8($sp)
+ l.d $f14, 16($sp)
+ l.d $f15, 24($sp)
+ l.d $f16, 32($sp)
+ l.d $f17, 40($sp)
+ l.d $f18, 48($sp)
+ l.d $f19, 56($sp)
+ jalr $t9 # native call
+ daddiu $sp, $sp, 64
+
+ # result sign extension is handled in C code
+ # prepare for call to artQuickGenericJniEndTrampoline(Thread*, result, result_f)
+ move $a0, rSELF # pass Thread::Current
+ move $a1, $v0
+ jal artQuickGenericJniEndTrampoline
+ dmfc1 $a2, $f0
+
+ ld $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
+ bne $t0, $zero, 2f # check for pending exceptions
+ move $sp, $s8 # tear down the alloca
+
+ # tear dpown the callee-save frame
+ RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+
+ jalr $zero, $ra
+ dmtc1 $v0, $f0 # place return value to FP return value
+
+1:
+ move $sp, $s8 # tear down the alloca
+2:
+ RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+ DELIVER_PENDING_EXCEPTION
+END art_quick_generic_jni_trampoline
+
+ .extern artQuickToInterpreterBridge
+ENTRY art_quick_to_interpreter_bridge
+ SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+ move $a1, rSELF # pass Thread::Current
+ jal artQuickToInterpreterBridge # (Method* method, Thread*, SP)
+ move $a2, $sp # pass $sp
+ ld $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
+ daddiu $sp, $sp, REFS_AND_ARGS_MINUS_REFS_SIZE # skip a0-a7 and f12-f19
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+ bne $t0, $zero, 1f
+ dmtc1 $v0, $f0 # place return value to FP return value
+ jalr $zero, $ra
+ dmtc1 $v1, $f1 # place return value to FP return value
+1:
+ DELIVER_PENDING_EXCEPTION
+END art_quick_to_interpreter_bridge
+
+ /*
+ * Routine that intercepts method calls and returns.
+ */
+ .extern artInstrumentationMethodEntryFromCode
+ .extern artInstrumentationMethodExitFromCode
+ENTRY art_quick_instrumentation_entry
+ SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+ daddiu $sp, $sp, -16 # space for saving arg0
+ .cfi_adjust_cfa_offset 16
+ sd $a0, 0($sp) # save arg0
+ move $a3, $ra # pass $ra
+ jal artInstrumentationMethodEntryFromCode # (Method*, Object*, Thread*, RA)
+ move $a2, rSELF # pass Thread::Current
+ move $t9, $v0 # $t9 holds reference to code
+ ld $a0, 0($sp) # restore arg0
+ daddiu $sp, $sp, 16 # remove args
+ .cfi_adjust_cfa_offset -16
+ RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+ jalr $t9 # call method
+ nop
+END art_quick_instrumentation_entry
+ /* intentional fallthrough */
+ .global art_quick_instrumentation_exit
+art_quick_instrumentation_exit:
+ .cfi_startproc
+ daddiu $t9, $ra, 4 # put current address into $t9 to rebuild $gp
+ .cpload $t9
+ move $ra, $zero # link register is to here, so clobber with 0 for later checks
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
+ move $t0, $sp # remember bottom of caller's frame
+ daddiu $sp, $sp, -16 # save return values and set up args
+ .cfi_adjust_cfa_offset 16
+ sd $v0, 0($sp)
+ .cfi_rel_offset 2, 0
+ s.d $f0, 8($sp)
+ mov.d $f15, $f0 # pass fpr result
+ move $a2, $v0 # pass gpr result
+ move $a1, $t0 # pass $sp
+ jal artInstrumentationMethodExitFromCode # (Thread*, SP, gpr_res, fpr_res)
+ move $a0, rSELF # pass Thread::Current
+ move $t0, $v0 # set aside returned link register
+ move $ra, $v1 # set link register for deoptimization
+ ld $v0, 0($sp) # restore return values
+ l.d $f0, 8($sp)
+ jalr $zero, $t0 # return
+ daddiu $sp, $sp, 16+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE # 16 bytes of saved values + ref_only callee save frame
+ .cfi_adjust_cfa_offset -(16+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE)
+END art_quick_instrumentation_exit
+
+UNIMPLEMENTED art_quick_deoptimize
+UNIMPLEMENTED art_quick_indexof
+UNIMPLEMENTED art_quick_string_compareto
diff --git a/runtime/arch/mips64/quick_method_frame_info_mips64.h b/runtime/arch/mips64/quick_method_frame_info_mips64.h
new file mode 100644
index 0000000..de55e81
--- /dev/null
+++ b/runtime/arch/mips64/quick_method_frame_info_mips64.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_ARCH_MIPS64_QUICK_METHOD_FRAME_INFO_MIPS64_H_
+#define ART_RUNTIME_ARCH_MIPS64_QUICK_METHOD_FRAME_INFO_MIPS64_H_
+
+#include "quick/quick_method_frame_info.h"
+#include "registers_mips64.h"
+#include "runtime.h" // for Runtime::CalleeSaveType.
+
+namespace art {
+namespace mips64 {
+
+static constexpr uint32_t kMips64CalleeSaveRefSpills =
+ (1 << art::mips64::S2) | (1 << art::mips64::S3) | (1 << art::mips64::S4) |
+ (1 << art::mips64::S5) | (1 << art::mips64::S6) | (1 << art::mips64::S7) |
+ (1 << art::mips64::GP) | (1 << art::mips64::S8);
+static constexpr uint32_t kMips64CalleeSaveArgSpills =
+ (1 << art::mips64::A1) | (1 << art::mips64::A2) | (1 << art::mips64::A3) |
+ (1 << art::mips64::A4) | (1 << art::mips64::A5) | (1 << art::mips64::A6) |
+ (1 << art::mips64::A7);
+static constexpr uint32_t kMips64CalleeSaveAllSpills =
+ (1 << art::mips64::S0) | (1 << art::mips64::S1);
+
+static constexpr uint32_t kMips64CalleeSaveFpRefSpills = 0;
+static constexpr uint32_t kMips64CalleeSaveFpArgSpills =
+ (1 << art::mips64::F12) | (1 << art::mips64::F13) | (1 << art::mips64::F14) |
+ (1 << art::mips64::F15) | (1 << art::mips64::F16) | (1 << art::mips64::F17) |
+ (1 << art::mips64::F18) | (1 << art::mips64::F19);
+// F12 should not be necessary to spill, as A0 is always in use.
+static constexpr uint32_t kMips64CalleeSaveFpAllSpills =
+ (1 << art::mips64::F24) | (1 << art::mips64::F25) | (1 << art::mips64::F26) |
+ (1 << art::mips64::F27) | (1 << art::mips64::F28) | (1 << art::mips64::F29) |
+ (1 << art::mips64::F30) | (1 << art::mips64::F31);
+
+constexpr uint32_t Mips64CalleeSaveCoreSpills(Runtime::CalleeSaveType type) {
+ return kMips64CalleeSaveRefSpills |
+ (type == Runtime::kRefsAndArgs ? kMips64CalleeSaveArgSpills : 0) |
+ (type == Runtime::kSaveAll ? kMips64CalleeSaveAllSpills : 0) | (1 << art::mips64::RA);
+}
+
+constexpr uint32_t Mips64CalleeSaveFpSpills(Runtime::CalleeSaveType type) {
+ return kMips64CalleeSaveFpRefSpills |
+ (type == Runtime::kRefsAndArgs ? kMips64CalleeSaveFpArgSpills: 0) |
+ (type == Runtime::kSaveAll ? kMips64CalleeSaveFpAllSpills : 0);
+}
+
+constexpr uint32_t Mips64CalleeSaveFrameSize(Runtime::CalleeSaveType type) {
+ return RoundUp((POPCOUNT(Mips64CalleeSaveCoreSpills(type)) /* gprs */ +
+ POPCOUNT(Mips64CalleeSaveFpSpills(type)) /* fprs */ +
+ + 1 /* Method* */) * kMips64PointerSize, kStackAlignment);
+}
+
+constexpr QuickMethodFrameInfo Mips64CalleeSaveMethodFrameInfo(Runtime::CalleeSaveType type) {
+ return QuickMethodFrameInfo(Mips64CalleeSaveFrameSize(type),
+ Mips64CalleeSaveCoreSpills(type),
+ Mips64CalleeSaveFpSpills(type));
+}
+
+} // namespace mips64
+} // namespace art
+
+#endif // ART_RUNTIME_ARCH_MIPS64_QUICK_METHOD_FRAME_INFO_MIPS64_H_
diff --git a/runtime/arch/mips64/registers_mips64.cc b/runtime/arch/mips64/registers_mips64.cc
new file mode 100644
index 0000000..4959208
--- /dev/null
+++ b/runtime/arch/mips64/registers_mips64.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "registers_mips64.h"
+
+#include <ostream>
+
+namespace art {
+namespace mips64 {
+
+static const char* kRegisterNames[] = {
+ "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra",
+};
+
+std::ostream& operator<<(std::ostream& os, const GpuRegister& rhs) {
+ if (rhs >= ZERO && rhs < kNumberOfGpuRegisters) {
+ os << kRegisterNames[rhs];
+ } else {
+ os << "GpuRegister[" << static_cast<int>(rhs) << "]";
+ }
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const FpuRegister& rhs) {
+ if (rhs >= F0 && rhs < kNumberOfFpuRegisters) {
+ os << "f" << static_cast<int>(rhs);
+ } else {
+ os << "FpuRegister[" << static_cast<int>(rhs) << "]";
+ }
+ return os;
+}
+
+} // namespace mips64
+} // namespace art
diff --git a/runtime/arch/mips64/registers_mips64.h b/runtime/arch/mips64/registers_mips64.h
new file mode 100644
index 0000000..38bc8f2
--- /dev/null
+++ b/runtime/arch/mips64/registers_mips64.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_ARCH_MIPS64_REGISTERS_MIPS64_H_
+#define ART_RUNTIME_ARCH_MIPS64_REGISTERS_MIPS64_H_
+
+#include <iosfwd>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "globals.h"
+
+namespace art {
+namespace mips64 {
+
+enum GpuRegister {
+ ZERO = 0,
+ AT = 1, // Assembler temporary.
+ V0 = 2, // Values.
+ V1 = 3,
+ A0 = 4, // Arguments.
+ A1 = 5,
+ A2 = 6,
+ A3 = 7,
+ A4 = 8,
+ A5 = 9,
+ A6 = 10,
+ A7 = 11,
+ T0 = 12, // Temporaries.
+ T1 = 13,
+ T2 = 14,
+ T3 = 15,
+ S0 = 16, // Saved values.
+ S1 = 17,
+ S2 = 18,
+ S3 = 19,
+ S4 = 20,
+ S5 = 21,
+ S6 = 22,
+ S7 = 23,
+ T8 = 24, // More temporaries.
+ T9 = 25,
+ K0 = 26, // Reserved for trap handler.
+ K1 = 27,
+ GP = 28, // Global pointer.
+ SP = 29, // Stack pointer.
+ S8 = 30, // Saved value/frame pointer.
+ RA = 31, // Return address.
+ kNumberOfGpuRegisters = 32,
+ kNoGpuRegister = -1 // Signals an illegal register.
+};
+std::ostream& operator<<(std::ostream& os, const GpuRegister& rhs);
+
+// Values for floating point registers.
+enum FpuRegister {
+ F0 = 0,
+ F1 = 1,
+ F2 = 2,
+ F3 = 3,
+ F4 = 4,
+ F5 = 5,
+ F6 = 6,
+ F7 = 7,
+ F8 = 8,
+ F9 = 9,
+ F10 = 10,
+ F11 = 11,
+ F12 = 12,
+ F13 = 13,
+ F14 = 14,
+ F15 = 15,
+ F16 = 16,
+ F17 = 17,
+ F18 = 18,
+ F19 = 19,
+ F20 = 20,
+ F21 = 21,
+ F22 = 22,
+ F23 = 23,
+ F24 = 24,
+ F25 = 25,
+ F26 = 26,
+ F27 = 27,
+ F28 = 28,
+ F29 = 29,
+ F30 = 30,
+ F31 = 31,
+ kNumberOfFpuRegisters = 32,
+ kNoFpuRegister = -1,
+};
+std::ostream& operator<<(std::ostream& os, const FpuRegister& rhs);
+
+} // namespace mips64
+} // namespace art
+
+#endif // ART_RUNTIME_ARCH_MIPS64_REGISTERS_MIPS64_H_
diff --git a/runtime/arch/arm64/portable_entrypoints_arm64.S b/runtime/arch/mips64/thread_mips64.cc
similarity index 60%
copy from runtime/arch/arm64/portable_entrypoints_arm64.S
copy to runtime/arch/mips64/thread_mips64.cc
index 9e2c030..c55537c 100644
--- a/runtime/arch/arm64/portable_entrypoints_arm64.S
+++ b/runtime/arch/mips64/thread_mips64.cc
@@ -14,17 +14,21 @@
* limitations under the License.
*/
-#include "asm_support_arm64.S"
+#include "thread.h"
- /*
- * Portable invocation stub.
- */
-UNIMPLEMENTED art_portable_invoke_stub
+#include "asm_support_mips64.h"
+#include "base/logging.h"
-UNIMPLEMENTED art_portable_proxy_invoke_handler
+namespace art {
-UNIMPLEMENTED art_portable_resolution_trampoline
+void Thread::InitCpu() {
+ CHECK_EQ(THREAD_FLAGS_OFFSET, ThreadFlagsOffset<8>().Int32Value());
+ CHECK_EQ(THREAD_CARD_TABLE_OFFSET, CardTableOffset<8>().Int32Value());
+ CHECK_EQ(THREAD_EXCEPTION_OFFSET, ExceptionOffset<8>().Int32Value());
+}
-UNIMPLEMENTED art_portable_to_interpreter_bridge
+void Thread::CleanupCpu() {
+ // Do nothing.
+}
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
+} // namespace art
diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S
index 632c5f3..037c26e 100644
--- a/runtime/arch/quick_alloc_entrypoints.S
+++ b/runtime/arch/quick_alloc_entrypoints.S
@@ -16,25 +16,25 @@
.macro GENERATE_ALLOC_ENTRYPOINTS c_suffix, cxx_suffix
// Called by managed code to allocate an object.
-TWO_ARG_DOWNCALL art_quick_alloc_object\c_suffix, artAllocObjectFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+TWO_ARG_DOWNCALL art_quick_alloc_object\c_suffix, artAllocObjectFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Called by managed code to allocate an object of a resolved class.
-TWO_ARG_DOWNCALL art_quick_alloc_object_resolved\c_suffix, artAllocObjectFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+TWO_ARG_DOWNCALL art_quick_alloc_object_resolved\c_suffix, artAllocObjectFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Called by managed code to allocate an object of an initialized class.
-TWO_ARG_DOWNCALL art_quick_alloc_object_initialized\c_suffix, artAllocObjectFromCodeInitialized\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+TWO_ARG_DOWNCALL art_quick_alloc_object_initialized\c_suffix, artAllocObjectFromCodeInitialized\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Called by managed code to allocate an object when the caller doesn't know whether it has access
// to the created type.
-TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check\c_suffix, artAllocObjectFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check\c_suffix, artAllocObjectFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Called by managed code to allocate an array.
-THREE_ARG_DOWNCALL art_quick_alloc_array\c_suffix, artAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+THREE_ARG_DOWNCALL art_quick_alloc_array\c_suffix, artAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Called by managed code to allocate an array of a resolve class.
-THREE_ARG_DOWNCALL art_quick_alloc_array_resolved\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+THREE_ARG_DOWNCALL art_quick_alloc_array_resolved\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Called by managed code to allocate an array when the caller doesn't know whether it has access
// to the created type.
-THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check\c_suffix, artAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check\c_suffix, artAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
-THREE_ARG_DOWNCALL art_quick_check_and_alloc_array\c_suffix, artCheckAndAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+THREE_ARG_DOWNCALL art_quick_check_and_alloc_array\c_suffix, artCheckAndAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
-THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check\c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check\c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
.endm
.macro GENERATE_ALL_ALLOC_ENTRYPOINTS
@@ -46,4 +46,8 @@
GENERATE_ALLOC_ENTRYPOINTS _bump_pointer_instrumented, BumpPointerInstrumented
GENERATE_ALLOC_ENTRYPOINTS _tlab, TLAB
GENERATE_ALLOC_ENTRYPOINTS _tlab_instrumented, TLABInstrumented
+GENERATE_ALLOC_ENTRYPOINTS _region, Region
+GENERATE_ALLOC_ENTRYPOINTS _region_instrumented, RegionInstrumented
+GENERATE_ALLOC_ENTRYPOINTS _region_tlab, RegionTLAB
+GENERATE_ALLOC_ENTRYPOINTS _region_tlab_instrumented, RegionTLABInstrumented
.endm
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 0fcd297..6acc2a7 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -272,9 +272,9 @@
".cfi_adjust_cfa_offset -16\n\t"
: "=a" (result)
// Use the result from rax
- : "D"(arg0), "S"(arg1), "d"(arg2), "a"(code), [referrer] "m"(referrer)
+ : "D"(arg0), "S"(arg1), "d"(arg2), "a"(code), [referrer] "c"(referrer)
// This places arg0 into rdi, arg1 into rsi, arg2 into rdx, and code into rax
- : "rbx", "rcx", "rbp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ : "rbx", "rbp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"memory"); // clobber all
// TODO: Should we clobber the other registers?
#else
@@ -302,7 +302,7 @@
#if defined(__i386__)
// TODO: Set the thread?
__asm__ __volatile__(
- "movd %[hidden], %%xmm0\n\t"
+ "movd %[hidden], %%xmm7\n\t"
"subl $12, %%esp\n\t" // Align stack.
"pushl %[referrer]\n\t" // Store referrer
"call *%%edi\n\t" // Call the stub
@@ -799,6 +799,9 @@
}
TEST_F(StubTest, UnlockObject) {
+ // This will lead to monitor error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
TestUnlockObject(this);
}
@@ -992,6 +995,9 @@
TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+ // This will lead to OOM error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
// TODO: Check the "Unresolved" allocation stubs
Thread* self = Thread::Current();
@@ -1116,6 +1122,9 @@
#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
// TODO: Check the "Unresolved" allocation stubs
+ // This will lead to OOM error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
Thread* self = Thread::Current();
// Create an object
ScopedObjectAccess soa(self);
@@ -1139,8 +1148,8 @@
if ((false)) {
// Use an arbitrary method from c to use as referrer
size_t result = Invoke3(static_cast<size_t>(c->GetDexTypeIndex()), // type_idx
- reinterpret_cast<size_t>(c_obj->GetVirtualMethod(0)), // arbitrary
10U,
+ reinterpret_cast<size_t>(c_obj->GetVirtualMethod(0)), // arbitrary
StubTest::GetEntrypoint(self, kQuickAllocArray),
self);
@@ -1155,7 +1164,8 @@
{
// We can use nullptr in the second argument as we do not need a method here (not used in
// resolved/initialized cases)
- size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(nullptr), 10U,
+ size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), 10U,
+ reinterpret_cast<size_t>(nullptr),
StubTest::GetEntrypoint(self, kQuickAllocArrayResolved),
self);
EXPECT_FALSE(self->IsExceptionPending()) << PrettyTypeOf(self->GetException(nullptr));
@@ -1173,8 +1183,9 @@
// Out-of-memory.
{
- size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(nullptr),
+ size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()),
GB, // that should fail...
+ reinterpret_cast<size_t>(nullptr),
StubTest::GetEntrypoint(self, kQuickAllocArrayResolved),
self);
diff --git a/runtime/arch/x86/asm_support_x86.S b/runtime/arch/x86/asm_support_x86.S
index fea16da..122428b 100644
--- a/runtime/arch/x86/asm_support_x86.S
+++ b/runtime/arch/x86/asm_support_x86.S
@@ -25,6 +25,8 @@
#define MACRO1(macro_name, macro_arg1) .macro macro_name
#define MACRO2(macro_name, macro_arg1, macro_args2) .macro macro_name
#define MACRO3(macro_name, macro_arg1, macro_args2, macro_args3) .macro macro_name
+ #define MACRO4(macro_name, macro_arg1, macro_arg2, macro_arg3, macro_arg4) .macro macro_name
+ #define MACRO5(macro_name, macro_arg1, macro_arg2, macro_arg3, macro_arg4, macro_arg5) .macro macro_name
#define END_MACRO .endmacro
// Clang's as(1) uses $0, $1, and so on for macro arguments.
@@ -43,6 +45,8 @@
#define MACRO1(macro_name, macro_arg1) .macro macro_name macro_arg1
#define MACRO2(macro_name, macro_arg1, macro_arg2) .macro macro_name macro_arg1, macro_arg2
#define MACRO3(macro_name, macro_arg1, macro_arg2, macro_arg3) .macro macro_name macro_arg1, macro_arg2, macro_arg3
+ #define MACRO4(macro_name, macro_arg1, macro_arg2, macro_arg3, macro_arg4) .macro macro_name macro_arg1, macro_arg2, macro_arg3, macro_arg4
+ #define MACRO5(macro_name, macro_arg1, macro_arg2, macro_arg3, macro_arg4, macro_arg5) .macro macro_name macro_arg1, macro_arg2, macro_arg3, macro_arg4, macro_arg5
#define END_MACRO .endm
// Regular gas(1) uses \argument_name for macro arguments.
diff --git a/runtime/arch/x86/asm_support_x86.h b/runtime/arch/x86/asm_support_x86.h
index 5a88f80..b0a6017 100644
--- a/runtime/arch/x86/asm_support_x86.h
+++ b/runtime/arch/x86/asm_support_x86.h
@@ -21,6 +21,8 @@
#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 32
#define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 32
-#define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 32
+
+// 32 bytes for GPRs and 32 bytes for FPRs.
+#define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE (32 + 32)
#endif // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_
diff --git a/runtime/arch/x86/context_x86.cc b/runtime/arch/x86/context_x86.cc
index 49aa326..4ea4684 100644
--- a/runtime/arch/x86/context_x86.cc
+++ b/runtime/arch/x86/context_x86.cc
@@ -30,6 +30,9 @@
for (size_t i = 0; i < kNumberOfCpuRegisters; i++) {
gprs_[i] = nullptr;
}
+ for (size_t i = 0; i < kNumberOfFloatRegisters; ++i) {
+ fprs_[i] = nullptr;
+ }
gprs_[ESP] = &esp_;
// Initialize registers with easy to spot debug values.
esp_ = X86Context::kBadGprBase + ESP;
@@ -40,7 +43,7 @@
mirror::ArtMethod* method = fr.GetMethod();
const QuickMethodFrameInfo frame_info = method->GetQuickFrameInfo();
size_t spill_count = POPCOUNT(frame_info.CoreSpillMask());
- DCHECK_EQ(frame_info.FpSpillMask(), 0u);
+ size_t fp_spill_count = POPCOUNT(frame_info.FpSpillMask());
if (spill_count > 0) {
// Lowest number spill is farthest away, walk registers and fill into context.
int j = 2; // Offset j to skip return address spill.
@@ -51,6 +54,24 @@
}
}
}
+ if (fp_spill_count > 0) {
+ // Lowest number spill is farthest away, walk registers and fill into context.
+ size_t j = 2; // Offset j to skip return address spill.
+ size_t fp_spill_size_in_words = fp_spill_count * 2;
+ for (size_t i = 0; i < kNumberOfFloatRegisters; ++i) {
+ if (((frame_info.FpSpillMask() >> i) & 1) != 0) {
+ // There are 2 pieces to each XMM register, to match VR size.
+ fprs_[2*i] = reinterpret_cast<uint32_t*>(
+ fr.CalleeSaveAddress(spill_count + fp_spill_size_in_words - j,
+ frame_info.FrameSizeInBytes()));
+ fprs_[2*i+1] = reinterpret_cast<uint32_t*>(
+ fr.CalleeSaveAddress(spill_count + fp_spill_size_in_words - j - 1,
+ frame_info.FrameSizeInBytes()));
+ // Two void* per XMM register.
+ j += 2;
+ }
+ }
+ }
}
void X86Context::SmashCallerSaves() {
@@ -59,27 +80,21 @@
gprs_[EDX] = const_cast<uintptr_t*>(&gZero);
gprs_[ECX] = nullptr;
gprs_[EBX] = nullptr;
+ memset(&fprs_[0], '\0', sizeof(fprs_));
}
-bool X86Context::SetGPR(uint32_t reg, uintptr_t value) {
+void X86Context::SetGPR(uint32_t reg, uintptr_t value) {
CHECK_LT(reg, static_cast<uint32_t>(kNumberOfCpuRegisters));
+ DCHECK(IsAccessibleGPR(reg));
CHECK_NE(gprs_[reg], &gZero);
- if (gprs_[reg] != nullptr) {
- *gprs_[reg] = value;
- return true;
- } else {
- return false;
- }
+ *gprs_[reg] = value;
}
-bool X86Context::GetFPR(uint32_t reg ATTRIBUTE_UNUSED, uintptr_t* val ATTRIBUTE_UNUSED) {
- LOG(FATAL) << "Floating-point registers are all caller save in X86";
- UNREACHABLE();
-}
-
-bool X86Context::SetFPR(uint32_t reg ATTRIBUTE_UNUSED, uintptr_t value ATTRIBUTE_UNUSED) {
- LOG(FATAL) << "Floating-point registers are all caller save in X86";
- UNREACHABLE();
+void X86Context::SetFPR(uint32_t reg, uintptr_t value) {
+ CHECK_LT(reg, static_cast<uint32_t>(kNumberOfFloatRegisters));
+ DCHECK(IsAccessibleFPR(reg));
+ CHECK_NE(fprs_[reg], reinterpret_cast<const uint32_t*>(&gZero));
+ *fprs_[reg] = value;
}
void X86Context::DoLongJump() {
@@ -90,17 +105,30 @@
for (size_t i = 0; i < kNumberOfCpuRegisters; ++i) {
gprs[kNumberOfCpuRegisters - i - 1] = gprs_[i] != nullptr ? *gprs_[i] : X86Context::kBadGprBase + i;
}
+ uint32_t fprs[kNumberOfFloatRegisters];
+ for (size_t i = 0; i < kNumberOfFloatRegisters; ++i) {
+ fprs[i] = fprs_[i] != nullptr ? *fprs_[i] : X86Context::kBadFprBase + i;
+ }
// We want to load the stack pointer one slot below so that the ret will pop eip.
uintptr_t esp = gprs[kNumberOfCpuRegisters - ESP - 1] - sizeof(intptr_t);
gprs[kNumberOfCpuRegisters] = esp;
*(reinterpret_cast<uintptr_t*>(esp)) = eip_;
__asm__ __volatile__(
+ "movl %1, %%ebx\n\t" // Address base of FPRs.
+ "movsd 0(%%ebx), %%xmm0\n\t" // Load up XMM0-XMM7.
+ "movsd 8(%%ebx), %%xmm1\n\t"
+ "movsd 16(%%ebx), %%xmm2\n\t"
+ "movsd 24(%%ebx), %%xmm3\n\t"
+ "movsd 32(%%ebx), %%xmm4\n\t"
+ "movsd 40(%%ebx), %%xmm5\n\t"
+ "movsd 48(%%ebx), %%xmm6\n\t"
+ "movsd 56(%%ebx), %%xmm7\n\t"
"movl %0, %%esp\n\t" // ESP points to gprs.
"popal\n\t" // Load all registers except ESP and EIP with values in gprs.
"popl %%esp\n\t" // Load stack pointer.
"ret\n\t" // From higher in the stack pop eip.
: // output.
- : "g"(&gprs[0]) // input.
+ : "g"(&gprs[0]), "g"(&fprs[0]) // input.
:); // clobber.
#else
UNIMPLEMENTED(FATAL);
diff --git a/runtime/arch/x86/context_x86.h b/runtime/arch/x86/context_x86.h
index 01c8b82..c66a9dc 100644
--- a/runtime/arch/x86/context_x86.h
+++ b/runtime/arch/x86/context_x86.h
@@ -36,43 +36,64 @@
void FillCalleeSaves(const StackVisitor& fr) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetSP(uintptr_t new_sp) OVERRIDE {
- bool success = SetGPR(ESP, new_sp);
- CHECK(success) << "Failed to set ESP register";
+ SetGPR(ESP, new_sp);
}
void SetPC(uintptr_t new_pc) OVERRIDE {
eip_ = new_pc;
}
+ bool IsAccessibleGPR(uint32_t reg) OVERRIDE {
+ DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCpuRegisters));
+ return gprs_[reg] != nullptr;
+ }
+
uintptr_t* GetGPRAddress(uint32_t reg) OVERRIDE {
DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCpuRegisters));
return gprs_[reg];
}
- bool GetGPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+ uintptr_t GetGPR(uint32_t reg) OVERRIDE {
DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCpuRegisters));
- if (gprs_[reg] == nullptr) {
- return false;
- } else {
- DCHECK(val != nullptr);
- *val = *gprs_[reg];
- return true;
- }
+ DCHECK(IsAccessibleGPR(reg));
+ return *gprs_[reg];
}
- bool SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
+ void SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
- bool GetFPR(uint32_t reg, uintptr_t* val) OVERRIDE;
+ bool IsAccessibleFPR(uint32_t reg) OVERRIDE {
+ DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfFloatRegisters));
+ return fprs_[reg] != nullptr;
+ }
- bool SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
+ uintptr_t GetFPR(uint32_t reg) OVERRIDE {
+ DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfFloatRegisters));
+ DCHECK(IsAccessibleFPR(reg));
+ return *fprs_[reg];
+ }
+
+ void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
void SmashCallerSaves() OVERRIDE;
void DoLongJump() OVERRIDE;
private:
- // Pointers to register locations, floating point registers are all caller save. Values are
- // initialized to NULL or the special registers below.
+ // Pretend XMM registers are made of uin32_t pieces, because they are manipulated
+ // in uint32_t chunks.
+ enum {
+ XMM0_0 = 0, XMM0_1,
+ XMM1_0, XMM1_1,
+ XMM2_0, XMM2_1,
+ XMM3_0, XMM3_1,
+ XMM4_0, XMM4_1,
+ XMM5_0, XMM5_1,
+ XMM6_0, XMM6_1,
+ XMM7_0, XMM7_1,
+ kNumberOfFloatRegisters};
+
+ // Pointers to register locations. Values are initialized to NULL or the special registers below.
uintptr_t* gprs_[kNumberOfCpuRegisters];
+ uint32_t* fprs_[kNumberOfFloatRegisters];
// Hold values for esp and eip if they are not located within a stack frame. EIP is somewhat
// special in that it cannot be encoded normally as a register operand to an instruction (except
// in 64bit addressing modes).
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index 48d6c80..7cdd2fc 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -16,7 +16,6 @@
#include "entrypoints/interpreter/interpreter_entrypoints.h"
#include "entrypoints/jni/jni_entrypoints.h"
-#include "entrypoints/portable/portable_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_default_externs.h"
#include "entrypoints/quick/quick_entrypoints.h"
@@ -30,7 +29,7 @@
const mirror::Class* ref_class);
void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
- PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
+ QuickEntryPoints* qpoints) {
// Interpreter
ipoints->pInterpreterToInterpreterBridge = artInterpreterToInterpreterBridge;
ipoints->pInterpreterToCompiledCodeBridge = artInterpreterToCompiledCodeBridge;
@@ -38,10 +37,6 @@
// JNI
jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
- // Portable
- ppoints->pPortableResolutionTrampoline = art_portable_resolution_trampoline;
- ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge;
-
// Alloc
ResetQuickAllocEntryPoints(qpoints);
diff --git a/runtime/arch/x86/portable_entrypoints_x86.S b/runtime/arch/x86/portable_entrypoints_x86.S
deleted file mode 100644
index 1f0900e..0000000
--- a/runtime/arch/x86/portable_entrypoints_x86.S
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "asm_support_x86.S"
-
- /*
- * Portable invocation stub.
- * On entry:
- * [sp] = return address
- * [sp + 4] = method pointer
- * [sp + 8] = argument array or NULL for no argument methods
- * [sp + 12] = size of argument array in bytes
- * [sp + 16] = (managed) thread pointer
- * [sp + 20] = JValue* result
- * [sp + 24] = result type char
- */
-DEFINE_FUNCTION art_portable_invoke_stub
- PUSH ebp // save ebp
- PUSH ebx // save ebx
- mov %esp, %ebp // copy value of stack pointer into base pointer
- CFI_DEF_CFA_REGISTER(ebp)
- mov 20(%ebp), %ebx // get arg array size
- addl LITERAL(28), %ebx // reserve space for return addr, method*, ebx, and ebp in frame
- andl LITERAL(0xFFFFFFF0), %ebx // align frame size to 16 bytes
- subl LITERAL(12), %ebx // remove space for return address, ebx, and ebp
- subl %ebx, %esp // reserve stack space for argument array
- SETUP_GOT_NOSAVE ebx // reset ebx to GOT table
- lea 4(%esp), %eax // use stack pointer + method ptr as dest for memcpy
- pushl 20(%ebp) // push size of region to memcpy
- pushl 16(%ebp) // push arg array as source of memcpy
- pushl %eax // push stack pointer as destination of memcpy
- call PLT_SYMBOL(memcpy) // (void*, const void*, size_t)
- addl LITERAL(12), %esp // pop arguments to memcpy
- mov 12(%ebp), %eax // move method pointer into eax
- mov %eax, (%esp) // push method pointer onto stack
- call *MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32(%eax) // call the method
- mov %ebp, %esp // restore stack pointer
- POP ebx // pop ebx
- POP ebp // pop ebp
- mov 20(%esp), %ecx // get result pointer
- cmpl LITERAL(68), 24(%esp) // test if result type char == 'D'
- je .Lreturn_double_portable
- cmpl LITERAL(70), 24(%esp) // test if result type char == 'F'
- je .Lreturn_float_portable
- mov %eax, (%ecx) // store the result
- mov %edx, 4(%ecx) // store the other half of the result
- ret
-.Lreturn_double_portable:
- fstpl (%ecx) // store the floating point result as double
- ret
-.Lreturn_float_portable:
- fstps (%ecx) // store the floating point result as float
- ret
-END_FUNCTION art_portable_invoke_stub
-
-DEFINE_FUNCTION art_portable_proxy_invoke_handler
- PUSH ebp // Set up frame.
- movl %esp, %ebp
- CFI_DEF_CFA_REGISTER(%ebp)
- subl LITERAL(8), %esp // Align stack
- leal 8(%ebp), %edx // %edx = ArtMethod** called_addr
- movl 12(%ebp), %ecx // %ecx = receiver
- movl 0(%edx), %eax // %eax = ArtMethod* called
- pushl %edx // Pass called_addr.
- pushl %fs:THREAD_SELF_OFFSET // Pass thread.
- pushl %ecx // Pass receiver.
- pushl %eax // Pass called.
- call SYMBOL(artPortableProxyInvokeHandler) // (called, receiver, Thread*, &called)
- leave
- CFI_RESTORE(%ebp)
- CFI_DEF_CFA(%esp, 4)
- movd %eax, %xmm0 // Place return value also into floating point return value.
- movd %edx, %xmm1
- punpckldq %xmm1, %xmm0
- ret
-END_FUNCTION art_portable_proxy_invoke_handler
-
-DEFINE_FUNCTION art_portable_resolution_trampoline
- PUSH ebp // Set up frame.
- movl %esp, %ebp
- CFI_DEF_CFA_REGISTER(%ebp)
- subl LITERAL(8), %esp // Align stack
- leal 8(%ebp), %edx // %edx = ArtMethod** called_addr
- movl 12(%ebp), %ecx // %ecx = receiver
- movl 0(%edx), %eax // %eax = ArtMethod* called
- pushl %edx // Pass called_addr.
- pushl %fs:THREAD_SELF_OFFSET // Pass thread.
- pushl %ecx // Pass receiver.
- pushl %eax // Pass called.
- call SYMBOL(artPortableResolutionTrampoline) // (called, receiver, Thread*, &called)
- leave
- CFI_RESTORE(%ebp)
- CFI_DEF_CFA(%esp, 4)
- testl %eax, %eax
- jz .Lresolve_fail
- jmp * %eax
-.Lresolve_fail: // Resolution failed, return with exception pending.
- ret
-END_FUNCTION art_portable_resolution_trampoline
-
-DEFINE_FUNCTION art_portable_to_interpreter_bridge
- PUSH ebp // Set up frame.
- movl %esp, %ebp
- CFI_DEF_CFA_REGISTER(%ebp)
- subl LITERAL(12), %esp // Align stack
- leal 8(%ebp), %edx // %edx = ArtMethod** called_addr
- movl 0(%edx), %eax // %eax = ArtMethod* called
- pushl %edx // Pass called_addr.
- pushl %fs:THREAD_SELF_OFFSET // Pass thread.
- pushl %eax // Pass called.
- call SYMBOL(artPortableToInterpreterBridge) // (called, Thread*, &called)
- leave
- CFI_RESTORE(%ebp)
- CFI_DEF_CFA(%esp, 4)
- ret
-END_FUNCTION art_portable_to_interpreter_bridge
-
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 1ce01c4..beacd49 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -90,6 +90,15 @@
PUSH ebx // Save args
PUSH edx
PUSH ecx
+ // Create space for FPR args.
+ subl MACRO_LITERAL(4 * 8), %esp
+ CFI_ADJUST_CFA_OFFSET(4 * 8)
+ // Save FPRs.
+ movsd %xmm0, 0(%esp)
+ movsd %xmm1, 8(%esp)
+ movsd %xmm2, 16(%esp)
+ movsd %xmm3, 24(%esp)
+
SETUP_GOT_NOSAVE VAR(got_reg, 0)
// Load Runtime::instance_ from GOT.
movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg, 0)), REG_VAR(temp_reg, 1)
@@ -102,7 +111,7 @@
// Ugly compile-time check, but we only have the preprocessor.
// Last +4: implicit return address pushed on stack when caller made call.
-#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 7*4 + 4)
+#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 7*4 + 4*8 + 4)
#error "REFS_AND_ARGS_CALLEE_SAVE_FRAME(X86) size not as expected."
#endif
END_MACRO
@@ -112,20 +121,39 @@
* Runtime::CreateCalleeSaveMethod(kRefsAndArgs) where the method is passed in EAX.
*/
MACRO0(SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_EAX)
+ // Save callee and GPR args, mixed together to agree with core spills bitmap.
PUSH edi // Save callee saves
PUSH esi
PUSH ebp
PUSH ebx // Save args
PUSH edx
PUSH ecx
+
+ // Create space for FPR args.
+ subl MACRO_LITERAL(32), %esp
+ CFI_ADJUST_CFA_OFFSET(32)
+
+ // Save FPRs.
+ movsd %xmm0, 0(%esp)
+ movsd %xmm1, 8(%esp)
+ movsd %xmm2, 16(%esp)
+ movsd %xmm3, 24(%esp)
+
PUSH eax // Store the ArtMethod reference at the bottom of the stack.
// Store esp as the stop quick frame.
movl %esp, %fs:THREAD_TOP_QUICK_FRAME_OFFSET
END_MACRO
MACRO0(RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME)
- addl MACRO_LITERAL(4), %esp // Remove padding
- CFI_ADJUST_CFA_OFFSET(-4)
+ // Restore FPRs. EAX is still on the stack.
+ movsd 4(%esp), %xmm0
+ movsd 12(%esp), %xmm1
+ movsd 20(%esp), %xmm2
+ movsd 28(%esp), %xmm3
+
+ addl MACRO_LITERAL(36), %esp // Remove FPRs and EAX.
+ CFI_ADJUST_CFA_OFFSET(-36)
+
POP ecx // Restore args except eax
POP edx
POP ebx
@@ -134,6 +162,30 @@
POP edi
END_MACRO
+// Restore register and jump to routine
+// Inputs: EDI contains pointer to code.
+// Notes: Need to pop EAX too (restores Method*)
+MACRO0(RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME_AND_JUMP)
+ POP eax // Restore Method*
+
+ // Restore FPRs.
+ movsd 0(%esp), %xmm0
+ movsd 8(%esp), %xmm1
+ movsd 16(%esp), %xmm2
+ movsd 24(%esp), %xmm3
+
+ addl MACRO_LITERAL(32), %esp // Remove FPRs.
+ CFI_ADJUST_CFA_OFFSET(-32)
+
+ POP ecx // Restore args except eax
+ POP edx
+ POP ebx
+ POP ebp // Restore callee saves
+ POP esi
+ xchgl 0(%esp),%edi // restore EDI and place code pointer as only value on stack
+ ret
+END_MACRO
+
/*
* Macro that set calls through to artDeliverPendingExceptionFromCode, where the pending
* exception is Thread::Current()->exception_.
@@ -243,13 +295,14 @@
DEFINE_FUNCTION RAW_VAR(c_name, 0)
SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME ebx, ebx
movl %esp, %edx // remember SP
+
// Outgoing argument set up
subl MACRO_LITERAL(12), %esp // alignment padding
CFI_ADJUST_CFA_OFFSET(12)
PUSH edx // pass SP
pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
CFI_ADJUST_CFA_OFFSET(4)
- pushl 32(%edx) // pass caller Method*
+ pushl 32+32(%edx) // pass caller Method*
CFI_ADJUST_CFA_OFFSET(4)
PUSH ecx // pass arg2
PUSH eax // pass arg1
@@ -257,6 +310,17 @@
movl %edx, %edi // save code pointer in EDI
addl MACRO_LITERAL(36), %esp // Pop arguments skip eax
CFI_ADJUST_CFA_OFFSET(-36)
+
+ // Restore FPRs.
+ movsd 0(%esp), %xmm0
+ movsd 8(%esp), %xmm1
+ movsd 16(%esp), %xmm2
+ movsd 24(%esp), %xmm3
+
+ // Remove space for FPR args.
+ addl MACRO_LITERAL(4 * 8), %esp
+ CFI_ADJUST_CFA_OFFSET(-4 * 8)
+
POP ecx // Restore args except eax
POP edx
POP ebx
@@ -284,7 +348,63 @@
INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck
/*
- * Quick invocation stub.
+ * Helper for quick invocation stub to set up XMM registers.
+ * Increments shorty and arg_array and clobbers temp_char.
+ * Branches to finished if it encounters the end of the shorty.
+ */
+MACRO5(LOOP_OVER_SHORTY_LOADING_XMMS, xmm_reg, shorty, arg_array, temp_char, finished)
+1: // LOOP
+ movb (REG_VAR(shorty, 1)), REG_VAR(temp_char, 3) // temp_char := *shorty
+ addl MACRO_LITERAL(1), REG_VAR(shorty, 1) // shorty++
+ cmpb MACRO_LITERAL(0), REG_VAR(temp_char, 3) // if (temp_char == '\0')
+ je RAW_VAR(finished, 4) // goto finished
+ cmpb MACRO_LITERAL(68), REG_VAR(temp_char, 3) // if (temp_char == 'D')
+ je 2f // goto FOUND_DOUBLE
+ cmpb MACRO_LITERAL(70), REG_VAR(temp_char, 3) // if (temp_char == 'F')
+ je 3f // goto FOUND_FLOAT
+ addl MACRO_LITERAL(4), REG_VAR(arg_array, 2) // arg_array++
+ // Handle extra space in arg array taken by a long.
+ cmpb MACRO_LITERAL(74), REG_VAR(temp_char, 3) // if (temp_char != 'J')
+ jne 1b // goto LOOP
+ addl MACRO_LITERAL(4), REG_VAR(arg_array, 2) // arg_array++
+ jmp 1b // goto LOOP
+2: // FOUND_DOUBLE
+ movsd (REG_VAR(arg_array, 2)), REG_VAR(xmm_reg, 0)
+ addl MACRO_LITERAL(8), REG_VAR(arg_array, 2) // arg_array+=2
+ jmp 4f
+3: // FOUND_FLOAT
+ movss (REG_VAR(arg_array, 2)), REG_VAR(xmm_reg, 0)
+ addl MACRO_LITERAL(4), REG_VAR(arg_array, 2) // arg_array++
+4:
+END_MACRO
+
+ /*
+ * Helper for quick invocation stub to set up GPR registers.
+ * Increments shorty and arg_array, and returns the current short character in
+ * temp_char. Branches to finished if it encounters the end of the shorty.
+ */
+MACRO4(SKIP_OVER_FLOATS, shorty, arg_array, temp_char, finished)
+1: // LOOP:
+ movb (REG_VAR(shorty, 0)), REG_VAR(temp_char, 2) // temp_char := *shorty
+ addl MACRO_LITERAL(1), REG_VAR(shorty, 0) // shorty++
+ cmpb MACRO_LITERAL(0), REG_VAR(temp_char, 2) // if (temp_char == '\0')
+ je RAW_VAR(finished, 3) // goto finished
+ cmpb MACRO_LITERAL(70), REG_VAR(temp_char, 2) // if (temp_char == 'F')
+ je 3f // goto SKIP_FLOAT
+ cmpb MACRO_LITERAL(68), REG_VAR(temp_char, 2) // if (temp_char == 'D')
+ je 4f // goto SKIP_DOUBLE
+ jmp 5f // goto end
+3: // SKIP_FLOAT
+ addl MACRO_LITERAL(4), REG_VAR(arg_array, 1) // arg_array++
+ jmp 1b // goto LOOP
+4: // SKIP_DOUBLE
+ addl MACRO_LITERAL(8), REG_VAR(arg_array, 1) // arg_array+=2
+ jmp 1b // goto LOOP
+5:
+END_MACRO
+
+ /*
+ * Quick invocation stub (non-static).
* On entry:
* [sp] = return address
* [sp + 4] = method pointer
@@ -295,30 +415,73 @@
* [sp + 24] = shorty
*/
DEFINE_FUNCTION art_quick_invoke_stub
+ // Save the non-volatiles.
PUSH ebp // save ebp
PUSH ebx // save ebx
+ PUSH esi // save esi
+ PUSH edi // save edi
+ // Set up argument XMM registers.
+ mov 24+16(%esp), %esi // ESI := shorty + 1 ; ie skip return arg character.
+ addl LITERAL(1), %esi
+ mov 8+16(%esp), %edi // EDI := arg_array + 4 ; ie skip this pointer.
+ addl LITERAL(4), %edi
+ // Clobbers ESI, EDI, EAX.
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm0, esi, edi, al, .Lxmm_setup_finished
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm1, esi, edi, al, .Lxmm_setup_finished
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm2, esi, edi, al, .Lxmm_setup_finished
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm3, esi, edi, al, .Lxmm_setup_finished
+ .balign 16
+.Lxmm_setup_finished:
mov %esp, %ebp // copy value of stack pointer into base pointer
CFI_DEF_CFA_REGISTER(ebp)
- mov 20(%ebp), %ebx // get arg array size
- addl LITERAL(28), %ebx // reserve space for return addr, method*, ebx, and ebp in frame
- andl LITERAL(0xFFFFFFF0), %ebx // align frame size to 16 bytes
- subl LITERAL(12), %ebx // remove space for return address, ebx, and ebp
+ mov 28(%ebp), %ebx // get arg array size
+ // reserve space for return addr, method*, ebx, ebp, esi, and edi in frame
+ addl LITERAL(36), %ebx
+ // align frame size to 16 bytes
+ andl LITERAL(0xFFFFFFF0), %ebx
+ subl LITERAL(20), %ebx // remove space for return address, ebx, ebp, esi and edi
subl %ebx, %esp // reserve stack space for argument array
- SETUP_GOT_NOSAVE ebx // clobbers ebx (harmless here)
- lea 4(%esp), %eax // use stack pointer + method ptr as dest for memcpy
- pushl 20(%ebp) // push size of region to memcpy
- pushl 16(%ebp) // push arg array as source of memcpy
- pushl %eax // push stack pointer as destination of memcpy
- call PLT_SYMBOL(memcpy) // (void*, const void*, size_t)
- addl LITERAL(12), %esp // pop arguments to memcpy
+
movl LITERAL(0), (%esp) // store NULL for method*
- mov 12(%ebp), %eax // move method pointer into eax
- mov 4(%esp), %ecx // copy arg1 into ecx
- mov 8(%esp), %edx // copy arg2 into edx
- mov 12(%esp), %ebx // copy arg3 into ebx
+
+ // Copy arg array into stack.
+ movl 28(%ebp), %ecx // ECX = size of args
+ movl 24(%ebp), %esi // ESI = argument array
+ leal 4(%esp), %edi // EDI = just after Method* in stack arguments
+ rep movsb // while (ecx--) { *edi++ = *esi++ }
+
+ mov 40(%ebp), %esi // ESI := shorty + 1 ; ie skip return arg character.
+ addl LITERAL(1), %esi
+ mov 24(%ebp), %edi // EDI := arg_array
+ mov 0(%edi), %ecx // ECX := this pointer
+ addl LITERAL(4), %edi // EDI := arg_array + 4 ; ie skip this pointer.
+
+ // Enumerate the possible cases for loading GPRS.
+ // edx (and maybe ebx):
+ SKIP_OVER_FLOATS esi, edi, al, .Lgpr_setup_finished
+ cmpb LITERAL(74), %al // if (al == 'J') goto FOUND_LONG
+ je .LfirstLong
+ // Must be an integer value.
+ movl (%edi), %edx
+ addl LITERAL(4), %edi // arg_array++
+
+ // Now check ebx
+ SKIP_OVER_FLOATS esi, edi, al, .Lgpr_setup_finished
+ // Must be first word of a long, or an integer. First word of long doesn't
+ // go into EBX, but can be loaded there anyways, as it is harmless.
+ movl (%edi), %ebx
+ jmp .Lgpr_setup_finished
+.LfirstLong:
+ movl (%edi), %edx
+ movl 4(%edi), %ebx
+ // Nothing left to load.
+.Lgpr_setup_finished:
+ mov 20(%ebp), %eax // move method pointer into eax
call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32(%eax) // call the method
mov %ebp, %esp // restore stack pointer
CFI_DEF_CFA_REGISTER(esp)
+ POP edi // pop edi
+ POP esi // pop esi
POP ebx // pop ebx
POP ebp // pop ebp
mov 20(%esp), %ecx // get result pointer
@@ -338,6 +501,123 @@
ret
END_FUNCTION art_quick_invoke_stub
+ /*
+ * Quick invocation stub (static).
+ * On entry:
+ * [sp] = return address
+ * [sp + 4] = method pointer
+ * [sp + 8] = argument array or NULL for no argument methods
+ * [sp + 12] = size of argument array in bytes
+ * [sp + 16] = (managed) thread pointer
+ * [sp + 20] = JValue* result
+ * [sp + 24] = shorty
+ */
+DEFINE_FUNCTION art_quick_invoke_static_stub
+ // Save the non-volatiles.
+ PUSH ebp // save ebp
+ PUSH ebx // save ebx
+ PUSH esi // save esi
+ PUSH edi // save edi
+ // Set up argument XMM registers.
+ mov 24+16(%esp), %esi // ESI := shorty + 1 ; ie skip return arg character.
+ addl LITERAL(1), %esi
+ mov 8+16(%esp), %edi // EDI := arg_array
+ // Clobbers ESI, EDI, EAX.
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm0, esi, edi, al, .Lxmm_setup_finished2
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm1, esi, edi, al, .Lxmm_setup_finished2
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm2, esi, edi, al, .Lxmm_setup_finished2
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm3, esi, edi, al, .Lxmm_setup_finished2
+ .balign 16
+.Lxmm_setup_finished2:
+ mov %esp, %ebp // copy value of stack pointer into base pointer
+ CFI_DEF_CFA_REGISTER(ebp)
+ mov 28(%ebp), %ebx // get arg array size
+ // reserve space for return addr, method*, ebx, ebp, esi, and edi in frame
+ addl LITERAL(36), %ebx
+ // align frame size to 16 bytes
+ andl LITERAL(0xFFFFFFF0), %ebx
+ subl LITERAL(20), %ebx // remove space for return address, ebx, ebp, esi and edi
+ subl %ebx, %esp // reserve stack space for argument array
+
+ movl LITERAL(0), (%esp) // store NULL for method*
+
+ // Copy arg array into stack.
+ movl 28(%ebp), %ecx // ECX = size of args
+ movl 24(%ebp), %esi // ESI = argument array
+ leal 4(%esp), %edi // EDI = just after Method* in stack arguments
+ rep movsb // while (ecx--) { *edi++ = *esi++ }
+
+ mov 40(%ebp), %esi // ESI := shorty + 1 ; ie skip return arg character.
+ addl LITERAL(1), %esi
+ mov 24(%ebp), %edi // EDI := arg_array
+
+ // Enumerate the possible cases for loading GPRS.
+ // ecx (and maybe edx)
+ SKIP_OVER_FLOATS esi, edi, al, .Lgpr_setup_finished2
+ cmpb LITERAL(74), %al // if (al == 'J') goto FOUND_LONG
+ je .LfirstLong2
+ // Must be an integer value. Load into ECX.
+ movl (%edi), %ecx
+ addl LITERAL(4), %edi // arg_array++
+
+ // Now check edx (and maybe ebx).
+ SKIP_OVER_FLOATS esi, edi, al, .Lgpr_setup_finished2
+ cmpb LITERAL(74), %al // if (al == 'J') goto FOUND_LONG
+ je .LSecondLong2
+ // Must be an integer. Load into EDX.
+ movl (%edi), %edx
+ addl LITERAL(4), %edi // arg_array++
+
+ // Is there anything for ebx?
+ SKIP_OVER_FLOATS esi, edi, al, .Lgpr_setup_finished2
+ // Must be first word of a long, or an integer. First word of long doesn't
+ // go into EBX, but can be loaded there anyways, as it is harmless.
+ movl (%edi), %ebx
+ jmp .Lgpr_setup_finished2
+.LSecondLong2:
+ // EDX:EBX is long. That is all.
+ movl (%edi), %edx
+ movl 4(%edi), %ebx
+ jmp .Lgpr_setup_finished2
+.LfirstLong2:
+ // ECX:EDX is a long
+ movl (%edi), %ecx
+ movl 4(%edi), %edx
+ addl LITERAL(8), %edi // arg_array += 2
+
+ // Anything for EBX?
+ SKIP_OVER_FLOATS esi, edi, al, .Lgpr_setup_finished2
+ // Must be first word of a long, or an integer. First word of long doesn't
+ // go into EBX, but can be loaded there anyways, as it is harmless.
+ movl (%edi), %ebx
+ jmp .Lgpr_setup_finished2
+ // Nothing left to load.
+.Lgpr_setup_finished2:
+ mov 20(%ebp), %eax // move method pointer into eax
+ call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32(%eax) // call the method
+ mov %ebp, %esp // restore stack pointer
+ CFI_DEF_CFA_REGISTER(esp)
+ POP edi // pop edi
+ POP esi // pop esi
+ POP ebx // pop ebx
+ POP ebp // pop ebp
+ mov 20(%esp), %ecx // get result pointer
+ mov %eax, (%ecx) // store the result assuming its a long, int or Object*
+ mov %edx, 4(%ecx) // store the other half of the result
+ mov 24(%esp), %edx // get the shorty
+ cmpb LITERAL(68), (%edx) // test if result type char == 'D'
+ je .Lreturn_double_quick2
+ cmpb LITERAL(70), (%edx) // test if result type char == 'F'
+ je .Lreturn_float_quick2
+ ret
+.Lreturn_double_quick2:
+ movsd %xmm0, (%ecx) // store the floating point result
+ ret
+.Lreturn_float_quick2:
+ movss %xmm0, (%ecx) // store the floating point result
+ ret
+END_FUNCTION art_quick_invoke_static_stub
+
MACRO3(NO_ARG_DOWNCALL, c_name, cxx_name, return_macro)
DEFINE_FUNCTION RAW_VAR(c_name, 0)
SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC
@@ -590,6 +870,46 @@
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
+
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
+
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
+
TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO
TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO
TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO
@@ -796,20 +1116,20 @@
NO_ARG_DOWNCALL art_quick_test_suspend, artTestSuspendFromCode, ret
DEFINE_FUNCTION art_quick_d2l
- PUSH eax // alignment padding
- PUSH ecx // pass arg2 a.hi
- PUSH eax // pass arg1 a.lo
- call SYMBOL(art_d2l) // (jdouble a)
+ subl LITERAL(12), %esp // alignment padding, room for argument
+ CFI_ADJUST_CFA_OFFSET(12)
+ movsd %xmm0, 0(%esp) // arg a
+ call SYMBOL(art_d2l) // (jdouble a)
addl LITERAL(12), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-12)
ret
END_FUNCTION art_quick_d2l
DEFINE_FUNCTION art_quick_f2l
- subl LITERAL(8), %esp // alignment padding
- CFI_ADJUST_CFA_OFFSET(8)
- PUSH eax // pass arg1 a
- call SYMBOL(art_f2l) // (jfloat a)
+ subl LITERAL(12), %esp // alignment padding
+ CFI_ADJUST_CFA_OFFSET(12)
+ movss %xmm0, 0(%esp) // arg a
+ call SYMBOL(art_f2l) // (jfloat a)
addl LITERAL(12), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-12)
ret
@@ -969,8 +1289,8 @@
movd %eax, %xmm0 // place return value also into floating point return value
movd %edx, %xmm1
punpckldq %xmm1, %xmm0
- addl LITERAL(44), %esp // pop arguments
- CFI_ADJUST_CFA_OFFSET(-44)
+ addl LITERAL(76), %esp // pop arguments
+ CFI_ADJUST_CFA_OFFSET(-76)
RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception
END_FUNCTION art_quick_proxy_invoke_handler
@@ -982,7 +1302,7 @@
PUSH ecx
movl 8(%esp), %eax // load caller Method*
movl MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET(%eax), %eax // load dex_cache_resolved_methods
- movd %xmm0, %ecx // get target method index stored in xmm0
+ movd %xmm7, %ecx // get target method index stored in xmm0
movl MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4), %eax // load the target method
POP ecx
jmp SYMBOL(art_quick_invoke_interface_trampoline)
@@ -1001,14 +1321,7 @@
addl LITERAL(16), %esp // pop arguments
test %eax, %eax // if code pointer is NULL goto deliver pending exception
jz 1f
- POP eax // called method
- POP ecx // restore args
- POP edx
- POP ebx
- POP ebp // restore callee saves except EDI
- POP esi
- xchgl 0(%esp),%edi // restore EDI and place code pointer as only value on stack
- ret // tail call into method
+ RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME_AND_JUMP
1:
RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
DELIVER_PENDING_EXCEPTION
@@ -1042,7 +1355,6 @@
movl %edx, %esp
// On x86 there are no registers passed, so nothing to pop here.
-
// Native call.
call *%eax
@@ -1069,8 +1381,10 @@
jnz .Lexception_in_native
// Tear down the callee-save frame.
- addl LITERAL(4), %esp // Remove padding
- CFI_ADJUST_CFA_OFFSET(-4)
+ // Remove space for FPR args and EAX
+ addl LITERAL(4 + 4 * 8), %esp
+ CFI_ADJUST_CFA_OFFSET(-(4 + 4 * 8))
+
POP ecx
addl LITERAL(4), %esp // Avoid edx, as it may be part of the result.
CFI_ADJUST_CFA_OFFSET(-4)
@@ -1100,12 +1414,21 @@
CFI_ADJUST_CFA_OFFSET(4)
PUSH eax // pass method
call SYMBOL(artQuickToInterpreterBridge) // (method, Thread*, SP)
- movd %eax, %xmm0 // place return value also into floating point return value
- movd %edx, %xmm1
- punpckldq %xmm1, %xmm0
addl LITERAL(16), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-16)
- RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+
+ // Return eax:edx in xmm0 also.
+ movd %eax, %xmm0
+ movd %edx, %xmm1
+ punpckldq %xmm1, %xmm0
+
+ addl LITERAL(48), %esp // Remove FPRs and EAX, ECX, EDX, EBX.
+ CFI_ADJUST_CFA_OFFSET(-48)
+
+ POP ebp // Restore callee saves
+ POP esi
+ POP edi
+
RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception
END_FUNCTION art_quick_to_interpreter_bridge
@@ -1125,18 +1448,25 @@
PUSH eax // Pass Method*.
call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, LR)
addl LITERAL(28), %esp // Pop arguments upto saved Method*.
- movl 28(%esp), %edi // Restore edi.
- movl %eax, 28(%esp) // Place code* over edi, just under return pc.
+ movl 60(%esp), %edi // Restore edi.
+ movl %eax, 60(%esp) // Place code* over edi, just under return pc.
movl SYMBOL(art_quick_instrumentation_exit)@GOT(%ebx), %ebx
// Place instrumentation exit as return pc. ebx holds the GOT computed on entry.
- movl %ebx, 32(%esp)
- movl (%esp), %eax // Restore eax.
- movl 8(%esp), %ecx // Restore ecx.
- movl 12(%esp), %edx // Restore edx.
- movl 16(%esp), %ebx // Restore ebx.
- movl 20(%esp), %ebp // Restore ebp.
- movl 24(%esp), %esi // Restore esi.
- addl LITERAL(28), %esp // Wind stack back upto code*.
+ movl %ebx, 64(%esp)
+ movl 0(%esp), %eax // Restore eax.
+ // Restore FPRs (extra 4 bytes of offset due to EAX push at top).
+ movsd 8(%esp), %xmm0
+ movsd 16(%esp), %xmm1
+ movsd 24(%esp), %xmm2
+ movsd 32(%esp), %xmm3
+
+ // Restore GPRs.
+ movl 40(%esp), %ecx // Restore ecx.
+ movl 48(%esp), %edx // Restore edx.
+ movl 48(%esp), %ebx // Restore ebx.
+ movl 52(%esp), %ebp // Restore ebp.
+ movl 56(%esp), %esi // Restore esi.
+ addl LITERAL(60), %esp // Wind stack back upto code*.
ret // Call method (and pop).
END_FUNCTION art_quick_instrumentation_entry
diff --git a/runtime/arch/x86/quick_method_frame_info_x86.h b/runtime/arch/x86/quick_method_frame_info_x86.h
index b9dc0d8..9bba531 100644
--- a/runtime/arch/x86/quick_method_frame_info_x86.h
+++ b/runtime/arch/x86/quick_method_frame_info_x86.h
@@ -24,25 +24,44 @@
namespace art {
namespace x86 {
+enum XMM {
+ XMM0 = 0,
+ XMM1 = 1,
+ XMM2 = 2,
+ XMM3 = 3,
+ XMM4 = 4,
+ XMM5 = 5,
+ XMM6 = 6,
+ XMM7 = 7,
+};
+
static constexpr uint32_t kX86CalleeSaveRefSpills =
(1 << art::x86::EBP) | (1 << art::x86::ESI) | (1 << art::x86::EDI);
static constexpr uint32_t kX86CalleeSaveArgSpills =
(1 << art::x86::ECX) | (1 << art::x86::EDX) | (1 << art::x86::EBX);
+static constexpr uint32_t kX86CalleeSaveFpArgSpills =
+ (1 << art::x86::XMM0) | (1 << art::x86::XMM1) |
+ (1 << art::x86::XMM2) | (1 << art::x86::XMM3);
constexpr uint32_t X86CalleeSaveCoreSpills(Runtime::CalleeSaveType type) {
return kX86CalleeSaveRefSpills | (type == Runtime::kRefsAndArgs ? kX86CalleeSaveArgSpills : 0) |
(1 << art::x86::kNumberOfCpuRegisters); // fake return address callee save
}
+constexpr uint32_t X86CalleeSaveFpSpills(Runtime::CalleeSaveType type) {
+ return type == Runtime::kRefsAndArgs ? kX86CalleeSaveFpArgSpills : 0;
+}
+
constexpr uint32_t X86CalleeSaveFrameSize(Runtime::CalleeSaveType type) {
return RoundUp((POPCOUNT(X86CalleeSaveCoreSpills(type)) /* gprs */ +
+ 2 * POPCOUNT(X86CalleeSaveFpSpills(type)) /* fprs */ +
1 /* Method* */) * kX86PointerSize, kStackAlignment);
}
constexpr QuickMethodFrameInfo X86CalleeSaveMethodFrameInfo(Runtime::CalleeSaveType type) {
return QuickMethodFrameInfo(X86CalleeSaveFrameSize(type),
X86CalleeSaveCoreSpills(type),
- 0u);
+ X86CalleeSaveFpSpills(type));
}
} // namespace x86
diff --git a/runtime/arch/x86_64/context_x86_64.cc b/runtime/arch/x86_64/context_x86_64.cc
index 6e9b99c..cdc2ec7 100644
--- a/runtime/arch/x86_64/context_x86_64.cc
+++ b/runtime/arch/x86_64/context_x86_64.cc
@@ -91,26 +91,18 @@
fprs_[XMM11] = nullptr;
}
-bool X86_64Context::SetGPR(uint32_t reg, uintptr_t value) {
+void X86_64Context::SetGPR(uint32_t reg, uintptr_t value) {
CHECK_LT(reg, static_cast<uint32_t>(kNumberOfCpuRegisters));
+ DCHECK(IsAccessibleGPR(reg));
CHECK_NE(gprs_[reg], &gZero);
- if (gprs_[reg] != nullptr) {
- *gprs_[reg] = value;
- return true;
- } else {
- return false;
- }
+ *gprs_[reg] = value;
}
-bool X86_64Context::SetFPR(uint32_t reg, uintptr_t value) {
+void X86_64Context::SetFPR(uint32_t reg, uintptr_t value) {
CHECK_LT(reg, static_cast<uint32_t>(kNumberOfFloatRegisters));
+ DCHECK(IsAccessibleFPR(reg));
CHECK_NE(fprs_[reg], reinterpret_cast<const uint64_t*>(&gZero));
- if (fprs_[reg] != nullptr) {
- *fprs_[reg] = value;
- return true;
- } else {
- return false;
- }
+ *fprs_[reg] = value;
}
extern "C" void art_quick_do_long_jump(uintptr_t*, uintptr_t*);
diff --git a/runtime/arch/x86_64/context_x86_64.h b/runtime/arch/x86_64/context_x86_64.h
index 902c3b9..0dda06e 100644
--- a/runtime/arch/x86_64/context_x86_64.h
+++ b/runtime/arch/x86_64/context_x86_64.h
@@ -36,44 +36,43 @@
void FillCalleeSaves(const StackVisitor& fr) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetSP(uintptr_t new_sp) OVERRIDE {
- bool success = SetGPR(RSP, new_sp);
- CHECK(success) << "Failed to set RSP register";
+ SetGPR(RSP, new_sp);
}
void SetPC(uintptr_t new_pc) OVERRIDE {
rip_ = new_pc;
}
+ bool IsAccessibleGPR(uint32_t reg) OVERRIDE {
+ DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCpuRegisters));
+ return gprs_[reg] != nullptr;
+ }
+
uintptr_t* GetGPRAddress(uint32_t reg) OVERRIDE {
DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCpuRegisters));
return gprs_[reg];
}
- bool GetGPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+ uintptr_t GetGPR(uint32_t reg) OVERRIDE {
DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCpuRegisters));
- if (gprs_[reg] == nullptr) {
- return false;
- } else {
- DCHECK(val != nullptr);
- *val = *gprs_[reg];
- return true;
- }
+ DCHECK(IsAccessibleGPR(reg));
+ return *gprs_[reg];
}
- bool SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
+ void SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
- bool GetFPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+ bool IsAccessibleFPR(uint32_t reg) OVERRIDE {
DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfFloatRegisters));
- if (fprs_[reg] == nullptr) {
- return false;
- } else {
- DCHECK(val != nullptr);
- *val = *fprs_[reg];
- return true;
- }
+ return fprs_[reg] != nullptr;
}
- bool SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
+ uintptr_t GetFPR(uint32_t reg) OVERRIDE {
+ DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfFloatRegisters));
+ DCHECK(IsAccessibleFPR(reg));
+ return *fprs_[reg];
+ }
+
+ void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
void SmashCallerSaves() OVERRIDE;
void DoLongJump() OVERRIDE;
diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
index a2766f7..b25d7a7 100644
--- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc
+++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
@@ -16,7 +16,6 @@
#include "entrypoints/interpreter/interpreter_entrypoints.h"
#include "entrypoints/jni/jni_entrypoints.h"
-#include "entrypoints/portable/portable_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_default_externs.h"
#include "entrypoints/quick/quick_entrypoints.h"
@@ -31,9 +30,9 @@
const mirror::Class* ref_class);
void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
- PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
+ QuickEntryPoints* qpoints) {
#if defined(__APPLE__)
- UNUSED(ipoints, jpoints, ppoints, qpoints);
+ UNUSED(ipoints, jpoints, qpoints);
UNIMPLEMENTED(FATAL);
#else
// Interpreter
@@ -43,10 +42,6 @@
// JNI
jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
- // Portable
- ppoints->pPortableResolutionTrampoline = art_portable_resolution_trampoline;
- ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge;
-
// Alloc
ResetQuickAllocEntryPoints(qpoints);
diff --git a/runtime/arch/x86_64/portable_entrypoints_x86_64.S b/runtime/arch/x86_64/portable_entrypoints_x86_64.S
deleted file mode 100644
index 3a54005..0000000
--- a/runtime/arch/x86_64/portable_entrypoints_x86_64.S
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "asm_support_x86_64.S"
-
- /*
- * Portable invocation stub.
- */
-UNIMPLEMENTED art_portable_invoke_stub
-
-UNIMPLEMENTED art_portable_proxy_invoke_handler
-
-UNIMPLEMENTED art_portable_resolution_trampoline
-
-UNIMPLEMENTED art_portable_to_interpreter_bridge
-
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index a80e7d2..c865541 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -487,15 +487,21 @@
PUSH rbp // Save rbp.
PUSH r8 // Save r8/result*.
PUSH r9 // Save r9/shorty*.
+ PUSH rbx // Save native callee save rbx
+ PUSH r12 // Save native callee save r12
+ PUSH r13 // Save native callee save r13
+ PUSH r14 // Save native callee save r14
+ PUSH r15 // Save native callee save r15
movq %rsp, %rbp // Copy value of stack pointer into base pointer.
CFI_DEF_CFA_REGISTER(rbp)
movl %edx, %r10d
- addl LITERAL(60), %edx // Reserve space for return addr, StackReference<method>, rbp,
- // r8 and r9 in frame.
- andl LITERAL(0xFFFFFFF0), %edx // Align frame size to 16 bytes.
- subl LITERAL(32), %edx // Remove space for return address, rbp, r8 and r9.
- subq %rdx, %rsp // Reserve stack space for argument array.
+ addl LITERAL(100), %edx // Reserve space for return addr, StackReference<method>, rbp,
+ // r8, r9, rbx, r12, r13, r14, and r15 in frame.
+ andl LITERAL(0xFFFFFFF0), %edx // Align frame size to 16 bytes.
+ subl LITERAL(72), %edx // Remove space for return address, rbp, r8, r9, rbx, r12,
+ // r13, r14, and r15
+ subq %rdx, %rsp // Reserve stack space for argument array.
#if (STACK_REFERENCE_SIZE != 4)
#error "STACK_REFERENCE_SIZE(X86_64) size not as expected."
@@ -503,15 +509,15 @@
movl LITERAL(0), (%rsp) // Store NULL for method*
movl %r10d, %ecx // Place size of args in rcx.
- movq %rdi, %rax // RAX := method to be called
- movq %rsi, %r11 // R11 := arg_array
- leaq 4(%rsp), %rdi // Rdi is pointing just above the StackReference<method> in the
+ movq %rdi, %rax // rax := method to be called
+ movq %rsi, %r11 // r11 := arg_array
+ leaq 4(%rsp), %rdi // rdi is pointing just above the StackReference<method> in the
// stack arguments.
// Copy arg array into stack.
rep movsb // while (rcx--) { *rdi++ = *rsi++ }
- leaq 1(%r9), %r10 // R10 := shorty + 1 ; ie skip return arg character
- movq %rax, %rdi // RDI := method to be called
- movl (%r11), %esi // RSI := this pointer
+ leaq 1(%r9), %r10 // r10 := shorty + 1 ; ie skip return arg character
+ movq %rax, %rdi // rdi := method to be called
+ movl (%r11), %esi // rsi := this pointer
addq LITERAL(4), %r11 // arg_array++
LOOP_OVER_SHORTY_LOADING_GPRS rdx, edx, .Lgpr_setup_finished
LOOP_OVER_SHORTY_LOADING_GPRS rcx, ecx, .Lgpr_setup_finished
@@ -520,8 +526,12 @@
.Lgpr_setup_finished:
call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method.
movq %rbp, %rsp // Restore stack pointer.
- CFI_DEF_CFA_REGISTER(rsp)
- POP r9 // Pop r9 - shorty*.
+ POP r15 // Pop r15
+ POP r14 // Pop r14
+ POP r13 // Pop r13
+ POP r12 // Pop r12
+ POP rbx // Pop rbx
+ POP r9 // Pop r9 - shorty*
POP r8 // Pop r8 - result*.
POP rbp // Pop rbp
cmpb LITERAL(68), (%r9) // Test if result type char == 'D'.
@@ -531,10 +541,10 @@
movq %rax, (%r8) // Store the result assuming its a long, int or Object*
ret
.Lreturn_double_quick:
- movsd %xmm0, (%r8) // Store the double floating point result.
+ movsd %xmm0, (%r8) // Store the double floating point result.
ret
.Lreturn_float_quick:
- movss %xmm0, (%r8) // Store the floating point result.
+ movss %xmm0, (%r8) // Store the floating point result.
ret
#endif // __APPLE__
END_FUNCTION art_quick_invoke_stub
@@ -571,30 +581,36 @@
PUSH rbp // Save rbp.
PUSH r8 // Save r8/result*.
PUSH r9 // Save r9/shorty*.
+ PUSH rbx // Save rbx
+ PUSH r12 // Save r12
+ PUSH r13 // Save r13
+ PUSH r14 // Save r14
+ PUSH r15 // Save r15
movq %rsp, %rbp // Copy value of stack pointer into base pointer.
CFI_DEF_CFA_REGISTER(rbp)
movl %edx, %r10d
- addl LITERAL(60), %edx // Reserve space for return addr, StackReference<method>, rbp,
- // r8 and r9 in frame.
- andl LITERAL(0xFFFFFFF0), %edx // Align frame size to 16 bytes.
- subl LITERAL(32), %edx // Remove space for return address, rbp, r8 and r9.
- subq %rdx, %rsp // Reserve stack space for argument array.
+ addl LITERAL(100), %edx // Reserve space for return addr, StackReference<method>, rbp,
+ // r8, r9, r12, r13, r14, and r15 in frame.
+ andl LITERAL(0xFFFFFFF0), %edx // Align frame size to 16 bytes.
+ subl LITERAL(72), %edx // Remove space for return address, rbp, r8, r9, rbx, r12,
+ // r13, r14, and r15.
+ subq %rdx, %rsp // Reserve stack space for argument array.
#if (STACK_REFERENCE_SIZE != 4)
#error "STACK_REFERENCE_SIZE(X86_64) size not as expected."
#endif
- movl LITERAL(0), (%rsp) // Store NULL for method*
+ movl LITERAL(0), (%rsp) // Store NULL for method*
- movl %r10d, %ecx // Place size of args in rcx.
- movq %rdi, %rax // RAX := method to be called
- movq %rsi, %r11 // R11 := arg_array
- leaq 4(%rsp), %rdi // Rdi is pointing just above the StackReference<method> in the
- // stack arguments.
+ movl %r10d, %ecx // Place size of args in rcx.
+ movq %rdi, %rax // rax := method to be called
+ movq %rsi, %r11 // r11 := arg_array
+ leaq 4(%rsp), %rdi // rdi is pointing just above the StackReference<method> in the
+ // stack arguments.
// Copy arg array into stack.
- rep movsb // while (rcx--) { *rdi++ = *rsi++ }
- leaq 1(%r9), %r10 // R10 := shorty + 1 ; ie skip return arg character
- movq %rax, %rdi // RDI := method to be called
+ rep movsb // while (rcx--) { *rdi++ = *rsi++ }
+ leaq 1(%r9), %r10 // r10 := shorty + 1 ; ie skip return arg character
+ movq %rax, %rdi // rdi := method to be called
LOOP_OVER_SHORTY_LOADING_GPRS rsi, esi, .Lgpr_setup_finished2
LOOP_OVER_SHORTY_LOADING_GPRS rdx, edx, .Lgpr_setup_finished2
LOOP_OVER_SHORTY_LOADING_GPRS rcx, ecx, .Lgpr_setup_finished2
@@ -602,22 +618,26 @@
LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished2
.Lgpr_setup_finished2:
call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method.
- movq %rbp, %rsp // Restore stack pointer.
- CFI_DEF_CFA_REGISTER(rsp)
- POP r9 // Pop r9 - shorty*.
- POP r8 // Pop r8 - result*.
- POP rbp // Pop rbp
- cmpb LITERAL(68), (%r9) // Test if result type char == 'D'.
+ movq %rbp, %rsp // Restore stack pointer.
+ POP r15 // Pop r15
+ POP r14 // Pop r14
+ POP r13 // Pop r13
+ POP r12 // Pop r12
+ POP rbx // Pop rbx
+ POP r9 // Pop r9 - shorty*.
+ POP r8 // Pop r8 - result*.
+ POP rbp // Pop rbp
+ cmpb LITERAL(68), (%r9) // Test if result type char == 'D'.
je .Lreturn_double_quick2
- cmpb LITERAL(70), (%r9) // Test if result type char == 'F'.
+ cmpb LITERAL(70), (%r9) // Test if result type char == 'F'.
je .Lreturn_float_quick2
- movq %rax, (%r8) // Store the result assuming its a long, int or Object*
+ movq %rax, (%r8) // Store the result assuming its a long, int or Object*
ret
.Lreturn_double_quick2:
- movsd %xmm0, (%r8) // Store the double floating point result.
+ movsd %xmm0, (%r8) // Store the double floating point result.
ret
.Lreturn_float_quick2:
- movss %xmm0, (%r8) // Store the floating point result.
+ movss %xmm0, (%r8) // Store the floating point result.
ret
#endif // __APPLE__
END_FUNCTION art_quick_invoke_static_stub
@@ -883,6 +903,46 @@
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
+
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
+
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
+
TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO
TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO
TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO
diff --git a/runtime/arch/x86_64/registers_x86_64.h b/runtime/arch/x86_64/registers_x86_64.h
index 8b0dc07..dda1d5f 100644
--- a/runtime/arch/x86_64/registers_x86_64.h
+++ b/runtime/arch/x86_64/registers_x86_64.h
@@ -43,6 +43,7 @@
R13 = 13,
R14 = 14,
R15 = 15,
+ kLastCpuRegister = 15,
kNumberOfCpuRegisters = 16,
kNoRegister = -1 // Signals an illegal register.
};
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 7454cca..a35e05b 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -148,18 +148,10 @@
ADD_TEST_EQ(MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET,
art::mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value())
-#define MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32 (40 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32,
- art::mirror::ArtMethod::EntryPointFromPortableCompiledCodeOffset(4).Int32Value())
-
#define MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32 (36 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32,
art::mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value())
-#define MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_64 (56 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_64,
- art::mirror::ArtMethod::EntryPointFromPortableCompiledCodeOffset(8).Int32Value())
-
#define MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64 (48 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64,
art::mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(8).Int32Value())
diff --git a/runtime/atomic.h b/runtime/atomic.h
index cf61277..87de506 100644
--- a/runtime/atomic.h
+++ b/runtime/atomic.h
@@ -46,6 +46,9 @@
class QuasiAtomic {
#if defined(__mips__) && !defined(__LP64__)
static constexpr bool kNeedSwapMutexes = true;
+#elif defined(__mips__) && defined(__LP64__)
+ // TODO - mips64 still need this for Cas64 ???
+ static constexpr bool kNeedSwapMutexes = true;
#else
static constexpr bool kNeedSwapMutexes = false;
#endif
diff --git a/runtime/barrier.cc b/runtime/barrier.cc
index 5a8fbb3..66ee870 100644
--- a/runtime/barrier.cc
+++ b/runtime/barrier.cc
@@ -52,7 +52,7 @@
// Pass function is called by the last thread, the count will
// be decremented to zero and a Broadcast will be made on the
// condition variable, thus waking this up.
- if (count_ != 0) {
+ while (count_ != 0) {
condition_.Wait(self);
}
}
@@ -62,7 +62,18 @@
SetCountLocked(self, count_ + delta);
bool timed_out = false;
if (count_ != 0) {
- timed_out = condition_.TimedWait(self, timeout_ms, 0);
+ uint32_t timeout_ns = 0;
+ uint64_t abs_timeout = NanoTime() + MsToNs(timeout_ms);
+ for (;;) {
+ timed_out = condition_.TimedWait(self, timeout_ms, timeout_ns);
+ if (timed_out || count_ == 0) return timed_out;
+ // Compute time remaining on timeout.
+ uint64_t now = NanoTime();
+ int64_t time_left = abs_timeout - now;
+ if (time_left <= 0) return true;
+ timeout_ns = time_left % (1000*1000);
+ timeout_ms = time_left / (1000*1000);
+ }
}
return timed_out;
}
diff --git a/runtime/barrier.h b/runtime/barrier.h
index 5ca88e8..0e7f61e 100644
--- a/runtime/barrier.h
+++ b/runtime/barrier.h
@@ -14,6 +14,16 @@
* limitations under the License.
*/
+// CAUTION: THIS IS NOT A FULLY GENERAL BARRIER API.
+
+// It may either be used as a "latch" or single-use barrier, or it may be reused under
+// very limited conditions, e.g. if only Pass(), but not Wait() is called. Unlike a standard
+// latch API, it is possible to initialize the latch to a count of zero, repeatedly call
+// Pass() or Wait(), and only then set the count using the Increment() method. Threads at
+// a Wait() are only awoken if the count reaches zero AFTER the decrement is applied.
+// This works because, also unlike most latch APIs, there is no way to Wait() without
+// decrementing the count, and thus nobody can spuriosly wake up on the initial zero.
+
#ifndef ART_RUNTIME_BARRIER_H_
#define ART_RUNTIME_BARRIER_H_
@@ -22,20 +32,23 @@
namespace art {
+// TODO: Maybe give this a better name.
class Barrier {
public:
explicit Barrier(int count);
virtual ~Barrier();
- // Pass through the barrier, decrements the count but does not block.
+ // Pass through the barrier, decrement the count but do not block.
void Pass(Thread* self);
// Wait on the barrier, decrement the count.
void Wait(Thread* self);
- // Set the count to a new value, if the value is 0 then everyone waiting on the condition
- // variable is resumed.
- void Init(Thread* self, int count);
+ // The following three calls are only safe if we somehow know that no other thread both
+ // - has been woken up, and
+ // - has not left the Wait() or Increment() call.
+ // If these calls are made in that situation, the offending thread is likely to go back
+ // to sleep, resulting in a deadlock.
// Increment the count by delta, wait on condition if count is non zero.
void Increment(Thread* self, int delta) LOCKS_EXCLUDED(lock_);
@@ -44,6 +57,10 @@
// true if time out occurred.
bool Increment(Thread* self, int delta, uint32_t timeout_ms) LOCKS_EXCLUDED(lock_);
+ // Set the count to a new value. This should only be used if there is no possibility that
+ // another thread is still in Wait(). See above.
+ void Init(Thread* self, int count);
+
private:
void SetCountLocked(Thread* self, int count) EXCLUSIVE_LOCKS_REQUIRED(lock_);
diff --git a/runtime/barrier_test.cc b/runtime/barrier_test.cc
index de348dc..f68a5d4 100644
--- a/runtime/barrier_test.cc
+++ b/runtime/barrier_test.cc
@@ -27,22 +27,17 @@
namespace art {
class CheckWaitTask : public Task {
public:
- CheckWaitTask(Barrier* barrier, AtomicInteger* count1, AtomicInteger* count2,
- AtomicInteger* count3)
+ CheckWaitTask(Barrier* barrier, AtomicInteger* count1, AtomicInteger* count2)
: barrier_(barrier),
count1_(count1),
- count2_(count2),
- count3_(count3) {}
+ count2_(count2) {}
void Run(Thread* self) {
- LOG(INFO) << "Before barrier 1 " << *self;
+ LOG(INFO) << "Before barrier" << *self;
++*count1_;
barrier_->Wait(self);
++*count2_;
- LOG(INFO) << "Before barrier 2 " << *self;
- barrier_->Wait(self);
- ++*count3_;
- LOG(INFO) << "After barrier 2 " << *self;
+ LOG(INFO) << "After barrier" << *self;
}
virtual void Finalize() {
@@ -53,7 +48,6 @@
Barrier* const barrier_;
AtomicInteger* const count1_;
AtomicInteger* const count2_;
- AtomicInteger* const count3_;
};
class BarrierTest : public CommonRuntimeTest {
@@ -67,31 +61,27 @@
TEST_F(BarrierTest, CheckWait) {
Thread* self = Thread::Current();
ThreadPool thread_pool("Barrier test thread pool", num_threads);
- Barrier barrier(0);
+ Barrier barrier(num_threads + 1); // One extra Wait() in main thread.
+ Barrier timeout_barrier(0); // Only used for sleeping on timeout.
AtomicInteger count1(0);
AtomicInteger count2(0);
- AtomicInteger count3(0);
for (int32_t i = 0; i < num_threads; ++i) {
- thread_pool.AddTask(self, new CheckWaitTask(&barrier, &count1, &count2, &count3));
+ thread_pool.AddTask(self, new CheckWaitTask(&barrier, &count1, &count2));
}
thread_pool.StartWorkers(self);
- barrier.Increment(self, num_threads);
- // At this point each thread should have passed through the barrier. The first count should be
- // equal to num_threads.
- EXPECT_EQ(num_threads, count1.LoadRelaxed());
- // Count 3 should still be zero since no thread should have gone past the second barrier.
- EXPECT_EQ(0, count3.LoadRelaxed());
- // Now lets tell the threads to pass again.
- barrier.Increment(self, num_threads);
- // Count 2 should be equal to num_threads since each thread must have passed the second barrier
- // at this point.
- EXPECT_EQ(num_threads, count2.LoadRelaxed());
+ while (count1.LoadRelaxed() != num_threads) {
+ timeout_barrier.Increment(self, 1, 100); // sleep 100 msecs
+ }
+ // Count 2 should still be zero since no thread should have gone past the barrier.
+ EXPECT_EQ(0, count2.LoadRelaxed());
+ // Perform one additional Wait(), allowing pool threads to proceed.
+ barrier.Wait(self);
// Wait for all the threads to finish.
thread_pool.Wait(self, true, false);
- // All three counts should be equal to num_threads now.
- EXPECT_EQ(count1.LoadRelaxed(), count2.LoadRelaxed());
- EXPECT_EQ(count2.LoadRelaxed(), count3.LoadRelaxed());
- EXPECT_EQ(num_threads, count3.LoadRelaxed());
+ // Both counts should be equal to num_threads now.
+ EXPECT_EQ(count1.LoadRelaxed(), num_threads);
+ EXPECT_EQ(count2.LoadRelaxed(), num_threads);
+ timeout_barrier.Init(self, 0); // Reset to zero for destruction.
}
class CheckPassTask : public Task {
diff --git a/runtime/base/allocator.h b/runtime/base/allocator.h
index 5a09c96..8720f0e 100644
--- a/runtime/base/allocator.h
+++ b/runtime/base/allocator.h
@@ -62,7 +62,7 @@
kAllocatorTagRememberedSet,
kAllocatorTagModUnionCardSet,
kAllocatorTagModUnionReferenceArray,
- kAllocatorTagJNILibrarires,
+ kAllocatorTagJNILibraries,
kAllocatorTagCompileTimeClassPath,
kAllocatorTagOatFile,
kAllocatorTagDexFileVerifier,
diff --git a/runtime/base/bit_vector-inl.h b/runtime/base/bit_vector-inl.h
index dc13dd5..39b19e5 100644
--- a/runtime/base/bit_vector-inl.h
+++ b/runtime/base/bit_vector-inl.h
@@ -29,7 +29,7 @@
return bit_index_ == other.bit_index_;
}
-inline int BitVector::IndexIterator::operator*() const {
+inline uint32_t BitVector::IndexIterator::operator*() const {
DCHECK_LT(bit_index_, BitSize());
return bit_index_;
}
diff --git a/runtime/base/bit_vector.cc b/runtime/base/bit_vector.cc
index 4390180..c3e24a7 100644
--- a/runtime/base/bit_vector.cc
+++ b/runtime/base/bit_vector.cc
@@ -276,6 +276,10 @@
}
}
+#if defined(__clang__) && defined(__ARM_64BIT_STATE)
+// b/19180814 When POPCOUNT is inlined, boot up failed on arm64 devices.
+__attribute__((optnone))
+#endif
uint32_t BitVector::NumSetBits(const uint32_t* storage, uint32_t end) {
uint32_t word_end = WordIndex(end);
uint32_t partial_word_bits = end & 0x1f;
diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h
index 1e28a27..557a2ec 100644
--- a/runtime/base/bit_vector.h
+++ b/runtime/base/bit_vector.h
@@ -52,7 +52,7 @@
return !(*this == other);
}
- int operator*() const;
+ uint32_t operator*() const;
IndexIterator& operator++();
diff --git a/runtime/base/bit_vector_test.cc b/runtime/base/bit_vector_test.cc
index 31fd0e7..fe3313d1 100644
--- a/runtime/base/bit_vector_test.cc
+++ b/runtime/base/bit_vector_test.cc
@@ -57,10 +57,10 @@
BitVector::IndexIterator iterator = bv.Indexes().begin();
EXPECT_TRUE(iterator != bv.Indexes().end());
- EXPECT_EQ(0, *iterator);
+ EXPECT_EQ(0u, *iterator);
++iterator;
EXPECT_TRUE(iterator != bv.Indexes().end());
- EXPECT_EQ(static_cast<int>(kBits - 1), *iterator);
+ EXPECT_EQ(kBits - 1u, *iterator);
++iterator;
EXPECT_TRUE(iterator == bv.Indexes().end());
}
diff --git a/runtime/base/dumpable.h b/runtime/base/dumpable.h
index 3c316cc..9bc4089 100644
--- a/runtime/base/dumpable.h
+++ b/runtime/base/dumpable.h
@@ -17,6 +17,8 @@
#ifndef ART_RUNTIME_BASE_DUMPABLE_H_
#define ART_RUNTIME_BASE_DUMPABLE_H_
+#include <ostream>
+
#include "base/macros.h"
namespace art {
diff --git a/runtime/base/histogram-inl.h b/runtime/base/histogram-inl.h
index b329a31..812ed86 100644
--- a/runtime/base/histogram-inl.h
+++ b/runtime/base/histogram-inl.h
@@ -35,10 +35,13 @@
DCHECK_GT(new_max, max_);
GrowBuckets(new_max);
}
-
BucketiseValue(value);
}
+template <class Value> inline void Histogram<Value>::AdjustAndAddValue(Value value) {
+ AddValue(value / kAdjust);
+}
+
template <class Value> inline Histogram<Value>::Histogram(const char* name)
: kAdjust(0),
kInitialBucketCount(0),
diff --git a/runtime/base/histogram.h b/runtime/base/histogram.h
index 1e12be8..78f6e1c 100644
--- a/runtime/base/histogram.h
+++ b/runtime/base/histogram.h
@@ -46,6 +46,7 @@
// This is the expected constructor when creating new Histograms.
Histogram(const char* name, Value initial_bucket_width, size_t max_buckets = 100);
void AddValue(Value);
+ void AdjustAndAddValue(Value); // Add a value after dividing it by kAdjust.
// Builds the cumulative distribution function from the frequency data.
// Accumulative summation of frequencies.
// cumulative_freq[i] = sum(frequency[j] : 0 < j < i )
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
index b781d60..0764b87 100644
--- a/runtime/base/logging.cc
+++ b/runtime/base/logging.cc
@@ -16,6 +16,8 @@
#include "logging.h"
+#include <iostream>
+#include <limits>
#include <sstream>
#include "base/mutex.h"
@@ -42,6 +44,19 @@
static std::unique_ptr<std::string> gProgramInvocationName;
static std::unique_ptr<std::string> gProgramInvocationShortName;
+// Print INTERNAL_FATAL messages directly instead of at destruction time. This only works on the
+// host right now: for the device, a stream buf collating output into lines and calling LogLine or
+// lower-level logging is necessary.
+#ifdef HAVE_ANDROID_OS
+static constexpr bool kPrintInternalFatalDirectly = false;
+#else
+static constexpr bool kPrintInternalFatalDirectly = !kIsTargetBuild;
+#endif
+
+static bool PrintDirectly(LogSeverity severity) {
+ return kPrintInternalFatalDirectly && severity == INTERNAL_FATAL;
+}
+
const char* GetCmdLine() {
return (gCmdLine.get() != nullptr) ? gCmdLine->c_str() : nullptr;
}
@@ -169,31 +184,39 @@
LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, int error)
: data_(new LogMessageData(file, line, severity, error)) {
+ if (PrintDirectly(severity)) {
+ static const char* log_characters = "VDIWEFF";
+ CHECK_EQ(strlen(log_characters), INTERNAL_FATAL + 1U);
+ stream() << ProgramInvocationShortName() << " " << log_characters[static_cast<size_t>(severity)]
+ << " " << getpid() << " " << ::art::GetTid() << " " << file << ":" << line << "]";
+ }
}
LogMessage::~LogMessage() {
- if (data_->GetSeverity() < gMinimumLogSeverity) {
- return; // No need to format something we're not going to output.
- }
+ if (!PrintDirectly(data_->GetSeverity())) {
+ if (data_->GetSeverity() < gMinimumLogSeverity) {
+ return; // No need to format something we're not going to output.
+ }
- // Finish constructing the message.
- if (data_->GetError() != -1) {
- data_->GetBuffer() << ": " << strerror(data_->GetError());
- }
- std::string msg(data_->ToString());
+ // Finish constructing the message.
+ if (data_->GetError() != -1) {
+ data_->GetBuffer() << ": " << strerror(data_->GetError());
+ }
+ std::string msg(data_->ToString());
- // Do the actual logging with the lock held.
- {
- MutexLock mu(Thread::Current(), *Locks::logging_lock_);
- if (msg.find('\n') == std::string::npos) {
- LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), msg.c_str());
- } else {
- msg += '\n';
- size_t i = 0;
- while (i < msg.size()) {
- size_t nl = msg.find('\n', i);
- msg[nl] = '\0';
- LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), &msg[i]);
- i = nl + 1;
+ // Do the actual logging with the lock held.
+ {
+ MutexLock mu(Thread::Current(), *Locks::logging_lock_);
+ if (msg.find('\n') == std::string::npos) {
+ LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), msg.c_str());
+ } else {
+ msg += '\n';
+ size_t i = 0;
+ while (i < msg.size()) {
+ size_t nl = msg.find('\n', i);
+ msg[nl] = '\0';
+ LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), &msg[i]);
+ i = nl + 1;
+ }
}
}
}
@@ -205,6 +228,9 @@
}
std::ostream& LogMessage::stream() {
+ if (PrintDirectly(data_->GetSeverity())) {
+ return std::cerr;
+ }
return data_->GetBuffer();
}
@@ -239,8 +265,25 @@
void LogMessage::LogLineLowStack(const char* file, unsigned int line, LogSeverity log_severity,
const char* message) {
#ifdef HAVE_ANDROID_OS
- // TODO: be more conservative on stack usage here.
- LogLine(file, line, log_severity, message);
+ // Use android_writeLog() to avoid stack-based buffers used by android_printLog().
+ const char* tag = ProgramInvocationShortName();
+ int priority = kLogSeverityToAndroidLogPriority[log_severity];
+ char* buf = nullptr;
+ size_t buf_size = 0u;
+ if (priority == ANDROID_LOG_FATAL) {
+ // Allocate buffer for snprintf(buf, buf_size, "%s:%u] %s", file, line, message) below.
+ // If allocation fails, fall back to printing only the message.
+ buf_size = strlen(file) + 1 /* ':' */ + std::numeric_limits<typeof(line)>::max_digits10 +
+ 2 /* "] " */ + strlen(message) + 1 /* terminating 0 */;
+ buf = reinterpret_cast<char*>(malloc(buf_size));
+ }
+ if (buf != nullptr) {
+ snprintf(buf, buf_size, "%s:%u] %s", file, line, message);
+ android_writeLog(priority, tag, buf);
+ free(buf);
+ } else {
+ android_writeLog(priority, tag, message);
+ }
#else
static const char* log_characters = "VDIWEFF";
CHECK_EQ(strlen(log_characters), INTERNAL_FATAL + 1U);
@@ -260,4 +303,13 @@
#endif
}
+ScopedLogSeverity::ScopedLogSeverity(LogSeverity level) {
+ old_ = gMinimumLogSeverity;
+ gMinimumLogSeverity = level;
+}
+
+ScopedLogSeverity::~ScopedLogSeverity() {
+ gMinimumLogSeverity = old_;
+}
+
} // namespace art
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index ae83e33..cc1a4a1 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -254,6 +254,16 @@
DISALLOW_COPY_AND_ASSIGN(LogMessage);
};
+// Allows to temporarily change the minimum severity level for logging.
+class ScopedLogSeverity {
+ public:
+ explicit ScopedLogSeverity(LogSeverity level);
+ ~ScopedLogSeverity();
+
+ private:
+ LogSeverity old_;
+};
+
} // namespace art
#endif // ART_RUNTIME_BASE_LOGGING_H_
diff --git a/runtime/base/macros.h b/runtime/base/macros.h
index 66d6fab..f705469 100644
--- a/runtime/base/macros.h
+++ b/runtime/base/macros.h
@@ -158,6 +158,8 @@
#define ALWAYS_INLINE_LAMBDA ALWAYS_INLINE
#endif
+#define NO_INLINE __attribute__ ((noinline))
+
#if defined (__APPLE__)
#define HOT_ATTR
#define COLD_ATTR
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index aa2aefc..6e00cc7 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -319,16 +319,25 @@
exclusive_owner_ = 0;
}
+// Helper to ignore the lock requirement.
+static bool IsShuttingDown() NO_THREAD_SAFETY_ANALYSIS {
+ Runtime* runtime = Runtime::Current();
+ return runtime == nullptr || runtime->IsShuttingDownLocked();
+}
+
Mutex::~Mutex() {
+ bool shutting_down = IsShuttingDown();
#if ART_USE_FUTEXES
if (state_.LoadRelaxed() != 0) {
- Runtime* runtime = Runtime::Current();
- bool shutting_down = runtime == nullptr || runtime->IsShuttingDown(Thread::Current());
LOG(shutting_down ? WARNING : FATAL) << "destroying mutex with owner: " << exclusive_owner_;
} else {
- CHECK_EQ(exclusive_owner_, 0U) << "unexpectedly found an owner on unlocked mutex " << name_;
- CHECK_EQ(num_contenders_.LoadSequentiallyConsistent(), 0)
- << "unexpectedly found a contender on mutex " << name_;
+ if (exclusive_owner_ != 0) {
+ LOG(shutting_down ? WARNING : FATAL) << "unexpectedly found an owner on unlocked mutex "
+ << name_;
+ }
+ if (num_contenders_.LoadSequentiallyConsistent() != 0) {
+ LOG(shutting_down ? WARNING : FATAL) << "unexpectedly found a contender on mutex " << name_;
+ }
}
#else
// We can't use CHECK_MUTEX_CALL here because on shutdown a suspended daemon thread
@@ -338,8 +347,6 @@
errno = rc;
// TODO: should we just not log at all if shutting down? this could be the logging mutex!
MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_);
- Runtime* runtime = Runtime::Current();
- bool shutting_down = (runtime == NULL) || runtime->IsShuttingDownLocked();
PLOG(shutting_down ? WARNING : FATAL) << "pthread_mutex_destroy failed for " << name_;
}
#endif
@@ -430,7 +437,18 @@
}
void Mutex::ExclusiveUnlock(Thread* self) {
- DCHECK(self == NULL || self == Thread::Current());
+ if (kIsDebugBuild && self != nullptr && self != Thread::Current()) {
+ std::string name1 = "<null>";
+ std::string name2 = "<null>";
+ if (self != nullptr) {
+ self->GetThreadName(name1);
+ }
+ if (Thread::Current() != nullptr) {
+ Thread::Current()->GetThreadName(name2);
+ }
+ LOG(FATAL) << GetName() << " level=" << level_ << " self=" << name1
+ << " Thread::Current()=" << name2;
+ }
AssertHeld(self);
DCHECK_NE(exclusive_owner_, 0U);
recursion_count_--;
@@ -598,7 +616,7 @@
#if ART_USE_FUTEXES
bool done = false;
timespec end_abs_ts;
- InitTimeSpec(true, CLOCK_REALTIME, ms, ns, &end_abs_ts);
+ InitTimeSpec(true, CLOCK_MONOTONIC, ms, ns, &end_abs_ts);
do {
int32_t cur_state = state_.LoadRelaxed();
if (cur_state == 0) {
@@ -607,7 +625,7 @@
} else {
// Failed to acquire, hang up.
timespec now_abs_ts;
- InitTimeSpec(true, CLOCK_REALTIME, 0, 0, &now_abs_ts);
+ InitTimeSpec(true, CLOCK_MONOTONIC, 0, 0, &now_abs_ts);
timespec rel_ts;
if (ComputeRelativeTimeSpec(&rel_ts, end_abs_ts, now_abs_ts)) {
return false; // Timed out.
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 9c93cc6..745b209 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -60,6 +60,7 @@
kThreadSuspendCountLock,
kAbortLock,
kJdwpSocketLock,
+ kRegionSpaceRegionLock,
kReferenceQueueSoftReferencesLock,
kReferenceQueuePhantomReferencesLock,
kReferenceQueueFinalizerReferencesLock,
@@ -70,6 +71,7 @@
kRosAllocBracketLock,
kRosAllocBulkFreeLock,
kAllocSpaceLock,
+ kBumpPointerSpaceBlockLock,
kDexFileMethodInlinerLock,
kDexFileToMethodInlinerMapLock,
kMarkSweepMarkStackLock,
diff --git a/runtime/base/stl_util.h b/runtime/base/stl_util.h
index ff9f40c..3c5565c 100644
--- a/runtime/base/stl_util.h
+++ b/runtime/base/stl_util.h
@@ -57,8 +57,8 @@
// If container is NULL, this function is a no-op.
//
// As an alternative to calling STLDeleteElements() directly, consider
-// ElementDeleter (defined below), which ensures that your container's elements
-// are deleted when the ElementDeleter goes out of scope.
+// using a container of std::unique_ptr, which ensures that your container's
+// elements are deleted when the container goes out of scope.
template <class T>
void STLDeleteElements(T *container) {
if (!container) return;
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index 6e5e7a1..f272d88 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -178,10 +178,16 @@
return fd_ >= 0;
}
-bool FdFile::ReadFully(void* buffer, size_t byte_count) {
+static ssize_t ReadIgnoreOffset(int fd, void *buf, size_t count, off_t offset) {
+ DCHECK_EQ(offset, 0);
+ return read(fd, buf, count);
+}
+
+template <ssize_t (*read_func)(int, void*, size_t, off_t)>
+static bool ReadFullyGeneric(int fd, void* buffer, size_t byte_count, size_t offset) {
char* ptr = static_cast<char*>(buffer);
while (byte_count > 0) {
- ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd_, ptr, byte_count));
+ ssize_t bytes_read = TEMP_FAILURE_RETRY(read_func(fd, ptr, byte_count, offset));
if (bytes_read <= 0) {
// 0: end of file
// -1: error
@@ -189,10 +195,19 @@
}
byte_count -= bytes_read; // Reduce the number of remaining bytes.
ptr += bytes_read; // Move the buffer forward.
+ offset += static_cast<size_t>(bytes_read); // Move the offset forward.
}
return true;
}
+bool FdFile::ReadFully(void* buffer, size_t byte_count) {
+ return ReadFullyGeneric<ReadIgnoreOffset>(fd_, buffer, byte_count, 0);
+}
+
+bool FdFile::PreadFully(void* buffer, size_t byte_count, size_t offset) {
+ return ReadFullyGeneric<pread>(fd_, buffer, byte_count, offset);
+}
+
bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
const char* ptr = static_cast<const char*>(buffer);
moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
@@ -241,4 +256,8 @@
return (flush_result != 0) ? flush_result : close_result;
}
+void FdFile::MarkUnchecked() {
+ guard_state_ = GuardState::kNoCheck;
+}
+
} // namespace unix_file
diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h
index 8db2ee4..d51fbd6 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/runtime/base/unix_file/fd_file.h
@@ -74,6 +74,7 @@
}
void DisableAutoClose();
bool ReadFully(void* buffer, size_t byte_count) WARN_UNUSED;
+ bool PreadFully(void* buffer, size_t byte_count, size_t offset) WARN_UNUSED;
bool WriteFully(const void* buffer, size_t byte_count) WARN_UNUSED;
// This enum is public so that we can define the << operator over it.
@@ -84,6 +85,9 @@
kNoCheck // Do not check for the current file instance.
};
+ // WARNING: Only use this when you know what you're doing!
+ void MarkUnchecked();
+
protected:
// If the guard state indicates checking (!=kNoCheck), go to the target state "target". Print the
// given warning if the current state is or exceeds warn_threshold.
diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc
index a7e5b96..388f717 100644
--- a/runtime/base/unix_file/fd_file_test.cc
+++ b/runtime/base/unix_file/fd_file_test.cc
@@ -76,4 +76,38 @@
EXPECT_FALSE(file.ReadFully(&buffer, 4));
}
+template <size_t Size>
+static void NullTerminateCharArray(char (&array)[Size]) {
+ array[Size - 1] = '\0';
+}
+
+TEST_F(FdFileTest, ReadFullyWithOffset) {
+ // New scratch file, zero-length.
+ art::ScratchFile tmp;
+ FdFile file;
+ ASSERT_TRUE(file.Open(tmp.GetFilename(), O_RDWR));
+ EXPECT_GE(file.Fd(), 0);
+ EXPECT_TRUE(file.IsOpened());
+
+ char ignore_prefix[20] = {'a', };
+ NullTerminateCharArray(ignore_prefix);
+ char read_suffix[10] = {'b', };
+ NullTerminateCharArray(read_suffix);
+
+ off_t offset = 0;
+ // Write scratch data to file that we can read back into.
+ EXPECT_TRUE(file.Write(ignore_prefix, sizeof(ignore_prefix), offset));
+ offset += sizeof(ignore_prefix);
+ EXPECT_TRUE(file.Write(read_suffix, sizeof(read_suffix), offset));
+
+ ASSERT_EQ(file.Flush(), 0);
+
+ // Reading at an offset should only produce 'bbbb...', since we ignore the 'aaa...' prefix.
+ char buffer[sizeof(read_suffix)];
+ EXPECT_TRUE(file.PreadFully(buffer, sizeof(read_suffix), offset));
+ EXPECT_STREQ(&read_suffix[0], &buffer[0]);
+
+ ASSERT_EQ(file.Close(), 0);
+}
+
} // namespace unix_file
diff --git a/runtime/base/unix_file/null_file.cc b/runtime/base/unix_file/null_file.cc
deleted file mode 100644
index 322c25a..0000000
--- a/runtime/base/unix_file/null_file.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "base/unix_file/null_file.h"
-#include <errno.h>
-
-namespace unix_file {
-
-NullFile::NullFile() {
-}
-
-NullFile::~NullFile() {
-}
-
-int NullFile::Close() {
- return 0;
-}
-
-int NullFile::Flush() {
- return 0;
-}
-
-int64_t NullFile::Read(char* buf ATTRIBUTE_UNUSED, int64_t byte_count ATTRIBUTE_UNUSED,
- int64_t offset) const {
- if (offset < 0) {
- return -EINVAL;
- }
- return 0;
-}
-
-int NullFile::SetLength(int64_t new_length) {
- if (new_length < 0) {
- return -EINVAL;
- }
- return 0;
-}
-
-int64_t NullFile::GetLength() const {
- return 0;
-}
-
-int64_t NullFile::Write(const char* buf ATTRIBUTE_UNUSED, int64_t byte_count ATTRIBUTE_UNUSED,
- int64_t offset) {
- if (offset < 0) {
- return -EINVAL;
- }
- return byte_count;
-}
-
-} // namespace unix_file
diff --git a/runtime/base/unix_file/null_file.h b/runtime/base/unix_file/null_file.h
deleted file mode 100644
index 3394731..0000000
--- a/runtime/base/unix_file/null_file.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_BASE_UNIX_FILE_NULL_FILE_H_
-#define ART_RUNTIME_BASE_UNIX_FILE_NULL_FILE_H_
-
-#include "base/unix_file/random_access_file.h"
-#include "base/macros.h"
-
-namespace unix_file {
-
-// A RandomAccessFile implementation equivalent to /dev/null. Writes are
-// discarded, and there's no data to be read. Callers could use FdFile in
-// conjunction with /dev/null, but that's not portable and costs a file
-// descriptor. NullFile is "free".
-//
-// Thread safe.
-class NullFile : public RandomAccessFile {
- public:
- NullFile();
- virtual ~NullFile();
-
- // RandomAccessFile API.
- virtual int Close();
- virtual int Flush();
- virtual int64_t Read(char* buf, int64_t byte_count, int64_t offset) const;
- virtual int SetLength(int64_t new_length);
- virtual int64_t GetLength() const;
- virtual int64_t Write(const char* buf, int64_t byte_count, int64_t offset);
-
- private:
- DISALLOW_COPY_AND_ASSIGN(NullFile);
-};
-
-} // namespace unix_file
-
-#endif // ART_RUNTIME_BASE_UNIX_FILE_NULL_FILE_H_
diff --git a/runtime/base/unix_file/null_file_test.cc b/runtime/base/unix_file/null_file_test.cc
deleted file mode 100644
index 410fdfc..0000000
--- a/runtime/base/unix_file/null_file_test.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "base/unix_file/null_file.h"
-
-#include <errno.h>
-
-#include "gtest/gtest.h"
-
-namespace unix_file {
-
-class NullFileTest : public testing::Test { };
-
-TEST_F(NullFileTest, Read) {
- NullFile f;
- char buf[256];
- // You can't read a negative number of bytes...
- ASSERT_EQ(-EINVAL, f.Read(buf, 0, -1));
- // ...but everything else is fine (though you'll get no data).
- ASSERT_EQ(0, f.Read(buf, 128, 0));
- ASSERT_EQ(0, f.Read(buf, 128, 128));
-}
-
-TEST_F(NullFileTest, SetLength) {
- NullFile f;
- // You can't set a negative length...
- ASSERT_EQ(-EINVAL, f.SetLength(-1));
- // ...but everything else is fine.
- ASSERT_EQ(0, f.SetLength(0));
- ASSERT_EQ(0, f.SetLength(128));
-}
-
-TEST_F(NullFileTest, GetLength) {
- const std::string content("hello");
- NullFile f;
- // The length is always 0.
- ASSERT_EQ(0, f.GetLength());
- ASSERT_EQ(content.size(), static_cast<uint64_t>(f.Write(content.data(), content.size(), 0)));
- ASSERT_EQ(0, f.GetLength());
-}
-
-TEST_F(NullFileTest, Write) {
- const std::string content("hello");
- NullFile f;
- // You can't write at a negative offset...
- ASSERT_EQ(-EINVAL, f.Write(content.data(), content.size(), -128));
- // But you can write anywhere else...
- ASSERT_EQ(content.size(), static_cast<uint64_t>(f.Write(content.data(), content.size(), 0)));
- ASSERT_EQ(content.size(), static_cast<uint64_t>(f.Write(content.data(), content.size(), 128)));
- // ...though the file will remain empty.
- ASSERT_EQ(0, f.GetLength());
-}
-
-} // namespace unix_file
diff --git a/runtime/base/unix_file/random_access_file_utils_test.cc b/runtime/base/unix_file/random_access_file_utils_test.cc
deleted file mode 100644
index 9457d22..0000000
--- a/runtime/base/unix_file/random_access_file_utils_test.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "base/unix_file/random_access_file_utils.h"
-#include "base/unix_file/fd_file.h"
-#include "base/unix_file/string_file.h"
-#include "gtest/gtest.h"
-
-namespace unix_file {
-
-class RandomAccessFileUtilsTest : public testing::Test { };
-
-TEST_F(RandomAccessFileUtilsTest, CopyFile) {
- StringFile src;
- StringFile dst;
-
- const std::string content("hello");
- src.Assign(content);
- ASSERT_EQ(src.ToStringPiece(), content);
- ASSERT_EQ(dst.ToStringPiece(), "");
-
- ASSERT_TRUE(CopyFile(src, &dst));
- ASSERT_EQ(src.ToStringPiece(), dst.ToStringPiece());
-}
-
-TEST_F(RandomAccessFileUtilsTest, BadSrc) {
- FdFile src(-1, false);
- StringFile dst;
- ASSERT_FALSE(CopyFile(src, &dst));
-}
-
-TEST_F(RandomAccessFileUtilsTest, BadDst) {
- StringFile src;
- FdFile dst(-1, false);
-
- // We need some source content to trigger a write.
- // Copying an empty file is a no-op.
- src.Assign("hello");
-
- ASSERT_FALSE(CopyFile(src, &dst));
-}
-
-} // namespace unix_file
diff --git a/runtime/base/unix_file/string_file.cc b/runtime/base/unix_file/string_file.cc
deleted file mode 100644
index ff0d0fa..0000000
--- a/runtime/base/unix_file/string_file.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "base/unix_file/string_file.h"
-#include <errno.h>
-#include <algorithm>
-#include "base/logging.h"
-
-namespace unix_file {
-
-StringFile::StringFile() {
-}
-
-StringFile::~StringFile() {
-}
-
-int StringFile::Close() {
- return 0;
-}
-
-int StringFile::Flush() {
- return 0;
-}
-
-int64_t StringFile::Read(char *buf, int64_t byte_count, int64_t offset) const {
- CHECK(buf);
- CHECK_GE(byte_count, 0);
-
- if (offset < 0) {
- return -EINVAL;
- }
-
- const int64_t available_bytes = std::min(byte_count, GetLength() - offset);
- if (available_bytes < 0) {
- return 0; // Not an error, but nothing for us to do, either.
- }
- memcpy(buf, data_.data() + offset, available_bytes);
- return available_bytes;
-}
-
-int StringFile::SetLength(int64_t new_length) {
- if (new_length < 0) {
- return -EINVAL;
- }
- data_.resize(new_length);
- return 0;
-}
-
-int64_t StringFile::GetLength() const {
- return data_.size();
-}
-
-int64_t StringFile::Write(const char *buf, int64_t byte_count, int64_t offset) {
- CHECK(buf);
- CHECK_GE(byte_count, 0);
-
- if (offset < 0) {
- return -EINVAL;
- }
-
- if (byte_count == 0) {
- return 0;
- }
-
- // FUSE seems happy to allow writes past the end. (I'd guess it doesn't
- // synthesize a write of zero bytes so that we're free to implement sparse
- // files.) GNU as(1) seems to require such writes. Those files are small.
- const int64_t bytes_past_end = offset - GetLength();
- if (bytes_past_end > 0) {
- data_.append(bytes_past_end, '\0');
- }
-
- data_.replace(offset, byte_count, buf, byte_count);
- return byte_count;
-}
-
-void StringFile::Assign(const art::StringPiece &new_data) {
- data_.assign(new_data.data(), new_data.size());
-}
-
-const art::StringPiece StringFile::ToStringPiece() const {
- return data_;
-}
-
-} // namespace unix_file
diff --git a/runtime/base/unix_file/string_file.h b/runtime/base/unix_file/string_file.h
deleted file mode 100644
index 26904f8..0000000
--- a/runtime/base/unix_file/string_file.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_BASE_UNIX_FILE_STRING_FILE_H_
-#define ART_RUNTIME_BASE_UNIX_FILE_STRING_FILE_H_
-
-#include <stdint.h>
-
-#include <string>
-
-#include "base/macros.h"
-#include "base/stringpiece.h"
-#include "base/unix_file/random_access_file.h"
-
-namespace unix_file {
-
-// A RandomAccessFile implementation backed by a std::string. (That is, all data is
-// kept in memory.)
-//
-// Not thread safe.
-class StringFile : public RandomAccessFile {
- public:
- StringFile();
- virtual ~StringFile();
-
- // RandomAccessFile API.
- virtual int Close();
- virtual int Flush();
- virtual int64_t Read(char* buf, int64_t byte_count, int64_t offset) const;
- virtual int SetLength(int64_t new_length);
- virtual int64_t GetLength() const;
- virtual int64_t Write(const char* buf, int64_t byte_count, int64_t offset);
-
- // Bonus API.
- void Assign(const art::StringPiece& new_data);
- const art::StringPiece ToStringPiece() const;
-
- private:
- std::string data_;
-
- DISALLOW_COPY_AND_ASSIGN(StringFile);
-};
-
-} // namespace unix_file
-
-#endif // ART_RUNTIME_BASE_UNIX_FILE_STRING_FILE_H_
diff --git a/runtime/base/unix_file/string_file_test.cc b/runtime/base/unix_file/string_file_test.cc
deleted file mode 100644
index 8821461..0000000
--- a/runtime/base/unix_file/string_file_test.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "base/unix_file/string_file.h"
-#include "base/unix_file/random_access_file_test.h"
-#include "gtest/gtest.h"
-
-namespace unix_file {
-
-class StringFileTest : public RandomAccessFileTest {
- protected:
- virtual RandomAccessFile* MakeTestFile() {
- return new StringFile;
- }
-};
-
-TEST_F(StringFileTest, Read) {
- TestRead();
-}
-
-TEST_F(StringFileTest, SetLength) {
- TestSetLength();
-}
-
-TEST_F(StringFileTest, Write) {
- TestWrite();
-}
-
-} // namespace unix_file
diff --git a/runtime/base/variant_map.h b/runtime/base/variant_map.h
new file mode 100644
index 0000000..cf7977e
--- /dev/null
+++ b/runtime/base/variant_map.h
@@ -0,0 +1,453 @@
+/*
+ * 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_RUNTIME_BASE_VARIANT_MAP_H_
+#define ART_RUNTIME_BASE_VARIANT_MAP_H_
+
+#include <memory.h>
+#include <map>
+#include <utility>
+
+namespace art {
+
+//
+// A variant map is a heterogenous, type safe key->value map. It allows
+// for multiple different value types to be stored dynamically in the same map.
+//
+// It provides the following interface in a nutshell:
+//
+// struct VariantMap {
+// template <typename TValue>
+// TValue* Get(Key<T> key); // nullptr if the value was never set, otherwise the value.
+//
+// template <typename TValue>
+// void Set(Key<T> key, TValue value);
+// };
+//
+// Since the key is strongly typed at compile-time, it is impossible to accidentally
+// read/write a value with a different type than the key at either compile-time or run-time.
+//
+// Do not use VariantMap/VariantMapKey directly. Instead subclass each of them and use
+// the subclass, for example:
+//
+// template <typename TValue>
+// struct FruitMapKey : VariantMapKey<TValue> {
+// FruitMapKey() {}
+// };
+//
+// struct FruitMap : VariantMap<FruitMap, FruitMapKey> {
+// // This 'using' line is necessary to inherit the variadic constructor.
+// using VariantMap<FruitMap, FruitMapKey>::VariantMap;
+//
+// // Make the next '4' usages of Key slightly shorter to type.
+// template <typename TValue>
+// using Key = FruitMapKey<TValue>;
+//
+// static const Key<int> Apple;
+// static const Key<double> Orange;
+// static const Key<std::string> Banana;
+// };
+//
+// const FruitMap::Key<int> FruitMap::Apple;
+// const FruitMap::Key<double> FruitMap::Orange;
+// const FruitMap::Key<std::string> Banana;
+//
+// See variant_map_test.cc for more examples.
+//
+
+// Implementation details for VariantMap.
+namespace detail {
+ // Allocate a unique counter value each time it's called.
+ struct VariantMapKeyCounterAllocator {
+ static size_t AllocateCounter() {
+ static size_t counter = 0;
+ counter++;
+
+ return counter;
+ }
+ };
+
+ // Type-erased version of VariantMapKey<T>
+ struct VariantMapKeyRaw {
+ // TODO: this may need to call a virtual function to support string comparisons
+ bool operator<(const VariantMapKeyRaw& other) const {
+ return key_counter_ < other.key_counter_;
+ }
+
+ // The following functions need to be virtual since we don't know the compile-time type anymore:
+
+ // Clone the key, creating a copy of the contents.
+ virtual VariantMapKeyRaw* Clone() const = 0;
+
+ // Delete a value whose runtime type is that of the non-erased key's TValue.
+ virtual void ValueDelete(void* value) const = 0;
+
+ // Clone a value whose runtime type is that of the non-erased key's TValue.
+ virtual void* ValueClone(void* value) const = 0;
+
+ // Compare one key to another (same as operator<).
+ virtual bool Compare(const VariantMapKeyRaw* other) const {
+ if (other == nullptr) {
+ return false;
+ }
+ return key_counter_ < other->key_counter_;
+ }
+
+ virtual ~VariantMapKeyRaw() {}
+
+ protected:
+ VariantMapKeyRaw()
+ : key_counter_(VariantMapKeyCounterAllocator::AllocateCounter()) {}
+ // explicit VariantMapKeyRaw(size_t counter)
+ // : key_counter_(counter) {}
+
+ size_t GetCounter() const {
+ return key_counter_;
+ }
+
+ protected:
+ // Avoid the object slicing problem; use Clone() instead.
+ VariantMapKeyRaw(const VariantMapKeyRaw& other) = default;
+ VariantMapKeyRaw(VariantMapKeyRaw&& other) = default;
+
+ private:
+ size_t key_counter_; // Runtime type ID. Unique each time a new type is reified.
+ };
+} // namespace detail
+
+// The base type for keys used by the VariantMap. Users must subclass this type.
+template <typename TValue>
+struct VariantMapKey : detail::VariantMapKeyRaw {
+ // Instantiate a default value for this key. If an explicit default value was provided
+ // then that is used. Otherwise, the default value for the type TValue{} is returned.
+ TValue CreateDefaultValue() const {
+ if (default_value_ == nullptr) {
+ return TValue{}; // NOLINT [readability/braces] [4]
+ } else {
+ return TValue(*default_value_);
+ }
+ }
+
+ protected:
+ // explicit VariantMapKey(size_t counter) : detail::VariantMapKeyRaw(counter) {}
+ explicit VariantMapKey(const TValue& default_value)
+ : default_value_(std::make_shared<TValue>(default_value)) {}
+ explicit VariantMapKey(TValue&& default_value)
+ : default_value_(std::make_shared<TValue>(default_value)) {}
+ VariantMapKey() {}
+ virtual ~VariantMapKey() {}
+
+ private:
+ virtual VariantMapKeyRaw* Clone() const {
+ return new VariantMapKey<TValue>(*this);
+ }
+
+ virtual void* ValueClone(void* value) const {
+ if (value == nullptr) {
+ return nullptr;
+ }
+
+ TValue* strong_value = reinterpret_cast<TValue*>(value);
+ return new TValue(*strong_value);
+ }
+
+ virtual void ValueDelete(void* value) const {
+ if (value == nullptr) {
+ return;
+ }
+
+ // Smartly invoke the proper delete/delete[]/etc
+ const std::default_delete<TValue> deleter = std::default_delete<TValue>();
+ deleter(reinterpret_cast<TValue*>(value));
+ }
+
+ VariantMapKey(const VariantMapKey& other) = default;
+ VariantMapKey(VariantMapKey&& other) = default;
+
+ template <typename Base, template <typename TV> class TKey> friend struct VariantMap;
+
+ // Store a prototype of the key's default value, for usage with VariantMap::GetOrDefault
+ std::shared_ptr<TValue> default_value_;
+};
+
+// Implementation details for a stringified VariantMapStringKey.
+namespace detail {
+ struct VariantMapStringKeyRegistry {
+ // TODO
+ };
+} // namespace detail
+
+// Alternative base type for all keys used by VariantMap, supports runtime strings as the name.
+template <typename TValue>
+struct VariantMapStringKey : VariantMapKey<TValue> {
+ explicit VariantMapStringKey(const char* name)
+ : // VariantMapKey(/*std::hash<std::string>()(name)*/),
+ name_(name) {
+ }
+
+ private:
+ const char* name_;
+};
+
+// A variant map allows type-safe heteregeneous key->value mappings.
+// All possible key types must be specified at compile-time. Values may be added/removed
+// at runtime.
+template <typename Base, template <typename TV> class TKey>
+struct VariantMap {
+ // Allow users of this static interface to use the key type.
+ template <typename TValue>
+ using Key = TKey<TValue>;
+
+ // Look up the value from the key. The pointer becomes invalid if this key is overwritten/removed.
+ // A null value is returned only when the key does not exist in this map.
+ template <typename TValue>
+ const TValue* Get(const TKey<TValue>& key) const {
+ return GetValuePtr(key);
+ }
+
+ // Look up the value from the key. The pointer becomes invalid if this key is overwritten/removed.
+ // A null value is returned only when the key does not exist in this map.
+ template <typename TValue>
+ TValue* Get(const TKey<TValue>& key) {
+ return GetValuePtr(key);
+ }
+
+ // Lookup the value from the key. If it was not set in the map, return the default value.
+ // The default value is either the key's default, or TValue{} if the key doesn't have a default.
+ template <typename TValue>
+ TValue GetOrDefault(const TKey<TValue>& key) const {
+ auto* ptr = Get(key);
+ return (ptr == nullptr) ? key.CreateDefaultValue() : *ptr;
+ }
+
+ private:
+ // TODO: move to detail, or make it more generic like a ScopeGuard(function)
+ template <typename TValue>
+ struct ScopedRemove {
+ ScopedRemove(VariantMap& map, const TKey<TValue>& key) : map_(map), key_(key) {}
+ ~ScopedRemove() {
+ map_.Remove(key_);
+ }
+
+ VariantMap& map_;
+ const TKey<TValue>& key_;
+ };
+
+ public:
+ // Release the value from the key. If it was not set in the map, returns the default value.
+ // If the key was set, it is removed as a side effect.
+ template <typename TValue>
+ TValue ReleaseOrDefault(const TKey<TValue>& key) {
+ ScopedRemove<TValue> remove_on_return(*this, key);
+
+ TValue* ptr = Get(key);
+ if (ptr != nullptr) {
+ return std::move(*ptr);
+ } else {
+ TValue default_value = key.CreateDefaultValue();
+ return std::move(default_value);
+ }
+ }
+
+ // See if a value is stored for this key.
+ template <typename TValue>
+ bool Exists(const TKey<TValue>& key) const {
+ return GetKeyValueIterator(key) != storage_map_.end();
+ }
+
+ // Set a value for a given key, overwriting the previous value if any.
+ template <typename TValue>
+ void Set(const TKey<TValue>& key, const TValue& value) {
+ Remove(key);
+ storage_map_.insert({{key.Clone(), new TValue(value)}});
+ }
+
+ // Set a value for a given key, only if there was no previous value before.
+ // Returns true if the value was set, false if a previous value existed.
+ template <typename TValue>
+ bool SetIfMissing(const TKey<TValue>& key, const TValue& value) {
+ TValue* ptr = Get(key);
+ if (ptr == nullptr) {
+ Set(key, value);
+ return true;
+ }
+ return false;
+ }
+
+ // Remove the value for a given key, or a no-op if there was no previously set value.
+ template <typename TValue>
+ void Remove(const TKey<TValue>& key) {
+ StaticAssertKeyType<TValue>();
+
+ auto&& it = GetKeyValueIterator(key);
+ if (it != storage_map_.end()) {
+ key.ValueDelete(it->second);
+ delete it->first;
+ storage_map_.erase(it);
+ }
+ }
+
+ // Remove all key/value pairs.
+ void Clear() {
+ DeleteStoredValues();
+ storage_map_.clear();
+ }
+
+ // How many key/value pairs are stored in this map.
+ size_t Size() const {
+ return storage_map_.size();
+ }
+
+ // Construct an empty map.
+ explicit VariantMap() {}
+
+ template <typename ... TKeyValue>
+ explicit VariantMap(const TKeyValue& ... key_value_list) {
+ static_assert(sizeof...(TKeyValue) % 2 == 0, "Must be an even number of key/value elements");
+ InitializeParameters(key_value_list...);
+ }
+
+ // Create a new map from an existing map, copying all the key/value pairs.
+ VariantMap(const VariantMap& other) {
+ operator=(other);
+ }
+
+ // Copy the key/value pairs from the other map into this one. Existing key/values are cleared.
+ VariantMap& operator=(const VariantMap& other) {
+ if (this == &other) {
+ return *this;
+ }
+
+ Clear();
+
+ for (auto&& kv_pair : other.storage_map_) {
+ const detail::VariantMapKeyRaw* raw_key_other = kv_pair.first;
+ void* value = kv_pair.second;
+
+ detail::VariantMapKeyRaw* cloned_raw_key = raw_key_other->Clone();
+ void* cloned_value = raw_key_other->ValueClone(value);
+
+ storage_map_.insert({{ cloned_raw_key, cloned_value }});
+ }
+
+ return *this;
+ }
+
+ // Create a new map by moving an existing map into this one. The other map becomes empty.
+ VariantMap(VariantMap&& other) {
+ operator=(std::forward<VariantMap>(other));
+ }
+
+ // Move the existing map's key/value pairs into this one. The other map becomes empty.
+ VariantMap& operator=(VariantMap&& other) {
+ if (this != &other) {
+ Clear();
+ storage_map_.swap(other.storage_map_);
+ other.storage_map_.clear();
+ }
+ return *this;
+ }
+
+ ~VariantMap() {
+ DeleteStoredValues();
+ }
+
+ private:
+ void InitializeParameters() {}
+
+ template <typename TK, typename TValue, typename ... Rest>
+ void InitializeParameters(const TK& key, const TValue& value, const Rest& ... rest) {
+ static_assert(
+ std::is_same<TK, TKey<TValue>>::value, "The 0th/2nd/4th/etc parameters must be a key");
+
+ const TKey<TValue>& key_refined = key;
+
+ Set(key_refined, value);
+ InitializeParameters(rest...);
+ }
+
+ // Custom key comparator for std::map, needed since we are storing raw pointers as the keys.
+ struct KeyComparator {
+ bool operator()(const detail::VariantMapKeyRaw* lhs,
+ const detail::VariantMapKeyRaw* rhs) const {
+ if (lhs == nullptr) {
+ return lhs != rhs;
+ }
+
+ return lhs->Compare(rhs);
+ }
+ };
+
+ // Map of key pointers to value pointers. Pointers are never null.
+ using StorageMap = std::map<const detail::VariantMapKeyRaw*, void*, KeyComparator>;
+
+ template <typename TValue>
+ typename StorageMap::iterator GetKeyValueIterator(const TKey<TValue>& key) {
+ StaticAssertKeyType<TValue>();
+
+ const TKey<TValue>* key_ptr = &key;
+ const detail::VariantMapKeyRaw* raw_ptr = key_ptr;
+ return storage_map_.find(raw_ptr);
+ }
+
+ template <typename TValue>
+ typename StorageMap::const_iterator GetKeyValueIterator(const TKey<TValue>& key) const {
+ StaticAssertKeyType<TValue>();
+
+ const TKey<TValue>* key_ptr = &key;
+ const detail::VariantMapKeyRaw* raw_ptr = key_ptr;
+ return storage_map_.find(raw_ptr);
+ }
+
+ template <typename TValue>
+ TValue* GetValuePtr(const TKey<TValue>& key) {
+ return const_cast<TValue*>(GetValueConstPtr(key));
+ }
+
+ template <typename TValue>
+ const TValue* GetValuePtr(const TKey<TValue>& key) const {
+ return GetValueConstPtr(key);
+ }
+
+ template <typename TValue>
+ const TValue* GetValueConstPtr(const TKey<TValue>& key) const {
+ auto&& it = GetKeyValueIterator(key);
+ if (it == storage_map_.end()) {
+ return nullptr;
+ }
+
+ return reinterpret_cast<const TValue*>(it->second);
+ }
+
+ template <typename TValue>
+ static void StaticAssertKeyType() {
+ static_assert(std::is_base_of<VariantMapKey<TValue>, TKey<TValue>>::value,
+ "The provided key type (TKey) must be a subclass of VariantMapKey");
+ }
+
+ void DeleteStoredValues() {
+ for (auto&& kv_pair : storage_map_) {
+ kv_pair.first->ValueDelete(kv_pair.second);
+ delete kv_pair.first;
+ }
+ }
+
+ StorageMap storage_map_;
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_VARIANT_MAP_H_
diff --git a/runtime/base/variant_map_test.cc b/runtime/base/variant_map_test.cc
new file mode 100644
index 0000000..827de46
--- /dev/null
+++ b/runtime/base/variant_map_test.cc
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+#include "variant_map.h"
+#include "gtest/gtest.h"
+
+#define EXPECT_NULL(expected) EXPECT_EQ(reinterpret_cast<const void*>(expected), \
+ reinterpret_cast<void*>(NULL));
+
+namespace art {
+
+namespace {
+ template <typename TValue>
+ struct FruitMapKey : VariantMapKey<TValue> {
+ FruitMapKey() {}
+ };
+
+ struct FruitMap : VariantMap<FruitMap, FruitMapKey> {
+ // This 'using' line is necessary to inherit the variadic constructor.
+ using VariantMap<FruitMap, FruitMapKey>::VariantMap;
+
+ // Make the next '4' usages of Key slightly shorter to type.
+ template <typename TValue>
+ using Key = FruitMapKey<TValue>;
+
+ static const Key<int> Apple;
+ static const Key<double> Orange;
+ };
+
+ const FruitMap::Key<int> FruitMap::Apple;
+ const FruitMap::Key<double> FruitMap::Orange;
+} // namespace
+
+TEST(VariantMaps, BasicReadWrite) {
+ FruitMap fm;
+
+ EXPECT_NULL(fm.Get(FruitMap::Apple));
+ EXPECT_FALSE(fm.Exists(FruitMap::Apple));
+ EXPECT_NULL(fm.Get(FruitMap::Orange));
+ EXPECT_FALSE(fm.Exists(FruitMap::Orange));
+
+ fm.Set(FruitMap::Apple, 1);
+ EXPECT_NULL(fm.Get(FruitMap::Orange));
+ EXPECT_EQ(1, *fm.Get(FruitMap::Apple));
+ EXPECT_TRUE(fm.Exists(FruitMap::Apple));
+
+ fm.Set(FruitMap::Apple, 5);
+ EXPECT_NULL(fm.Get(FruitMap::Orange));
+ EXPECT_EQ(5, *fm.Get(FruitMap::Apple));
+ EXPECT_TRUE(fm.Exists(FruitMap::Apple));
+
+ fm.Set(FruitMap::Orange, 555.0);
+ EXPECT_EQ(5, *fm.Get(FruitMap::Apple));
+ EXPECT_DOUBLE_EQ(555.0, *fm.Get(FruitMap::Orange));
+ EXPECT_EQ(size_t(2), fm.Size());
+
+ fm.Remove(FruitMap::Apple);
+ EXPECT_FALSE(fm.Exists(FruitMap::Apple));
+
+ fm.Clear();
+ EXPECT_EQ(size_t(0), fm.Size());
+ EXPECT_FALSE(fm.Exists(FruitMap::Orange));
+}
+
+TEST(VariantMaps, RuleOfFive) {
+ // Test empty constructor
+ FruitMap fmEmpty;
+ EXPECT_EQ(size_t(0), fmEmpty.Size());
+
+ // Test empty constructor
+ FruitMap fmFilled;
+ fmFilled.Set(FruitMap::Apple, 1);
+ fmFilled.Set(FruitMap::Orange, 555.0);
+ EXPECT_EQ(size_t(2), fmFilled.Size());
+
+ // Test copy constructor
+ FruitMap fmEmptyCopy(fmEmpty);
+ EXPECT_EQ(size_t(0), fmEmptyCopy.Size());
+
+ // Test copy constructor
+ FruitMap fmFilledCopy(fmFilled);
+ EXPECT_EQ(size_t(2), fmFilledCopy.Size());
+ EXPECT_EQ(*fmFilled.Get(FruitMap::Apple), *fmFilledCopy.Get(FruitMap::Apple));
+ EXPECT_DOUBLE_EQ(*fmFilled.Get(FruitMap::Orange), *fmFilledCopy.Get(FruitMap::Orange));
+
+ // Test operator=
+ FruitMap fmFilledCopy2;
+ fmFilledCopy2 = fmFilled;
+ EXPECT_EQ(size_t(2), fmFilledCopy2.Size());
+ EXPECT_EQ(*fmFilled.Get(FruitMap::Apple), *fmFilledCopy2.Get(FruitMap::Apple));
+ EXPECT_DOUBLE_EQ(*fmFilled.Get(FruitMap::Orange), *fmFilledCopy2.Get(FruitMap::Orange));
+
+ // Test move constructor
+ FruitMap fmMoved(std::move(fmFilledCopy));
+ EXPECT_EQ(size_t(0), fmFilledCopy.Size());
+ EXPECT_EQ(size_t(2), fmMoved.Size());
+ EXPECT_EQ(*fmFilled.Get(FruitMap::Apple), *fmMoved.Get(FruitMap::Apple));
+ EXPECT_DOUBLE_EQ(*fmFilled.Get(FruitMap::Orange), *fmMoved.Get(FruitMap::Orange));
+
+ // Test operator= move
+ FruitMap fmMoved2;
+ fmMoved2.Set(FruitMap::Apple, 12345); // This value will be clobbered after the move
+
+ fmMoved2 = std::move(fmFilledCopy2);
+ EXPECT_EQ(size_t(0), fmFilledCopy2.Size());
+ EXPECT_EQ(size_t(2), fmMoved2.Size());
+ EXPECT_EQ(*fmFilled.Get(FruitMap::Apple), *fmMoved2.Get(FruitMap::Apple));
+ EXPECT_DOUBLE_EQ(*fmFilled.Get(FruitMap::Orange), *fmMoved2.Get(FruitMap::Orange));
+}
+
+TEST(VariantMaps, VariadicConstructors) {
+ // Variadic constructor, 1 kv/pair
+ FruitMap fmApple(FruitMap::Apple, 12345);
+ EXPECT_EQ(size_t(1), fmApple.Size());
+ EXPECT_EQ(12345, *fmApple.Get(FruitMap::Apple));
+
+ // Variadic constructor, 2 kv/pair
+ FruitMap fmAppleAndOrange(FruitMap::Apple, 12345,
+ FruitMap::Orange, 100.0);
+ EXPECT_EQ(size_t(2), fmAppleAndOrange.Size());
+ EXPECT_EQ(12345, *fmAppleAndOrange.Get(FruitMap::Apple));
+ EXPECT_DOUBLE_EQ(100.0, *fmAppleAndOrange.Get(FruitMap::Orange));
+}
+
+TEST(VariantMaps, ReleaseOrDefault) {
+ FruitMap fmAppleAndOrange(FruitMap::Apple, 12345,
+ FruitMap::Orange, 100.0);
+
+ int apple = fmAppleAndOrange.ReleaseOrDefault(FruitMap::Apple);
+ EXPECT_EQ(12345, apple);
+
+ // Releasing will also remove the Apple key.
+ EXPECT_EQ(size_t(1), fmAppleAndOrange.Size());
+
+ // Releasing again yields a default value.
+ int apple2 = fmAppleAndOrange.ReleaseOrDefault(FruitMap::Apple);
+ EXPECT_EQ(0, apple2);
+}
+
+TEST(VariantMaps, GetOrDefault) {
+ FruitMap fm(FruitMap::Apple, 12345);
+
+ // Apple gives the expected value we set.
+ int apple = fm.GetOrDefault(FruitMap::Apple);
+ EXPECT_EQ(12345, apple);
+
+ // Map is still 1.
+ EXPECT_EQ(size_t(1), fm.Size());
+
+ // Orange gives back a default value, since it's not in the map.
+ double orange = fm.GetOrDefault(FruitMap::Orange);
+ EXPECT_DOUBLE_EQ(0.0, orange);
+}
+
+} // namespace art
diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h
index 4fe3852..93062a7 100644
--- a/runtime/check_reference_map_visitor.h
+++ b/runtime/check_reference_map_visitor.h
@@ -82,7 +82,7 @@
CHECK(stack_mask.LoadBit(dex_register_map.GetValue(reg) >> 2));
break;
case DexRegisterMap::kInRegister:
- CHECK_NE(register_mask & dex_register_map.GetValue(reg), 0u);
+ CHECK_NE(register_mask & (1 << dex_register_map.GetValue(reg)), 0u);
break;
case DexRegisterMap::kInFpuRegister:
// In Fpu register, should not be a reference.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 6aab632..25750bb 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -21,6 +21,7 @@
#include <memory>
#include <queue>
#include <string>
+#include <unistd.h>
#include <utility>
#include <vector>
@@ -237,9 +238,7 @@
log_new_dex_caches_roots_(false),
log_new_class_table_roots_(false),
intern_table_(intern_table),
- portable_resolution_trampoline_(nullptr),
quick_resolution_trampoline_(nullptr),
- portable_imt_conflict_trampoline_(nullptr),
quick_imt_conflict_trampoline_(nullptr),
quick_generic_jni_trampoline_(nullptr),
quick_to_interpreter_bridge_trampoline_(nullptr),
@@ -247,7 +246,7 @@
memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*));
}
-void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class_path) {
+void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path) {
VLOG(startup) << "ClassLinker::Init";
CHECK(!Runtime::Current()->GetHeap()->HasImageSpace()) << "Runtime has image. We should use it.";
@@ -378,7 +377,8 @@
Handle<mirror::Class> java_lang_reflect_ArtMethod(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::ArtMethod::ClassSize())));
CHECK(java_lang_reflect_ArtMethod.Get() != nullptr);
- java_lang_reflect_ArtMethod->SetObjectSize(mirror::ArtMethod::InstanceSize(sizeof(void*)));
+ size_t pointer_size = GetInstructionSetPointerSize(Runtime::Current()->GetInstructionSet());
+ java_lang_reflect_ArtMethod->SetObjectSize(mirror::ArtMethod::InstanceSize(pointer_size));
SetClassRoot(kJavaLangReflectArtMethod, java_lang_reflect_ArtMethod.Get());
java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusResolved, self);
mirror::ArtMethod::SetClass(java_lang_reflect_ArtMethod.Get());
@@ -406,9 +406,10 @@
// DexCache instances. Needs to be after String, Field, Method arrays since AllocDexCache uses
// these roots.
CHECK_NE(0U, boot_class_path.size());
- for (const DexFile* dex_file : boot_class_path) {
- CHECK(dex_file != nullptr);
+ for (auto& dex_file : boot_class_path) {
+ CHECK(dex_file.get() != nullptr);
AppendToBootClassPath(self, *dex_file);
+ opened_dex_files_.push_back(std::move(dex_file));
}
// now we can use FindSystemClass
@@ -575,6 +576,23 @@
FindSystemClass(self, "[Ljava/lang/StackTraceElement;"));
mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
+ // Ensure void type is resolved in the core's dex cache so java.lang.Void is correctly
+ // initialized.
+ {
+ const DexFile& dex_file = java_lang_Object->GetDexFile();
+ const DexFile::StringId* void_string_id = dex_file.FindStringId("V");
+ CHECK(void_string_id != nullptr);
+ uint32_t void_string_index = dex_file.GetIndexForStringId(*void_string_id);
+ const DexFile::TypeId* void_type_id = dex_file.FindTypeId(void_string_index);
+ CHECK(void_type_id != nullptr);
+ uint16_t void_type_idx = dex_file.GetIndexForTypeId(*void_type_id);
+ // Now we resolve void type so the dex cache contains it. We use java.lang.Object class
+ // as referrer so the used dex cache is core's one.
+ mirror::Class* resolved_type = ResolveType(dex_file, void_type_idx, java_lang_Object.Get());
+ CHECK_EQ(resolved_type, GetClassRoot(kPrimitiveVoid));
+ self->AssertNoPendingException();
+ }
+
FinishInit(self);
VLOG(startup) << "ClassLinker::InitFromCompiler exiting";
@@ -704,7 +722,14 @@
argv.push_back(compiler_options[i].c_str());
}
- return Exec(argv, error_msg);
+ if (!Exec(argv, error_msg)) {
+ // Manually delete the file. Ensures there is no garbage left over if the process unexpectedly
+ // died. Ignore unlink failure, propagate the original error.
+ TEMP_FAILURE_RETRY(unlink(oat_cache_filename));
+ return false;
+ }
+
+ return true;
}
const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) {
@@ -771,7 +796,7 @@
const uint32_t* dex_location_checksum,
bool generated,
std::vector<std::string>* error_msgs,
- std::vector<const DexFile*>* dex_files) {
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) {
if (oat_file == nullptr) {
return false;
}
@@ -818,12 +843,12 @@
}
if (success) {
- const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg);
- if (dex_file == nullptr) {
+ std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
+ if (dex_file.get() == nullptr) {
success = false;
error_msgs->push_back(error_msg);
} else {
- dex_files->push_back(dex_file);
+ dex_files->push_back(std::move(dex_file));
}
}
@@ -841,14 +866,7 @@
if (success) {
return true;
} else {
- // Free all the dex files we have loaded.
- auto it = dex_files->begin() + old_size;
- auto it_end = dex_files->end();
- for (; it != it_end; it++) {
- delete *it;
- }
- dex_files->erase(dex_files->begin() + old_size, it_end);
-
+ dex_files->erase(dex_files->begin() + old_size, dex_files->end());
return false;
}
}
@@ -859,7 +877,7 @@
// multidex ahead of time.
bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
std::vector<std::string>* error_msgs,
- std::vector<const DexFile*>* dex_files) {
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) {
// 1) Check whether we have an open oat file.
// This requires a dex checksum, use the "primary" one.
uint32_t dex_location_checksum;
@@ -1209,15 +1227,15 @@
error_msg->c_str());
return false;
}
- dex_file.reset(oat_dex_file->OpenDexFile(error_msg));
+ dex_file = oat_dex_file->OpenDexFile(error_msg);
} else {
bool verified = VerifyOatAndDexFileChecksums(oat_file, dex_location, *dex_location_checksum,
kRuntimeISA, error_msg);
if (!verified) {
return false;
}
- dex_file.reset(oat_file->GetOatDexFile(dex_location,
- dex_location_checksum)->OpenDexFile(error_msg));
+ dex_file = oat_file->GetOatDexFile(dex_location,
+ dex_location_checksum)->OpenDexFile(error_msg);
}
return dex_file.get() != nullptr;
}
@@ -1601,20 +1619,19 @@
error_msg);
}
-static void InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+void ClassLinker::InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg) {
ClassLinker* class_linker = reinterpret_cast<ClassLinker*>(arg);
-
DCHECK(obj != nullptr);
DCHECK(class_linker != nullptr);
+ size_t pointer_size = class_linker->image_pointer_size_;
if (obj->IsArtMethod()) {
mirror::ArtMethod* method = obj->AsArtMethod();
if (!method->IsNative()) {
- method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge);
+ method->SetEntryPointFromInterpreterPtrSize(artInterpreterToInterpreterBridge, pointer_size);
if (method != Runtime::Current()->GetResolutionMethod()) {
- method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
- method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
+ method->SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(),
+ pointer_size);
}
}
}
@@ -1635,9 +1652,7 @@
const char* image_file_location = oat_file.GetOatHeader().
GetStoreValueByKey(OatHeader::kImageLocationKey);
CHECK(image_file_location == nullptr || *image_file_location == 0);
- portable_resolution_trampoline_ = oat_file.GetOatHeader().GetPortableResolutionTrampoline();
quick_resolution_trampoline_ = oat_file.GetOatHeader().GetQuickResolutionTrampoline();
- portable_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetPortableImtConflictTrampoline();
quick_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetQuickImtConflictTrampoline();
quick_generic_jni_trampoline_ = oat_file.GetOatHeader().GetQuickGenericJniTrampoline();
quick_to_interpreter_bridge_trampoline_ = oat_file.GetOatHeader().GetQuickToInterpreterBridge();
@@ -1665,8 +1680,8 @@
nullptr);
CHECK(oat_dex_file != nullptr) << oat_file.GetLocation() << " " << dex_file_location;
std::string error_msg;
- const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg);
- if (dex_file == nullptr) {
+ std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
+ if (dex_file.get() == nullptr) {
LOG(FATAL) << "Failed to open dex file " << dex_file_location
<< " from within oat file " << oat_file.GetLocation()
<< " error '" << error_msg << "'";
@@ -1675,7 +1690,8 @@
CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
- AppendToBootClassPath(*dex_file, dex_cache);
+ AppendToBootClassPath(*dex_file.get(), dex_cache);
+ opened_dex_files_.push_back(std::move(dex_file));
}
// Set classes on AbstractMethod early so that IsMethod tests can be performed during the live
@@ -1697,8 +1713,8 @@
}
// Set entry point to interpreter if in InterpretOnly mode.
- if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) {
- ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ Runtime* runtime = Runtime::Current();
+ if (!runtime->IsCompiler() && runtime->GetInstrumentation()->InterpretOnly()) {
heap->VisitObjects(InitFromImageInterpretOnlyCallback, this);
}
@@ -1732,23 +1748,23 @@
WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
if ((flags & kVisitRootFlagAllRoots) != 0) {
for (GcRoot<mirror::Class>& root : class_table_) {
- root.VisitRoot(callback, arg, 0, kRootStickyClass);
+ root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
}
for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) {
- root.VisitRoot(callback, arg, 0, kRootStickyClass);
+ root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
}
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (auto& root : new_class_roots_) {
mirror::Class* old_ref = root.Read<kWithoutReadBarrier>();
- root.VisitRoot(callback, arg, 0, kRootStickyClass);
+ root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
mirror::Class* new_ref = root.Read<kWithoutReadBarrier>();
if (UNLIKELY(new_ref != old_ref)) {
// Uh ohes, GC moved a root in the log. Need to search the class_table and update the
// corresponding object. This is slow, but luckily for us, this may only happen with a
// concurrent moving GC.
auto it = class_table_.Find(GcRoot<mirror::Class>(old_ref));
- class_table_.Erase(it);
- class_table_.Insert(GcRoot<mirror::Class>(new_ref));
+ DCHECK(it != class_table_.end());
+ *it = GcRoot<mirror::Class>(new_ref);
}
}
}
@@ -1768,17 +1784,17 @@
// reinit references to when reinitializing a ClassLinker from a
// mapped image.
void ClassLinker::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags) {
- class_roots_.VisitRoot(callback, arg, 0, kRootVMInternal);
+ class_roots_.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
Thread* self = Thread::Current();
{
ReaderMutexLock mu(self, dex_lock_);
if ((flags & kVisitRootFlagAllRoots) != 0) {
for (GcRoot<mirror::DexCache>& dex_cache : dex_caches_) {
- dex_cache.VisitRoot(callback, arg, 0, kRootVMInternal);
+ dex_cache.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
}
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (size_t index : new_dex_cache_roots_) {
- dex_caches_[index].VisitRoot(callback, arg, 0, kRootVMInternal);
+ dex_caches_[index].VisitRoot(callback, arg, RootInfo(kRootVMInternal));
}
}
if ((flags & kVisitRootFlagClearRootLog) != 0) {
@@ -1791,12 +1807,10 @@
}
}
VisitClassRoots(callback, arg, flags);
- array_iftable_.VisitRoot(callback, arg, 0, kRootVMInternal);
+ array_iftable_.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
DCHECK(!array_iftable_.IsNull());
for (size_t i = 0; i < kFindArrayCacheSize; ++i) {
- if (!find_array_class_cache_[i].IsNull()) {
- find_array_class_cache_[i].VisitRoot(callback, arg, 0, kRootVMInternal);
- }
+ find_array_class_cache_[i].VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
}
}
@@ -1907,7 +1921,6 @@
mirror::ShortArray::ResetArrayClass();
mirror::Throwable::ResetClass();
mirror::StackTraceElement::ResetClass();
- STLDeleteElements(&boot_class_path_);
STLDeleteElements(&oat_files_);
}
@@ -2341,6 +2354,18 @@
Handle<mirror::Class> new_class_h(hs.NewHandle(new_class));
+ // Instrumentation may have updated entrypoints for all methods of all
+ // classes. However it could not update methods of this class while we
+ // were loading it. Now the class is resolved, we can update entrypoints
+ // as required by instrumentation.
+ if (Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
+ // We must be in the kRunnable state to prevent instrumentation from
+ // suspending all threads to update entrypoints while we are doing it
+ // for this class.
+ DCHECK_EQ(self->GetState(), kRunnable);
+ Runtime::Current()->GetInstrumentation()->InstallStubsForClass(new_class_h.Get());
+ }
+
/*
* We send CLASS_PREPARE events to the debugger from here. The
* definition of "preparation" is creating the static fields for a
@@ -2502,9 +2527,6 @@
if (method->IsNative()) {
// No code and native? Use generic trampoline.
result = GetQuickGenericJniStub();
- } else if (method->IsPortableCompiled()) {
- // No code? Do we expect portable code?
- result = GetQuickToPortableBridge();
} else {
// No code? You must mean to go into the interpreter.
result = GetQuickToInterpreterBridge();
@@ -2513,36 +2535,6 @@
return result;
}
-const void* ClassLinker::GetPortableOatCodeFor(mirror::ArtMethod* method,
- bool* have_portable_code) {
- CHECK(!method->IsAbstract()) << PrettyMethod(method);
- *have_portable_code = false;
- if (method->IsProxyMethod()) {
- return GetPortableProxyInvokeHandler();
- }
- bool found;
- OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
- const void* result = nullptr;
- const void* quick_code = nullptr;
- if (found) {
- result = oat_method.GetPortableCode();
- quick_code = oat_method.GetQuickCode();
- }
-
- if (result == nullptr) {
- if (quick_code == nullptr) {
- // No code? You must mean to go into the interpreter.
- result = GetPortableToInterpreterBridge();
- } else {
- // No code? But there's quick code, so use a bridge.
- result = GetPortableToQuickBridge();
- }
- } else {
- *have_portable_code = true;
- }
- return result;
-}
-
const void* ClassLinker::GetOatMethodQuickCodeFor(mirror::ArtMethod* method) {
if (method->IsNative() || method->IsAbstract() || method->IsProxyMethod()) {
return nullptr;
@@ -2552,15 +2544,6 @@
return found ? oat_method.GetQuickCode() : nullptr;
}
-const void* ClassLinker::GetOatMethodPortableCodeFor(mirror::ArtMethod* method) {
- if (method->IsNative() || method->IsAbstract() || method->IsProxyMethod()) {
- return nullptr;
- }
- bool found;
- OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
- return found ? oat_method.GetPortableCode() : nullptr;
-}
-
const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx,
uint32_t method_idx) {
bool found;
@@ -2572,34 +2555,15 @@
return oat_class.GetOatMethod(oat_method_idx).GetQuickCode();
}
-const void* ClassLinker::GetPortableOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx,
- uint32_t method_idx) {
- bool found;
- OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, &found);
- if (!found) {
- return nullptr;
- }
- uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx);
- return oat_class.GetOatMethod(oat_method_idx).GetPortableCode();
-}
-
// Returns true if the method must run with interpreter, false otherwise.
-static bool NeedsInterpreter(
- mirror::ArtMethod* method, const void* quick_code, const void* portable_code)
+static bool NeedsInterpreter(mirror::ArtMethod* method, const void* quick_code)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if ((quick_code == nullptr) && (portable_code == nullptr)) {
+ if (quick_code == nullptr) {
// No code: need interpreter.
// May return true for native code, in the case of generic JNI
// DCHECK(!method->IsNative());
return true;
}
-#ifdef ART_SEA_IR_MODE
- ScopedObjectAccess soa(Thread::Current());
- if (std::string::npos != PrettyMethod(method).find("fibonacci")) {
- LOG(INFO) << "Found " << PrettyMethod(method);
- return false;
- }
-#endif
// If interpreter mode is enabled, every method (except native and proxy) must
// be run with interpreter.
return Runtime::Current()->GetInstrumentation()->InterpretOnly() &&
@@ -2642,37 +2606,22 @@
// Only update static methods.
continue;
}
- const void* portable_code = nullptr;
const void* quick_code = nullptr;
if (has_oat_class) {
OatFile::OatMethod oat_method = oat_class.GetOatMethod(method_index);
- portable_code = oat_method.GetPortableCode();
quick_code = oat_method.GetQuickCode();
}
- const bool enter_interpreter = NeedsInterpreter(method, quick_code, portable_code);
- bool have_portable_code = false;
+ const bool enter_interpreter = NeedsInterpreter(method, quick_code);
if (enter_interpreter) {
// Use interpreter entry point.
// Check whether the method is native, in which case it's generic JNI.
- if (quick_code == nullptr && portable_code == nullptr && method->IsNative()) {
+ if (quick_code == nullptr && method->IsNative()) {
quick_code = GetQuickGenericJniStub();
- portable_code = GetPortableToQuickBridge();
} else {
- portable_code = GetPortableToInterpreterBridge();
quick_code = GetQuickToInterpreterBridge();
}
- } else {
- if (portable_code == nullptr) {
- portable_code = GetPortableToQuickBridge();
- } else {
- have_portable_code = true;
- }
- if (quick_code == nullptr) {
- quick_code = GetQuickToPortableBridge();
- }
}
- runtime->GetInstrumentation()->UpdateMethodsCode(method, quick_code, portable_code,
- have_portable_code);
+ runtime->GetInstrumentation()->UpdateMethodsCode(method, quick_code);
}
// Ignore virtual methods on the iterator.
}
@@ -2687,7 +2636,6 @@
}
// Method shouldn't have already been linked.
DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
- DCHECK(method->GetEntryPointFromPortableCompiledCode() == nullptr);
if (oat_class != nullptr) {
// Every kind of method should at least get an invoke stub from the oat_method.
// non-abstract methods also get their code pointers.
@@ -2697,8 +2645,7 @@
// Install entry point from interpreter.
bool enter_interpreter = NeedsInterpreter(method.Get(),
- method->GetEntryPointFromQuickCompiledCode(),
- method->GetEntryPointFromPortableCompiledCode());
+ method->GetEntryPointFromQuickCompiledCode());
if (enter_interpreter && !method->IsNative()) {
method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge);
} else {
@@ -2707,33 +2654,21 @@
if (method->IsAbstract()) {
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
- method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
return;
}
- bool have_portable_code = false;
if (method->IsStatic() && !method->IsConstructor()) {
// For static methods excluding the class initializer, install the trampoline.
// It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
// after initializing class (see ClassLinker::InitializeClass method).
method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());
- method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionStub());
} else if (enter_interpreter) {
if (!method->IsNative()) {
// Set entry point from compiled code if there's no code or in interpreter only mode.
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
- method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
} else {
method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());
- method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
}
- } else if (method->GetEntryPointFromPortableCompiledCode() != nullptr) {
- DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
- have_portable_code = true;
- method->SetEntryPointFromQuickCompiledCode(GetQuickToPortableBridge());
- } else {
- DCHECK(method->GetEntryPointFromQuickCompiledCode() != nullptr);
- method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
}
if (method->IsNative()) {
@@ -2748,12 +2683,6 @@
DCHECK(IsQuickGenericJniStub(entry_point) || IsQuickResolutionStub(entry_point));
}
}
-
- // Allow instrumentation its chance to hijack code.
- runtime->GetInstrumentation()->UpdateMethodsCode(method.Get(),
- method->GetEntryPointFromQuickCompiledCode(),
- method->GetEntryPointFromPortableCompiledCode(),
- have_portable_code);
}
@@ -2769,9 +2698,6 @@
CHECK(descriptor != nullptr);
klass->SetClass(GetClassRoot(kJavaLangClass));
- if (kUseBakerOrBrooksReadBarrier) {
- klass->AssertReadBarrierPointer();
- }
uint32_t access_flags = dex_class_def.GetJavaAccessFlags();
CHECK_EQ(access_flags & ~kAccJavaFlagsMask, 0U);
klass->SetAccessFlags(access_flags);
@@ -3708,6 +3634,19 @@
return false;
}
+ // We may be running with a preopted oat file but without image. In this case,
+ // we don't skip verification of preverified classes to ensure we initialize
+ // dex caches with all types resolved during verification.
+ // We need to trust image classes, as these might be coming out of a pre-opted, quickened boot
+ // image (that we just failed loading), and the verifier can't be run on quickened opcodes when
+ // the runtime isn't started. On the other hand, app classes can be re-verified even if they are
+ // already pre-opted, as then the runtime is started.
+ if (!Runtime::Current()->IsCompiler() &&
+ !Runtime::Current()->GetHeap()->HasImageSpace() &&
+ klass->GetClassLoader() != nullptr) {
+ return false;
+ }
+
uint16_t class_def_index = klass->GetDexClassDefIndex();
oat_file_class_status = oat_dex_file->GetOatClass(class_def_index).GetStatus();
if (oat_file_class_status == mirror::Class::kStatusVerified ||
@@ -4047,7 +3986,6 @@
// At runtime the method looks like a reference and argument saving method, clone the code
// related parameters from this method.
method->SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler());
- method->SetEntryPointFromPortableCompiledCode(GetPortableProxyInvokeHandler());
method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
return method;
@@ -4283,6 +4221,14 @@
WrapExceptionInInitializer(klass);
klass->SetStatus(mirror::Class::kStatusError, self);
success = false;
+ } else if (Runtime::Current()->IsTransactionAborted()) {
+ // The exception thrown when the transaction aborted has been caught and cleared
+ // so we need to throw it again now.
+ LOG(WARNING) << "Return from class initializer of " << PrettyDescriptor(klass.Get())
+ << " without exception while transaction was aborted: re-throw it now.";
+ Runtime::Current()->ThrowInternalErrorForAbortedTransaction(self);
+ klass->SetStatus(mirror::Class::kStatusError, self);
+ success = false;
} else {
RuntimeStats* global_stats = Runtime::Current()->GetStats();
RuntimeStats* thread_stats = self->GetStats();
@@ -4352,7 +4298,9 @@
{
StackHandleScope<1> hs(self);
Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType()));
- if (UNLIKELY(method2->GetReturnType() != return_type.Get())) {
+ mirror::Class* other_return_type = method2->GetReturnType();
+ // NOTE: return_type.Get() must be sequenced after method2->GetReturnType().
+ if (UNLIKELY(other_return_type != return_type.Get())) {
return false;
}
}
@@ -4368,11 +4316,13 @@
return false;
}
for (uint32_t i = 0; i < num_types; ++i) {
- mirror::Class* param_type =
- method1->GetClassFromTypeIndex(types1->GetTypeItem(i).type_idx_, true);
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> param_type(hs.NewHandle(
+ method1->GetClassFromTypeIndex(types1->GetTypeItem(i).type_idx_, true)));
mirror::Class* other_param_type =
method2->GetClassFromTypeIndex(types2->GetTypeItem(i).type_idx_, true);
- if (UNLIKELY(param_type != other_param_type)) {
+ // NOTE: param_type.Get() must be sequenced after method2->GetClassFromTypeIndex(...).
+ if (UNLIKELY(param_type.Get() != other_param_type)) {
return false;
}
}
@@ -4551,6 +4501,171 @@
return true;
}
+static void CountMethodsAndFields(ClassDataItemIterator& dex_data,
+ size_t* virtual_methods,
+ size_t* direct_methods,
+ size_t* static_fields,
+ size_t* instance_fields) {
+ *virtual_methods = *direct_methods = *static_fields = *instance_fields = 0;
+
+ while (dex_data.HasNextStaticField()) {
+ dex_data.Next();
+ (*static_fields)++;
+ }
+ while (dex_data.HasNextInstanceField()) {
+ dex_data.Next();
+ (*instance_fields)++;
+ }
+ while (dex_data.HasNextDirectMethod()) {
+ (*direct_methods)++;
+ dex_data.Next();
+ }
+ while (dex_data.HasNextVirtualMethod()) {
+ (*virtual_methods)++;
+ dex_data.Next();
+ }
+ DCHECK(!dex_data.HasNext());
+}
+
+static void DumpClass(std::ostream& os,
+ const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
+ const char* suffix) {
+ ClassDataItemIterator dex_data(dex_file, dex_file.GetClassData(dex_class_def));
+ os << dex_file.GetClassDescriptor(dex_class_def) << suffix << ":\n";
+ os << " Static fields:\n";
+ while (dex_data.HasNextStaticField()) {
+ const DexFile::FieldId& id = dex_file.GetFieldId(dex_data.GetMemberIndex());
+ os << " " << dex_file.GetFieldTypeDescriptor(id) << " " << dex_file.GetFieldName(id) << "\n";
+ dex_data.Next();
+ }
+ os << " Instance fields:\n";
+ while (dex_data.HasNextInstanceField()) {
+ const DexFile::FieldId& id = dex_file.GetFieldId(dex_data.GetMemberIndex());
+ os << " " << dex_file.GetFieldTypeDescriptor(id) << " " << dex_file.GetFieldName(id) << "\n";
+ dex_data.Next();
+ }
+ os << " Direct methods:\n";
+ while (dex_data.HasNextDirectMethod()) {
+ const DexFile::MethodId& id = dex_file.GetMethodId(dex_data.GetMemberIndex());
+ os << " " << dex_file.GetMethodName(id) << dex_file.GetMethodSignature(id).ToString() << "\n";
+ dex_data.Next();
+ }
+ os << " Virtual methods:\n";
+ while (dex_data.HasNextVirtualMethod()) {
+ const DexFile::MethodId& id = dex_file.GetMethodId(dex_data.GetMemberIndex());
+ os << " " << dex_file.GetMethodName(id) << dex_file.GetMethodSignature(id).ToString() << "\n";
+ dex_data.Next();
+ }
+}
+
+static std::string DumpClasses(const DexFile& dex_file1, const DexFile::ClassDef& dex_class_def1,
+ const DexFile& dex_file2, const DexFile::ClassDef& dex_class_def2) {
+ std::ostringstream os;
+ DumpClass(os, dex_file1, dex_class_def1, " (Compile time)");
+ DumpClass(os, dex_file2, dex_class_def2, " (Runtime)");
+ return os.str();
+}
+
+
+// Very simple structural check on whether the classes match. Only compares the number of
+// methods and fields.
+static bool SimpleStructuralCheck(const DexFile& dex_file1, const DexFile::ClassDef& dex_class_def1,
+ const DexFile& dex_file2, const DexFile::ClassDef& dex_class_def2,
+ std::string* error_msg) {
+ ClassDataItemIterator dex_data1(dex_file1, dex_file1.GetClassData(dex_class_def1));
+ ClassDataItemIterator dex_data2(dex_file2, dex_file2.GetClassData(dex_class_def2));
+
+ // Counters for current dex file.
+ size_t dex_virtual_methods1, dex_direct_methods1, dex_static_fields1, dex_instance_fields1;
+ CountMethodsAndFields(dex_data1, &dex_virtual_methods1, &dex_direct_methods1, &dex_static_fields1,
+ &dex_instance_fields1);
+ // Counters for compile-time dex file.
+ size_t dex_virtual_methods2, dex_direct_methods2, dex_static_fields2, dex_instance_fields2;
+ CountMethodsAndFields(dex_data2, &dex_virtual_methods2, &dex_direct_methods2, &dex_static_fields2,
+ &dex_instance_fields2);
+
+ if (dex_virtual_methods1 != dex_virtual_methods2) {
+ std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
+ *error_msg = StringPrintf("Virtual method count off: %zu vs %zu\n%s", dex_virtual_methods1,
+ dex_virtual_methods2, class_dump.c_str());
+ return false;
+ }
+ if (dex_direct_methods1 != dex_direct_methods2) {
+ std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
+ *error_msg = StringPrintf("Direct method count off: %zu vs %zu\n%s", dex_direct_methods1,
+ dex_direct_methods2, class_dump.c_str());
+ return false;
+ }
+ if (dex_static_fields1 != dex_static_fields2) {
+ std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
+ *error_msg = StringPrintf("Static field count off: %zu vs %zu\n%s", dex_static_fields1,
+ dex_static_fields2, class_dump.c_str());
+ return false;
+ }
+ if (dex_instance_fields1 != dex_instance_fields2) {
+ std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
+ *error_msg = StringPrintf("Instance field count off: %zu vs %zu\n%s", dex_instance_fields1,
+ dex_instance_fields2, class_dump.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+// Checks whether a the super-class changed from what we had at compile-time. This would
+// invalidate quickening.
+static bool CheckSuperClassChange(Handle<mirror::Class> klass,
+ const DexFile& dex_file,
+ const DexFile::ClassDef& class_def,
+ mirror::Class* super_class)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Check for unexpected changes in the superclass.
+ // Quick check 1) is the super_class class-loader the boot class loader? This always has
+ // precedence.
+ if (super_class->GetClassLoader() != nullptr &&
+ // Quick check 2) different dex cache? Breaks can only occur for different dex files,
+ // which is implied by different dex cache.
+ klass->GetDexCache() != super_class->GetDexCache()) {
+ // Now comes the expensive part: things can be broken if (a) the klass' dex file has a
+ // definition for the super-class, and (b) the files are in separate oat files. The oat files
+ // are referenced from the dex file, so do (b) first. Only relevant if we have oat files.
+ const OatFile* class_oat_file = dex_file.GetOatFile();
+ if (class_oat_file != nullptr) {
+ const OatFile* loaded_super_oat_file = super_class->GetDexFile().GetOatFile();
+ if (loaded_super_oat_file != nullptr && class_oat_file != loaded_super_oat_file) {
+ // Now check (a).
+ const DexFile::ClassDef* super_class_def = dex_file.FindClassDef(class_def.superclass_idx_);
+ if (super_class_def != nullptr) {
+ // Uh-oh, we found something. Do our check.
+ std::string error_msg;
+ if (!SimpleStructuralCheck(dex_file, *super_class_def,
+ super_class->GetDexFile(), *super_class->GetClassDef(),
+ &error_msg)) {
+ // Print a warning to the log. This exception might be caught, e.g., as common in test
+ // drivers. When the class is later tried to be used, we re-throw a new instance, as we
+ // only save the type of the exception.
+ LOG(WARNING) << "Incompatible structural change detected: " <<
+ StringPrintf(
+ "Structural change of %s is hazardous (%s at compile time, %s at runtime): %s",
+ PrettyType(super_class_def->class_idx_, dex_file).c_str(),
+ class_oat_file->GetLocation().c_str(),
+ loaded_super_oat_file->GetLocation().c_str(),
+ error_msg.c_str());
+ ThrowIncompatibleClassChangeError(klass.Get(),
+ "Structural change of %s is hazardous (%s at compile time, %s at runtime): %s",
+ PrettyType(super_class_def->class_idx_, dex_file).c_str(),
+ class_oat_file->GetLocation().c_str(),
+ loaded_super_oat_file->GetLocation().c_str(),
+ error_msg.c_str());
+ return false;
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
+
bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexFile& dex_file) {
CHECK_EQ(mirror::Class::kStatusIdx, klass->GetStatus());
const DexFile::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex());
@@ -4570,6 +4685,11 @@
}
CHECK(super_class->IsResolved());
klass->SetSuperClass(super_class);
+
+ if (!CheckSuperClassChange(klass, dex_file, class_def, super_class)) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return false;
+ }
}
const DexFile::TypeList* interfaces = dex_file.GetInterfacesList(class_def);
if (interfaces != nullptr) {
@@ -5366,7 +5486,8 @@
klass->SetNumReferenceInstanceFields(num_reference_fields);
if (!klass->IsVariableSize()) {
if (klass->DescriptorEquals("Ljava/lang/reflect/ArtMethod;")) {
- klass->SetObjectSize(mirror::ArtMethod::InstanceSize(sizeof(void*)));
+ size_t pointer_size = GetInstructionSetPointerSize(Runtime::Current()->GetInstructionSet());
+ klass->SetObjectSize(mirror::ArtMethod::InstanceSize(pointer_size));
} else {
std::string temp;
DCHECK_GE(size, sizeof(mirror::Object)) << klass->GetDescriptor(&temp);
@@ -5773,8 +5894,7 @@
}
}
-static OatFile::OatMethod CreateOatMethod(const void* code, bool is_portable) {
- CHECK_EQ(kUsePortableCompiler, is_portable);
+static OatFile::OatMethod CreateOatMethod(const void* code) {
CHECK(code != nullptr);
const uint8_t* base = reinterpret_cast<const uint8_t*>(code); // Base of data points at code.
base -= sizeof(void*); // Move backward so that code_offset != 0.
@@ -5782,21 +5902,11 @@
return OatFile::OatMethod(base, code_offset);
}
-bool ClassLinker::IsPortableResolutionStub(const void* entry_point) const {
- return (entry_point == GetPortableResolutionStub()) ||
- (portable_resolution_trampoline_ == entry_point);
-}
-
bool ClassLinker::IsQuickResolutionStub(const void* entry_point) const {
return (entry_point == GetQuickResolutionStub()) ||
(quick_resolution_trampoline_ == entry_point);
}
-bool ClassLinker::IsPortableToInterpreterBridge(const void* entry_point) const {
- return (entry_point == GetPortableToInterpreterBridge());
- // TODO: portable_to_interpreter_bridge_trampoline_ == entry_point;
-}
-
bool ClassLinker::IsQuickToInterpreterBridge(const void* entry_point) const {
return (entry_point == GetQuickToInterpreterBridge()) ||
(quick_to_interpreter_bridge_trampoline_ == entry_point);
@@ -5811,32 +5921,22 @@
return GetQuickGenericJniStub();
}
-void ClassLinker::SetEntryPointsToCompiledCode(mirror::ArtMethod* method, const void* method_code,
- bool is_portable) const {
- OatFile::OatMethod oat_method = CreateOatMethod(method_code, is_portable);
+void ClassLinker::SetEntryPointsToCompiledCode(mirror::ArtMethod* method,
+ const void* method_code) const {
+ OatFile::OatMethod oat_method = CreateOatMethod(method_code);
oat_method.LinkMethod(method);
method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
- // Create bridges to transition between different kinds of compiled bridge.
- if (method->GetEntryPointFromPortableCompiledCode() == nullptr) {
- method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
- } else {
- CHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
- method->SetEntryPointFromQuickCompiledCode(GetQuickToPortableBridge());
- method->SetIsPortableCompiled();
- }
}
void ClassLinker::SetEntryPointsToInterpreter(mirror::ArtMethod* method) const {
if (!method->IsNative()) {
method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge);
- method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
} else {
const void* quick_method_code = GetQuickGenericJniStub();
- OatFile::OatMethod oat_method = CreateOatMethod(quick_method_code, false);
+ OatFile::OatMethod oat_method = CreateOatMethod(quick_method_code);
oat_method.LinkMethod(method);
method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
- method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
}
}
@@ -5963,4 +6063,34 @@
return ComputeModifiedUtf8Hash(descriptor);
}
+bool ClassLinker::MayBeCalledWithDirectCodePointer(mirror::ArtMethod* m) {
+ // Non-image methods don't use direct code pointer.
+ if (!m->GetDeclaringClass()->IsBootStrapClassLoaded()) {
+ return false;
+ }
+ if (m->IsPrivate()) {
+ // The method can only be called inside its own oat file. Therefore it won't be called using
+ // its direct code if the oat file has been compiled in PIC mode.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const DexFile& dex_file = m->GetDeclaringClass()->GetDexFile();
+ const OatFile::OatDexFile* oat_dex_file = class_linker->FindOpenedOatDexFileForDexFile(dex_file);
+ if (oat_dex_file == nullptr) {
+ // No oat file: the method has not been compiled.
+ return false;
+ }
+ const OatFile* oat_file = oat_dex_file->GetOatFile();
+ return oat_file != nullptr && !oat_file->IsPic();
+ } else {
+ // The method can be called outside its own oat file. Therefore it won't be called using its
+ // direct code pointer only if all loaded oat files have been compiled in PIC mode.
+ ReaderMutexLock mu(Thread::Current(), dex_lock_);
+ for (const OatFile* oat_file : oat_files_) {
+ if (!oat_file->IsPic()) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
} // namespace art
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index b78d0b5..6570c5f 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -105,7 +105,7 @@
~ClassLinker();
// Initialize class linker by bootstraping from dex files.
- void InitWithoutImage(const std::vector<const DexFile*>& boot_class_path)
+ void InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Initialize class linker from one or more images.
@@ -324,7 +324,7 @@
// (if multidex) into the given vector.
bool OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
std::vector<std::string>* error_msgs,
- std::vector<const DexFile*>* dex_files)
+ std::vector<std::unique_ptr<const DexFile>>* dex_files)
LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
// Returns true if the given oat file has the same image checksum as the image it is paired with.
@@ -392,22 +392,16 @@
// Get the oat code for a method when its class isn't yet initialized
const void* GetQuickOatCodeFor(mirror::ArtMethod* method)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const void* GetPortableOatCodeFor(mirror::ArtMethod* method, bool* have_portable_code)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Get the oat code for a method from a method index.
const void* GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const void* GetPortableOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Get compiled code for a method, return null if no code
// exists. This is unlike Get..OatCodeFor which will return a bridge
// or interpreter entrypoint.
const void* GetOatMethodQuickCodeFor(mirror::ArtMethod* method)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const void* GetOatMethodPortableCodeFor(mirror::ArtMethod* method)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
pid_t GetClassesLockOwner(); // For SignalCatcher.
pid_t GetDexLockOwner(); // For SignalCatcher.
@@ -416,15 +410,9 @@
static const char* GetClassRootDescriptor(ClassRoot class_root);
- // Is the given entry point portable code to run the resolution stub?
- bool IsPortableResolutionStub(const void* entry_point) const;
-
// Is the given entry point quick code to run the resolution stub?
bool IsQuickResolutionStub(const void* entry_point) const;
- // Is the given entry point portable code to bridge into the interpreter?
- bool IsPortableToInterpreterBridge(const void* entry_point) const;
-
// Is the given entry point quick code to bridge into the interpreter?
bool IsQuickToInterpreterBridge(const void* entry_point) const;
@@ -436,8 +424,7 @@
}
// Set the entrypoints up for method to the given code.
- void SetEntryPointsToCompiledCode(mirror::ArtMethod* method, const void* method_code,
- bool is_portable) const
+ void SetEntryPointsToCompiledCode(mirror::ArtMethod* method, const void* method_code) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Set the entrypoints up for method to the enter the interpreter.
@@ -471,7 +458,14 @@
LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Returns true if the method can be called with its direct code pointer, false otherwise.
+ bool MayBeCalledWithDirectCodePointer(mirror::ArtMethod* m)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
private:
+ static void InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const OatFile::OatMethod FindOatMethodFor(mirror::ArtMethod* method, bool* found)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -728,6 +722,7 @@
const void* GetRuntimeQuickGenericJniStub() const;
std::vector<const DexFile*> boot_class_path_;
+ std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
mutable ReaderWriterMutex dex_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
std::vector<size_t> new_dex_cache_roots_ GUARDED_BY(dex_lock_);
@@ -798,9 +793,7 @@
// Trampolines within the image the bounce to runtime entrypoints. Done so that there is a single
// patch point within the image. TODO: make these proper relocations.
- const void* portable_resolution_trampoline_;
const void* quick_resolution_trampoline_;
- const void* portable_imt_conflict_trampoline_;
const void* quick_imt_conflict_trampoline_;
const void* quick_generic_jni_trampoline_;
const void* quick_to_interpreter_bridge_trampoline_;
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index ac078aa..64e129c 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -342,32 +342,30 @@
}
}
- void AssertDexFile(const DexFile* dex, mirror::ClassLoader* class_loader)
+ void AssertDexFile(const DexFile& dex, mirror::ClassLoader* class_loader)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ASSERT_TRUE(dex != nullptr);
-
// Verify all the classes defined in this file
- for (size_t i = 0; i < dex->NumClassDefs(); i++) {
- const DexFile::ClassDef& class_def = dex->GetClassDef(i);
- const char* descriptor = dex->GetClassDescriptor(class_def);
+ for (size_t i = 0; i < dex.NumClassDefs(); i++) {
+ const DexFile::ClassDef& class_def = dex.GetClassDef(i);
+ const char* descriptor = dex.GetClassDescriptor(class_def);
AssertDexFileClass(class_loader, descriptor);
}
// Verify all the types referenced by this file
- for (size_t i = 0; i < dex->NumTypeIds(); i++) {
- const DexFile::TypeId& type_id = dex->GetTypeId(i);
- const char* descriptor = dex->GetTypeDescriptor(type_id);
+ for (size_t i = 0; i < dex.NumTypeIds(); i++) {
+ const DexFile::TypeId& type_id = dex.GetTypeId(i);
+ const char* descriptor = dex.GetTypeDescriptor(type_id);
AssertDexFileClass(class_loader, descriptor);
}
class_linker_->VisitRoots(TestRootVisitor, nullptr, kVisitRootFlagAllRoots);
// Verify the dex cache has resolution methods in all resolved method slots
- mirror::DexCache* dex_cache = class_linker_->FindDexCache(*dex);
+ mirror::DexCache* dex_cache = class_linker_->FindDexCache(dex);
mirror::ObjectArray<mirror::ArtMethod>* resolved_methods = dex_cache->GetResolvedMethods();
for (size_t i = 0; i < static_cast<size_t>(resolved_methods->GetLength()); i++) {
- EXPECT_TRUE(resolved_methods->Get(i) != nullptr) << dex->GetLocation() << " i=" << i;
+ EXPECT_TRUE(resolved_methods->Get(i) != nullptr) << dex.GetLocation() << " i=" << i;
}
}
- static void TestRootVisitor(mirror::Object** root, void*, uint32_t, RootType) {
+ static void TestRootVisitor(mirror::Object** root, void*, const RootInfo&) {
EXPECT_TRUE(*root != nullptr);
}
};
@@ -744,7 +742,8 @@
TEST_F(ClassLinkerTest, LibCore) {
ScopedObjectAccess soa(Thread::Current());
- AssertDexFile(java_lang_dex_file_, nullptr);
+ ASSERT_TRUE(java_lang_dex_file_ != nullptr);
+ AssertDexFile(*java_lang_dex_file_, nullptr);
}
// The first reference array element must be a multiple of 4 bytes from the
@@ -1137,4 +1136,24 @@
CheckPreverified(statics.Get(), true);
}
+TEST_F(ClassLinkerTest, IsBootStrapClassLoaded) {
+ ScopedObjectAccess soa(Thread::Current());
+
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Statics"))));
+
+ // java.lang.Object is a bootstrap class.
+ Handle<mirror::Class> jlo_class(
+ hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
+ ASSERT_TRUE(jlo_class.Get() != nullptr);
+ EXPECT_TRUE(jlo_class.Get()->IsBootStrapClassLoaded());
+
+ // Statics is not a bootstrap class.
+ Handle<mirror::Class> statics(
+ hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStatics;", class_loader)));
+ ASSERT_TRUE(statics.Get() != nullptr);
+ EXPECT_FALSE(statics.Get()->IsBootStrapClassLoaded());
+}
+
} // namespace art
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 03b33e9..b7ffd60 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -20,6 +20,7 @@
#include <dlfcn.h>
#include <fcntl.h>
#include <ScopedLocalRef.h>
+#include <stdlib.h>
#include "../../external/icu/icu4c/source/common/unicode/uvernum.h"
#include "base/macros.h"
@@ -43,6 +44,11 @@
#include "well_known_classes.h"
int main(int argc, char **argv) {
+ // Gtests can be very noisy. For example, an executable with multiple tests will trigger native
+ // bridge warnings. The following line reduces the minimum log severity to ERROR and suppresses
+ // everything else. In case you want to see all messages, comment out the line.
+ setenv("ANDROID_LOG_TAGS", "*:e", 1);
+
art::InitLogging(argv);
LOG(::art::INFO) << "Running main() from common_runtime_test.cc...";
testing::InitGoogleTest(&argc, argv);
@@ -84,21 +90,29 @@
return file_->Fd();
}
-void ScratchFile::Unlink() {
- if (!OS::FileExists(filename_.c_str())) {
- return;
- }
+void ScratchFile::Close() {
if (file_.get() != nullptr) {
if (file_->FlushCloseOrErase() != 0) {
PLOG(WARNING) << "Error closing scratch file.";
}
}
+}
+
+void ScratchFile::Unlink() {
+ if (!OS::FileExists(filename_.c_str())) {
+ return;
+ }
+ Close();
int unlink_result = unlink(filename_.c_str());
CHECK_EQ(0, unlink_result);
}
CommonRuntimeTest::CommonRuntimeTest() {}
-CommonRuntimeTest::~CommonRuntimeTest() {}
+CommonRuntimeTest::~CommonRuntimeTest() {
+ // Ensure the dex files are cleaned up before the runtime.
+ loaded_dex_files_.clear();
+ runtime_.reset();
+}
void CommonRuntimeTest::SetUpAndroidRoot() {
if (IsHost()) {
@@ -169,16 +183,23 @@
}
}
+std::string CommonRuntimeTest::GetCoreArtLocation() {
+ return GetCoreFileLocation("art");
+}
-const DexFile* CommonRuntimeTest::LoadExpectSingleDexFile(const char* location) {
- std::vector<const DexFile*> dex_files;
+std::string CommonRuntimeTest::GetCoreOatLocation() {
+ return GetCoreFileLocation("oat");
+}
+
+std::unique_ptr<const DexFile> CommonRuntimeTest::LoadExpectSingleDexFile(const char* location) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
std::string error_msg;
if (!DexFile::Open(location, location, &error_msg, &dex_files)) {
LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n";
- return nullptr;
+ UNREACHABLE();
} else {
CHECK_EQ(1U, dex_files.size()) << "Expected only one dex file in " << location;
- return dex_files[0];
+ return std::move(dex_files[0]);
}
}
@@ -190,24 +211,20 @@
int mkdir_result = mkdir(dalvik_cache_.c_str(), 0700);
ASSERT_EQ(mkdir_result, 0);
- MemMap::Init(); // For LoadExpectSingleDexFile
-
- std::string error_msg;
- java_lang_dex_file_ = LoadExpectSingleDexFile(GetLibCoreDexFileName().c_str());
- boot_class_path_.push_back(java_lang_dex_file_);
-
std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB));
std::string max_heap_string(StringPrintf("-Xmx%zdm", gc::Heap::kDefaultMaximumSize / MB));
callbacks_.reset(new NoopCompilerCallbacks());
RuntimeOptions options;
- options.push_back(std::make_pair("bootclasspath", &boot_class_path_));
+ std::string boot_class_path_string = "-Xbootclasspath:" + GetLibCoreDexFileName();
+ options.push_back(std::make_pair(boot_class_path_string, nullptr));
options.push_back(std::make_pair("-Xcheck:jni", nullptr));
- options.push_back(std::make_pair(min_heap_string.c_str(), nullptr));
- options.push_back(std::make_pair(max_heap_string.c_str(), nullptr));
+ options.push_back(std::make_pair(min_heap_string, nullptr));
+ options.push_back(std::make_pair(max_heap_string, nullptr));
options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
SetUpRuntimeOptions(&options);
+
if (!Runtime::Create(options, false)) {
LOG(FATAL) << "Failed to create runtime";
return;
@@ -216,6 +233,9 @@
class_linker_ = runtime_->GetClassLinker();
class_linker_->FixupDexCaches(runtime_->GetResolutionMethod());
class_linker_->RunRootClinits();
+ boot_class_path_ = class_linker_->GetBootClassPath();
+ java_lang_dex_file_ = boot_class_path_[0];
+
// Runtime::Create acquired the mutator_lock_ that is normally given away when we
// Runtime::Start, give it away now and then switch to a more managable ScopedObjectAccess.
@@ -228,6 +248,11 @@
// pool is created by the runtime.
runtime_->GetHeap()->CreateThreadPool();
runtime_->GetHeap()->VerifyHeap(); // Check for heap corruption before the test
+
+ // Get the boot class path from the runtime so it can be used in tests.
+ boot_class_path_ = class_linker_->GetBootClassPath();
+ ASSERT_FALSE(boot_class_path_.empty());
+ java_lang_dex_file_ = boot_class_path_[0];
}
void CommonRuntimeTest::ClearDirectory(const char* dirpath) {
@@ -274,8 +299,6 @@
IcuCleanupFn icu_cleanup_fn = reinterpret_cast<IcuCleanupFn>(sym);
(*icu_cleanup_fn)();
- STLDeleteElements(&opened_dex_files_);
-
Runtime::Current()->GetHeap()->VerifyHeap(); // Check for heap corruption after the test
}
@@ -312,7 +335,7 @@
#define ART_TARGET_NATIVETEST_DIR_STRING ""
#endif
-std::vector<const DexFile*> CommonRuntimeTest::OpenTestDexFiles(const char* name) {
+std::vector<std::unique_ptr<const DexFile>> CommonRuntimeTest::OpenTestDexFiles(const char* name) {
CHECK(name != nullptr);
std::string filename;
if (IsHost()) {
@@ -325,28 +348,30 @@
filename += name;
filename += ".jar";
std::string error_msg;
- std::vector<const DexFile*> dex_files;
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
bool success = DexFile::Open(filename.c_str(), filename.c_str(), &error_msg, &dex_files);
CHECK(success) << "Failed to open '" << filename << "': " << error_msg;
- for (const DexFile* dex_file : dex_files) {
+ for (auto& dex_file : dex_files) {
CHECK_EQ(PROT_READ, dex_file->GetPermissions());
CHECK(dex_file->IsReadOnly());
}
- opened_dex_files_.insert(opened_dex_files_.end(), dex_files.begin(), dex_files.end());
return dex_files;
}
-const DexFile* CommonRuntimeTest::OpenTestDexFile(const char* name) {
- std::vector<const DexFile*> vector = OpenTestDexFiles(name);
+std::unique_ptr<const DexFile> CommonRuntimeTest::OpenTestDexFile(const char* name) {
+ std::vector<std::unique_ptr<const DexFile>> vector = OpenTestDexFiles(name);
EXPECT_EQ(1U, vector.size());
- return vector[0];
+ return std::move(vector[0]);
}
jobject CommonRuntimeTest::LoadDex(const char* dex_name) {
- std::vector<const DexFile*> dex_files = OpenTestDexFiles(dex_name);
+ std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name);
+ std::vector<const DexFile*> class_path;
CHECK_NE(0U, dex_files.size());
- for (const DexFile* dex_file : dex_files) {
+ for (auto& dex_file : dex_files) {
+ class_path.push_back(dex_file.get());
class_linker_->RegisterDexFile(*dex_file);
+ loaded_dex_files_.push_back(std::move(dex_file));
}
Thread* self = Thread::Current();
JNIEnvExt* env = self->GetJniEnv();
@@ -354,10 +379,25 @@
env->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
jobject class_loader = env->NewGlobalRef(class_loader_local.get());
self->SetClassLoaderOverride(class_loader_local.get());
- Runtime::Current()->SetCompileTimeClassPath(class_loader, dex_files);
+ Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path);
return class_loader;
}
+std::string CommonRuntimeTest::GetCoreFileLocation(const char* suffix) {
+ CHECK(suffix != nullptr);
+
+ std::string location;
+ if (IsHost()) {
+ const char* host_dir = getenv("ANDROID_HOST_OUT");
+ CHECK(host_dir != NULL);
+ location = StringPrintf("%s/framework/core.%s", host_dir, suffix);
+ } else {
+ location = StringPrintf("/data/art-test/core.%s", suffix);
+ }
+
+ return location;
+}
+
CheckJniAbortCatcher::CheckJniAbortCatcher() : vm_(Runtime::Current()->GetJavaVM()) {
vm_->SetCheckJniAbortHook(Hook, &actual_);
}
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index bd0dbaa..9efea84 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -55,6 +55,7 @@
int GetFd() const;
+ void Close();
void Unlink();
private:
@@ -75,12 +76,21 @@
CommonRuntimeTest();
~CommonRuntimeTest();
+ // Gets the path of the libcore dex file.
+ static std::string GetLibCoreDexFileName();
+
protected:
static bool IsHost() {
return !kIsTargetBuild;
}
- const DexFile* LoadExpectSingleDexFile(const char* location);
+ // File location to core.art, e.g. $ANDROID_HOST_OUT/system/framework/core.art
+ static std::string GetCoreArtLocation();
+
+ // File location to core.oat, e.g. $ANDROID_HOST_OUT/system/framework/core.oat
+ static std::string GetCoreOatLocation();
+
+ std::unique_ptr<const DexFile> LoadExpectSingleDexFile(const char* location);
virtual void SetUp();
@@ -91,32 +101,35 @@
virtual void TearDown();
- // Gets the path of the libcore dex file.
- std::string GetLibCoreDexFileName();
-
// Gets the path of the specified dex file for host or target.
- std::string GetDexFileName(const std::string& jar_prefix);
+ static std::string GetDexFileName(const std::string& jar_prefix);
std::string GetTestAndroidRoot();
- std::vector<const DexFile*> OpenTestDexFiles(const char* name)
+ std::vector<std::unique_ptr<const DexFile>> OpenTestDexFiles(const char* name)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const DexFile* OpenTestDexFile(const char* name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ std::unique_ptr<const DexFile> OpenTestDexFile(const char* name)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
jobject LoadDex(const char* dex_name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
std::string android_data_;
std::string dalvik_cache_;
- const DexFile* java_lang_dex_file_; // owned by runtime_
- std::vector<const DexFile*> boot_class_path_;
+
std::unique_ptr<Runtime> runtime_;
- // Owned by the runtime
+
+ // The class_linker_, java_lang_dex_file_, and boot_class_path_ are all
+ // owned by the runtime.
ClassLinker* class_linker_;
+ const DexFile* java_lang_dex_file_;
+ std::vector<const DexFile*> boot_class_path_;
private:
+ static std::string GetCoreFileLocation(const char* suffix);
+
std::unique_ptr<CompilerCallbacks> callbacks_;
- std::vector<const DexFile*> opened_dex_files_;
+ std::vector<std::unique_ptr<const DexFile>> loaded_dex_files_;
};
// Sets a CheckJni abort hook to catch failures. Note that this will cause CheckJNI to carry on
@@ -138,16 +151,6 @@
DISALLOW_COPY_AND_ASSIGN(CheckJniAbortCatcher);
};
-// TODO: These tests were disabled for portable when we went to having
-// MCLinker link LLVM ELF output because we no longer just have code
-// blobs in memory. We'll need to dlopen to load and relocate
-// temporary output to resurrect these tests.
-#define TEST_DISABLED_FOR_PORTABLE() \
- if (kUsePortableCompiler) { \
- printf("WARNING: TEST DISABLED FOR PORTABLE\n"); \
- return; \
- }
-
// TODO: When heap reference poisoning works with the compiler, get rid of this.
#define TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING() \
if (kPoisonHeapReferences) { \
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 846216c..f5b4354 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -418,6 +418,10 @@
break;
}
case Instruction::IGET_QUICK:
+ case Instruction::IGET_BOOLEAN_QUICK:
+ case Instruction::IGET_BYTE_QUICK:
+ case Instruction::IGET_CHAR_QUICK:
+ case Instruction::IGET_SHORT_QUICK:
case Instruction::IGET_WIDE_QUICK:
case Instruction::IGET_OBJECT_QUICK: {
// Since we replaced the field index, we ask the verifier to tell us which
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index d5cba50..678b29a 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -105,18 +105,17 @@
jobject Dbg::TypeCache::Add(mirror::Class* t) {
ScopedObjectAccessUnchecked soa(Thread::Current());
- int32_t hash_code = t->IdentityHashCode();
+ JNIEnv* const env = soa.Env();
+ ScopedLocalRef<jobject> local_ref(soa.Env(), soa.AddLocalReference<jobject>(t));
+ const int32_t hash_code = soa.Decode<mirror::Class*>(local_ref.get())->IdentityHashCode();
auto range = objects_.equal_range(hash_code);
for (auto it = range.first; it != range.second; ++it) {
- if (soa.Decode<mirror::Class*>(it->second) == t) {
+ if (soa.Decode<mirror::Class*>(it->second) == soa.Decode<mirror::Class*>(local_ref.get())) {
// Found a matching weak global, return it.
return it->second;
}
}
- JNIEnv* env = soa.Env();
- const jobject local_ref = soa.AddLocalReference<jobject>(t);
- const jobject weak_global = env->NewWeakGlobalRef(local_ref);
- env->DeleteLocalRef(local_ref);
+ const jobject weak_global = env->NewWeakGlobalRef(local_ref.get());
objects_.insert(std::make_pair(hash_code, weak_global));
return weak_global;
}
@@ -298,9 +297,6 @@
// Was there a -Xrunjdwp or -agentlib:jdwp= argument on the command line?
static bool gJdwpConfigured = false;
-// Broken-down JDWP options. (Only valid if IsJdwpConfigured() is true.)
-static JDWP::JdwpOptions gJdwpOptions;
-
// Runtime JDWP state.
static JDWP::JdwpState* gJdwpState = nullptr;
static bool gDebuggerConnected; // debugger or DDMS is connected.
@@ -342,19 +338,18 @@
// Breakpoints.
static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_);
-void DebugInvokeReq::VisitRoots(RootCallback* callback, void* arg, uint32_t tid,
- RootType root_type) {
+void DebugInvokeReq::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) {
if (receiver != nullptr) {
- callback(&receiver, arg, tid, root_type);
+ callback(&receiver, arg, root_info);
}
if (thread != nullptr) {
- callback(&thread, arg, tid, root_type);
+ callback(&thread, arg, root_info);
}
if (klass != nullptr) {
- callback(reinterpret_cast<mirror::Object**>(&klass), arg, tid, root_type);
+ callback(reinterpret_cast<mirror::Object**>(&klass), arg, root_info);
}
if (method != nullptr) {
- callback(reinterpret_cast<mirror::Object**>(&method), arg, tid, root_type);
+ callback(reinterpret_cast<mirror::Object**>(&method), arg, root_info);
}
}
@@ -366,21 +361,18 @@
method = nullptr;
}
-void SingleStepControl::VisitRoots(RootCallback* callback, void* arg, uint32_t tid,
- RootType root_type) {
- if (method != nullptr) {
- callback(reinterpret_cast<mirror::Object**>(&method), arg, tid, root_type);
+void SingleStepControl::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) {
+ if (method_ != nullptr) {
+ callback(reinterpret_cast<mirror::Object**>(&method_), arg, root_info);
}
}
-bool SingleStepControl::ContainsDexPc(uint32_t dex_pc) const {
- return dex_pcs.find(dex_pc) == dex_pcs.end();
+void SingleStepControl::AddDexPc(uint32_t dex_pc) {
+ dex_pcs_.insert(dex_pc);
}
-void SingleStepControl::Clear() {
- is_active = false;
- method = nullptr;
- dex_pcs.clear();
+bool SingleStepControl::ContainsDexPc(uint32_t dex_pc) const {
+ return dex_pcs_.find(dex_pc) == dex_pcs_.end();
}
static bool IsBreakpoint(const mirror::ArtMethod* m, uint32_t dex_pc)
@@ -537,119 +529,13 @@
}
}
-/*
- * Handle one of the JDWP name/value pairs.
- *
- * JDWP options are:
- * help: if specified, show help message and bail
- * transport: may be dt_socket or dt_shmem
- * address: for dt_socket, "host:port", or just "port" when listening
- * server: if "y", wait for debugger to attach; if "n", attach to debugger
- * timeout: how long to wait for debugger to connect / listen
- *
- * Useful with server=n (these aren't supported yet):
- * onthrow=<exception-name>: connect to debugger when exception thrown
- * onuncaught=y|n: connect to debugger when uncaught exception thrown
- * launch=<command-line>: launch the debugger itself
- *
- * The "transport" option is required, as is "address" if server=n.
- */
-static bool ParseJdwpOption(const std::string& name, const std::string& value) {
- if (name == "transport") {
- if (value == "dt_socket") {
- gJdwpOptions.transport = JDWP::kJdwpTransportSocket;
- } else if (value == "dt_android_adb") {
- gJdwpOptions.transport = JDWP::kJdwpTransportAndroidAdb;
- } else {
- LOG(ERROR) << "JDWP transport not supported: " << value;
- return false;
- }
- } else if (name == "server") {
- if (value == "n") {
- gJdwpOptions.server = false;
- } else if (value == "y") {
- gJdwpOptions.server = true;
- } else {
- LOG(ERROR) << "JDWP option 'server' must be 'y' or 'n'";
- return false;
- }
- } else if (name == "suspend") {
- if (value == "n") {
- gJdwpOptions.suspend = false;
- } else if (value == "y") {
- gJdwpOptions.suspend = true;
- } else {
- LOG(ERROR) << "JDWP option 'suspend' must be 'y' or 'n'";
- return false;
- }
- } else if (name == "address") {
- /* this is either <port> or <host>:<port> */
- std::string port_string;
- gJdwpOptions.host.clear();
- std::string::size_type colon = value.find(':');
- if (colon != std::string::npos) {
- gJdwpOptions.host = value.substr(0, colon);
- port_string = value.substr(colon + 1);
- } else {
- port_string = value;
- }
- if (port_string.empty()) {
- LOG(ERROR) << "JDWP address missing port: " << value;
- return false;
- }
- char* end;
- uint64_t port = strtoul(port_string.c_str(), &end, 10);
- if (*end != '\0' || port > 0xffff) {
- LOG(ERROR) << "JDWP address has junk in port field: " << value;
- return false;
- }
- gJdwpOptions.port = port;
- } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") {
- /* valid but unsupported */
- LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'";
- } else {
- LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'";
- }
-
- return true;
-}
-
-/*
- * Parse the latter half of a -Xrunjdwp/-agentlib:jdwp= string, e.g.:
- * "transport=dt_socket,address=8000,server=y,suspend=n"
- */
-bool Dbg::ParseJdwpOptions(const std::string& options) {
- VLOG(jdwp) << "ParseJdwpOptions: " << options;
-
- std::vector<std::string> pairs;
- Split(options, ',', &pairs);
-
- for (size_t i = 0; i < pairs.size(); ++i) {
- std::string::size_type equals = pairs[i].find('=');
- if (equals == std::string::npos) {
- LOG(ERROR) << "Can't parse JDWP option '" << pairs[i] << "' in '" << options << "'";
- return false;
- }
- ParseJdwpOption(pairs[i].substr(0, equals), pairs[i].substr(equals + 1));
- }
-
- if (gJdwpOptions.transport == JDWP::kJdwpTransportUnknown) {
- LOG(ERROR) << "Must specify JDWP transport: " << options;
- }
- if (!gJdwpOptions.server && (gJdwpOptions.host.empty() || gJdwpOptions.port == 0)) {
- LOG(ERROR) << "Must specify JDWP host and port when server=n: " << options;
- return false;
- }
-
- gJdwpConfigured = true;
- return true;
-}
-
-void Dbg::StartJdwp() {
+void Dbg::StartJdwp(const JDWP::JdwpOptions* jdwp_options) {
+ gJdwpConfigured = (jdwp_options != nullptr);
if (!gJdwpAllowed || !IsJdwpConfigured()) {
// No JDWP for you!
return;
}
+ DCHECK_NE(jdwp_options->transport, JDWP::kJdwpTransportUnknown);
CHECK(gRegistry == nullptr);
gRegistry = new ObjectRegistry;
@@ -657,7 +543,7 @@
// Init JDWP if the debugger is enabled. This may connect out to a
// debugger, passively listen for a debugger, or block waiting for a
// debugger.
- gJdwpState = JDWP::JdwpState::Create(&gJdwpOptions);
+ gJdwpState = JDWP::JdwpState::Create(jdwp_options);
if (gJdwpState == nullptr) {
// We probably failed because some other process has the port already, which means that
// if we don't abort the user is likely to think they're talking to us when they're actually
@@ -669,9 +555,7 @@
// This may cause us to suspend all threads.
if (gJdwpState->IsActive()) {
ScopedObjectAccess soa(Thread::Current());
- if (!gJdwpState->PostVMStart()) {
- LOG(WARNING) << "Failed to post 'start' message to debugger";
- }
+ gJdwpState->PostVMStart();
}
}
@@ -821,10 +705,15 @@
}
gDebuggerActive = false;
}
- gRegistry->Clear();
- gDebuggerConnected = false;
CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
runtime->GetThreadList()->ResumeAll();
+
+ {
+ ScopedObjectAccess soa(self);
+ gRegistry->Clear();
+ }
+
+ gDebuggerConnected = false;
}
bool Dbg::IsDebuggerActive() {
@@ -2193,6 +2082,7 @@
case kWaitingForJniOnLoad:
case kWaitingForMethodTracingStart:
case kWaitingForSignalCatcherOutput:
+ case kWaitingForVisitObjects:
case kWaitingInMainDebuggerLoop:
case kWaitingInMainSignalCatcherLoop:
case kWaitingPerformingGc:
@@ -2569,7 +2459,7 @@
VLOG(jdwp) << " --> slot " << slot << " " << reqSigByte;
size_t width = Dbg::GetTagWidth(reqSigByte);
- uint8_t* ptr = expandBufAddSpace(pReply, width+1);
+ uint8_t* ptr = expandBufAddSpace(pReply, width + 1);
JDWP::JdwpError error = Dbg::GetLocalValue(visitor, soa, slot, reqSigByte, ptr, width);
if (error != JDWP::ERR_NONE) {
return error;
@@ -2917,24 +2807,23 @@
// If the debugger is single-stepping one of our threads, check to
// see if we're that thread and we've reached a step point.
const SingleStepControl* single_step_control = thread->GetSingleStepControl();
- DCHECK(single_step_control != nullptr);
- if (single_step_control->is_active) {
+ if (single_step_control != nullptr) {
CHECK(!m->IsNative());
- if (single_step_control->step_depth == JDWP::SD_INTO) {
+ if (single_step_control->GetStepDepth() == JDWP::SD_INTO) {
// Step into method calls. We break when the line number
// or method pointer changes. If we're in SS_MIN mode, we
// always stop.
- if (single_step_control->method != m) {
+ if (single_step_control->GetMethod() != m) {
event_flags |= kSingleStep;
VLOG(jdwp) << "SS new method";
- } else if (single_step_control->step_size == JDWP::SS_MIN) {
+ } else if (single_step_control->GetStepSize() == JDWP::SS_MIN) {
event_flags |= kSingleStep;
VLOG(jdwp) << "SS new instruction";
} else if (single_step_control->ContainsDexPc(dex_pc)) {
event_flags |= kSingleStep;
VLOG(jdwp) << "SS new line";
}
- } else if (single_step_control->step_depth == JDWP::SD_OVER) {
+ } else if (single_step_control->GetStepDepth() == JDWP::SD_OVER) {
// Step over method calls. We break when the line number is
// different and the frame depth is <= the original frame
// depth. (We can't just compare on the method, because we
@@ -2943,13 +2832,13 @@
int stack_depth = GetStackDepth(thread);
- if (stack_depth < single_step_control->stack_depth) {
+ if (stack_depth < single_step_control->GetStackDepth()) {
// Popped up one or more frames, always trigger.
event_flags |= kSingleStep;
VLOG(jdwp) << "SS method pop";
- } else if (stack_depth == single_step_control->stack_depth) {
+ } else if (stack_depth == single_step_control->GetStackDepth()) {
// Same depth, see if we moved.
- if (single_step_control->step_size == JDWP::SS_MIN) {
+ if (single_step_control->GetStepSize() == JDWP::SS_MIN) {
event_flags |= kSingleStep;
VLOG(jdwp) << "SS new instruction";
} else if (single_step_control->ContainsDexPc(dex_pc)) {
@@ -2958,7 +2847,7 @@
}
}
} else {
- CHECK_EQ(single_step_control->step_depth, JDWP::SD_OUT);
+ CHECK_EQ(single_step_control->GetStepDepth(), JDWP::SD_OUT);
// Return from the current method. We break when the frame
// depth pops up.
@@ -2967,7 +2856,7 @@
// function, rather than the end of the returning function.
int stack_depth = GetStackDepth(thread);
- if (stack_depth < single_step_control->stack_depth) {
+ if (stack_depth < single_step_control->GetStackDepth()) {
event_flags |= kSingleStep;
VLOG(jdwp) << "SS method pop";
}
@@ -3194,7 +3083,7 @@
Handle<mirror::ArtMethod> method(hs.NewHandle(m));
verifier::MethodVerifier verifier(self, dex_cache->GetDexFile(), dex_cache, class_loader,
&m->GetClassDef(), code_item, m->GetDexMethodIndex(), method,
- m->GetAccessFlags(), false, true, false);
+ m->GetAccessFlags(), false, true, false, true);
// Note: we don't need to verify the method.
return InlineMethodAnalyser::AnalyseMethodCode(&verifier, nullptr);
}
@@ -3235,8 +3124,12 @@
}
}
+// Returns the deoptimization kind required to set a breakpoint in a method.
+// If a breakpoint has already been set, we also return the first breakpoint
+// through the given 'existing_brkpt' pointer.
static DeoptimizationRequest::Kind GetRequiredDeoptimizationKind(Thread* self,
- mirror::ArtMethod* m)
+ mirror::ArtMethod* m,
+ const Breakpoint** existing_brkpt)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (!Dbg::RequiresDeoptimization()) {
// We already run in interpreter-only mode so we don't need to deoptimize anything.
@@ -3244,12 +3137,14 @@
<< PrettyMethod(m);
return DeoptimizationRequest::kNothing;
}
- const Breakpoint* existing_breakpoint;
+ const Breakpoint* first_breakpoint;
{
ReaderMutexLock mu(self, *Locks::breakpoint_lock_);
- existing_breakpoint = FindFirstBreakpointForMethod(m);
+ first_breakpoint = FindFirstBreakpointForMethod(m);
+ *existing_brkpt = first_breakpoint;
}
- if (existing_breakpoint == nullptr) {
+
+ if (first_breakpoint == nullptr) {
// There is no breakpoint on this method yet: we need to deoptimize. If this method may be
// inlined, we deoptimize everything; otherwise we deoptimize only this method.
// Note: IsMethodPossiblyInlined goes into the method verifier and may cause thread suspension.
@@ -3264,8 +3159,16 @@
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
const bool is_compiled = class_linker->GetOatMethodQuickCodeFor(m) != nullptr;
if (is_compiled) {
- VLOG(jdwp) << "Need selective deoptimization for compiled method " << PrettyMethod(m);
- return DeoptimizationRequest::kSelectiveDeoptimization;
+ // If the method may be called through its direct code pointer (without loading
+ // its updated entrypoint), we need full deoptimization to not miss the breakpoint.
+ if (class_linker->MayBeCalledWithDirectCodePointer(m)) {
+ VLOG(jdwp) << "Need full deoptimization because of possible direct code call "
+ << "into image for compiled method " << PrettyMethod(m);
+ return DeoptimizationRequest::kFullDeoptimization;
+ } else {
+ VLOG(jdwp) << "Need selective deoptimization for compiled method " << PrettyMethod(m);
+ return DeoptimizationRequest::kSelectiveDeoptimization;
+ }
} else {
// Method is not compiled: we don't need to deoptimize.
VLOG(jdwp) << "No need for deoptimization for non-compiled method " << PrettyMethod(m);
@@ -3276,7 +3179,7 @@
// There is at least one breakpoint for this method: we don't need to deoptimize.
// Let's check that all breakpoints are configured the same way for deoptimization.
VLOG(jdwp) << "Breakpoint already set: no deoptimization is required";
- DeoptimizationRequest::Kind deoptimization_kind = existing_breakpoint->GetDeoptimizationKind();
+ DeoptimizationRequest::Kind deoptimization_kind = first_breakpoint->GetDeoptimizationKind();
if (kIsDebugBuild) {
ReaderMutexLock mu(self, *Locks::breakpoint_lock_);
SanityCheckExistingBreakpoints(m, deoptimization_kind);
@@ -3292,7 +3195,9 @@
mirror::ArtMethod* m = FromMethodId(location->method_id);
DCHECK(m != nullptr) << "No method for method id " << location->method_id;
- const DeoptimizationRequest::Kind deoptimization_kind = GetRequiredDeoptimizationKind(self, m);
+ const Breakpoint* existing_breakpoint = nullptr;
+ const DeoptimizationRequest::Kind deoptimization_kind =
+ GetRequiredDeoptimizationKind(self, m, &existing_breakpoint);
req->SetKind(deoptimization_kind);
if (deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization) {
req->SetMethod(m);
@@ -3304,7 +3209,15 @@
{
WriterMutexLock mu(self, *Locks::breakpoint_lock_);
- gBreakpoints.push_back(Breakpoint(m, location->dex_pc, deoptimization_kind));
+ // If there is at least one existing breakpoint on the same method, the new breakpoint
+ // must have the same deoptimization kind than the existing breakpoint(s).
+ DeoptimizationRequest::Kind breakpoint_deoptimization_kind;
+ if (existing_breakpoint != nullptr) {
+ breakpoint_deoptimization_kind = existing_breakpoint->GetDeoptimizationKind();
+ } else {
+ breakpoint_deoptimization_kind = deoptimization_kind;
+ }
+ gBreakpoints.push_back(Breakpoint(m, location->dex_pc, breakpoint_deoptimization_kind));
VLOG(jdwp) << "Set breakpoint #" << (gBreakpoints.size() - 1) << ": "
<< gBreakpoints[gBreakpoints.size() - 1];
}
@@ -3421,20 +3334,11 @@
return sts.GetError();
}
- //
- // Work out what Method* we're in, the current line number, and how deep the stack currently
+ // Work out what ArtMethod* we're in, the current line number, and how deep the stack currently
// is for step-out.
- //
-
struct SingleStepStackVisitor : public StackVisitor {
- explicit SingleStepStackVisitor(Thread* thread, SingleStepControl* single_step_control,
- int32_t* line_number)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : StackVisitor(thread, nullptr), single_step_control_(single_step_control),
- line_number_(line_number) {
- DCHECK_EQ(single_step_control_, thread->GetSingleStepControl());
- single_step_control_->method = nullptr;
- single_step_control_->stack_depth = 0;
+ explicit SingleStepStackVisitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : StackVisitor(thread, nullptr), stack_depth(0), method(nullptr), line_number(-1) {
}
// TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
@@ -3442,38 +3346,32 @@
bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
mirror::ArtMethod* m = GetMethod();
if (!m->IsRuntimeMethod()) {
- ++single_step_control_->stack_depth;
- if (single_step_control_->method == nullptr) {
+ ++stack_depth;
+ if (method == nullptr) {
mirror::DexCache* dex_cache = m->GetDeclaringClass()->GetDexCache();
- single_step_control_->method = m;
- *line_number_ = -1;
+ method = m;
if (dex_cache != nullptr) {
const DexFile& dex_file = *dex_cache->GetDexFile();
- *line_number_ = dex_file.GetLineNumFromPC(m, GetDexPc());
+ line_number = dex_file.GetLineNumFromPC(m, GetDexPc());
}
}
}
return true;
}
- SingleStepControl* const single_step_control_;
- int32_t* const line_number_;
+ int stack_depth;
+ mirror::ArtMethod* method;
+ int32_t line_number;
};
Thread* const thread = sts.GetThread();
- SingleStepControl* const single_step_control = thread->GetSingleStepControl();
- DCHECK(single_step_control != nullptr);
- int32_t line_number = -1;
- SingleStepStackVisitor visitor(thread, single_step_control, &line_number);
+ SingleStepStackVisitor visitor(thread);
visitor.WalkStack();
- //
// Find the dex_pc values that correspond to the current line, for line-based single-stepping.
- //
-
struct DebugCallbackContext {
- explicit DebugCallbackContext(SingleStepControl* single_step_control_cb, int32_t line_number_cb,
- const DexFile::CodeItem* code_item)
+ explicit DebugCallbackContext(SingleStepControl* single_step_control_cb,
+ int32_t line_number_cb, const DexFile::CodeItem* code_item)
: single_step_control_(single_step_control_cb), line_number_(line_number_cb),
code_item_(code_item), last_pc_valid(false), last_pc(0) {
}
@@ -3491,7 +3389,7 @@
} else if (context->last_pc_valid) { // and the line number is new
// Add everything from the last entry up until here to the set
for (uint32_t dex_pc = context->last_pc; dex_pc < address; ++dex_pc) {
- context->single_step_control_->dex_pcs.insert(dex_pc);
+ context->single_step_control_->AddDexPc(dex_pc);
}
context->last_pc_valid = false;
}
@@ -3503,7 +3401,7 @@
if (last_pc_valid) {
size_t end = code_item_->insns_size_in_code_units_;
for (uint32_t dex_pc = last_pc; dex_pc < end; ++dex_pc) {
- single_step_control_->dex_pcs.insert(dex_pc);
+ single_step_control_->AddDexPc(dex_pc);
}
}
}
@@ -3514,8 +3412,14 @@
bool last_pc_valid;
uint32_t last_pc;
};
- single_step_control->dex_pcs.clear();
- mirror::ArtMethod* m = single_step_control->method;
+
+ // Allocate single step.
+ SingleStepControl* single_step_control = new SingleStepControl(step_size, step_depth,
+ visitor.stack_depth,
+ visitor.method);
+ CHECK(single_step_control != nullptr) << "Failed to allocate SingleStepControl";
+ mirror::ArtMethod* m = single_step_control->GetMethod();
+ const int32_t line_number = visitor.line_number;
if (!m->IsNative()) {
const DexFile::CodeItem* const code_item = m->GetCodeItem();
DebugCallbackContext context(single_step_control, line_number, code_item);
@@ -3523,23 +3427,18 @@
DebugCallbackContext::Callback, nullptr, &context);
}
- //
- // Everything else...
- //
-
- single_step_control->step_size = step_size;
- single_step_control->step_depth = step_depth;
- single_step_control->is_active = true;
+ // Activate single-step in the thread.
+ thread->ActivateSingleStepControl(single_step_control);
if (VLOG_IS_ON(jdwp)) {
VLOG(jdwp) << "Single-step thread: " << *thread;
- VLOG(jdwp) << "Single-step step size: " << single_step_control->step_size;
- VLOG(jdwp) << "Single-step step depth: " << single_step_control->step_depth;
- VLOG(jdwp) << "Single-step current method: " << PrettyMethod(single_step_control->method);
+ VLOG(jdwp) << "Single-step step size: " << single_step_control->GetStepSize();
+ VLOG(jdwp) << "Single-step step depth: " << single_step_control->GetStepDepth();
+ VLOG(jdwp) << "Single-step current method: " << PrettyMethod(single_step_control->GetMethod());
VLOG(jdwp) << "Single-step current line: " << line_number;
- VLOG(jdwp) << "Single-step current stack depth: " << single_step_control->stack_depth;
+ VLOG(jdwp) << "Single-step current stack depth: " << single_step_control->GetStackDepth();
VLOG(jdwp) << "Single-step dex_pc values:";
- for (uint32_t dex_pc : single_step_control->dex_pcs) {
+ for (uint32_t dex_pc : single_step_control->GetDexPcs()) {
VLOG(jdwp) << StringPrintf(" %#x", dex_pc);
}
}
@@ -3553,9 +3452,7 @@
JDWP::JdwpError error;
Thread* thread = DecodeThread(soa, thread_id, &error);
if (error == JDWP::ERR_NONE) {
- SingleStepControl* single_step_control = thread->GetSingleStepControl();
- DCHECK(single_step_control != nullptr);
- single_step_control->Clear();
+ thread->DeactivateSingleStepControl();
}
}
@@ -4067,6 +3964,10 @@
}
}
+JDWP::JdwpState* Dbg::GetJdwpState() {
+ return gJdwpState;
+}
+
int Dbg::DdmHandleHpifChunk(HpifWhen when) {
if (when == HPIF_WHEN_NOW) {
DdmSendHeapInfo(when);
@@ -4230,10 +4131,15 @@
Reset();
}
- static void HeapChunkCallback(void* start, void* end, size_t used_bytes, void* arg)
+ static void HeapChunkJavaCallback(void* start, void* end, size_t used_bytes, void* arg)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_,
Locks::mutator_lock_) {
- reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkCallback(start, end, used_bytes);
+ reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkJavaCallback(start, end, used_bytes);
+ }
+
+ static void HeapChunkNativeCallback(void* start, void* end, size_t used_bytes, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkNativeCallback(start, end, used_bytes);
}
private:
@@ -4247,72 +4153,85 @@
pieceLenField_ = nullptr;
}
- void HeapChunkCallback(void* start, void* /*end*/, size_t used_bytes)
- SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_,
- Locks::mutator_lock_) {
+ bool IsNative() const {
+ return type_ == CHUNK_TYPE("NHSG");
+ }
+
+ // Returns true if the object is not an empty chunk.
+ bool ProcessRecord(void* start, size_t used_bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Note: heap call backs cannot manipulate the heap upon which they are crawling, care is taken
// in the following code not to allocate memory, by ensuring buf_ is of the correct size
if (used_bytes == 0) {
- if (start == nullptr) {
- // Reset for start of new heap.
- startOfNextMemoryChunk_ = nullptr;
- Flush();
- }
- // Only process in use memory so that free region information
- // also includes dlmalloc book keeping.
- return;
+ if (start == nullptr) {
+ // Reset for start of new heap.
+ startOfNextMemoryChunk_ = nullptr;
+ Flush();
+ }
+ // Only process in use memory so that free region information
+ // also includes dlmalloc book keeping.
+ return false;
}
-
- /* If we're looking at the native heap, we'll just return
- * (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks
- */
- bool native = type_ == CHUNK_TYPE("NHSG");
-
- // TODO: I'm not sure using start of next chunk works well with multiple spaces. We shouldn't
- // count gaps inbetween spaces as free memory.
if (startOfNextMemoryChunk_ != nullptr) {
- // Transmit any pending free memory. Native free memory of
- // over kMaxFreeLen could be because of the use of mmaps, so
- // don't report. If not free memory then start a new segment.
- bool flush = true;
- if (start > startOfNextMemoryChunk_) {
- const size_t kMaxFreeLen = 2 * kPageSize;
- void* freeStart = startOfNextMemoryChunk_;
- void* freeEnd = start;
- size_t freeLen = reinterpret_cast<char*>(freeEnd) - reinterpret_cast<char*>(freeStart);
- if (!native || freeLen < kMaxFreeLen) {
- AppendChunk(HPSG_STATE(SOLIDITY_FREE, 0), freeStart, freeLen);
- flush = false;
- }
+ // Transmit any pending free memory. Native free memory of over kMaxFreeLen could be because
+ // of the use of mmaps, so don't report. If not free memory then start a new segment.
+ bool flush = true;
+ if (start > startOfNextMemoryChunk_) {
+ const size_t kMaxFreeLen = 2 * kPageSize;
+ void* free_start = startOfNextMemoryChunk_;
+ void* free_end = start;
+ const size_t free_len =
+ reinterpret_cast<uintptr_t>(free_end) - reinterpret_cast<uintptr_t>(free_start);
+ if (!IsNative() || free_len < kMaxFreeLen) {
+ AppendChunk(HPSG_STATE(SOLIDITY_FREE, 0), free_start, free_len, IsNative());
+ flush = false;
}
- if (flush) {
- startOfNextMemoryChunk_ = nullptr;
- Flush();
- }
+ }
+ if (flush) {
+ startOfNextMemoryChunk_ = nullptr;
+ Flush();
+ }
}
- mirror::Object* obj = reinterpret_cast<mirror::Object*>(start);
-
- // Determine the type of this chunk.
- // OLD-TODO: if context.merge, see if this chunk is different from the last chunk.
- // If it's the same, we should combine them.
- uint8_t state = ExamineObject(obj, native);
- AppendChunk(state, start, used_bytes + chunk_overhead_);
- startOfNextMemoryChunk_ = reinterpret_cast<char*>(start) + used_bytes + chunk_overhead_;
+ return true;
}
- void AppendChunk(uint8_t state, void* ptr, size_t length)
+ void HeapChunkNativeCallback(void* start, void* /*end*/, size_t used_bytes)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (ProcessRecord(start, used_bytes)) {
+ uint8_t state = ExamineNativeObject(start);
+ AppendChunk(state, start, used_bytes + chunk_overhead_, true /*is_native*/);
+ startOfNextMemoryChunk_ = reinterpret_cast<char*>(start) + used_bytes + chunk_overhead_;
+ }
+ }
+
+ void HeapChunkJavaCallback(void* start, void* /*end*/, size_t used_bytes)
+ SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
+ if (ProcessRecord(start, used_bytes)) {
+ // Determine the type of this chunk.
+ // OLD-TODO: if context.merge, see if this chunk is different from the last chunk.
+ // If it's the same, we should combine them.
+ uint8_t state = ExamineJavaObject(reinterpret_cast<mirror::Object*>(start));
+ AppendChunk(state, start, used_bytes + chunk_overhead_, false /*is_native*/);
+ startOfNextMemoryChunk_ = reinterpret_cast<char*>(start) + used_bytes + chunk_overhead_;
+ }
+ }
+
+ void AppendChunk(uint8_t state, void* ptr, size_t length, bool is_native)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Make sure there's enough room left in the buffer.
// We need to use two bytes for every fractional 256 allocation units used by the chunk plus
// 17 bytes for any header.
- size_t needed = (((length/ALLOCATION_UNIT_SIZE + 255) / 256) * 2) + 17;
- size_t bytesLeft = buf_.size() - (size_t)(p_ - &buf_[0]);
- if (bytesLeft < needed) {
+ const size_t needed = ((RoundUp(length / ALLOCATION_UNIT_SIZE, 256) / 256) * 2) + 17;
+ size_t byte_left = &buf_.back() - p_;
+ if (byte_left < needed) {
+ if (is_native) {
+ // Cannot trigger memory allocation while walking native heap.
+ return;
+ }
Flush();
}
- bytesLeft = buf_.size() - (size_t)(p_ - &buf_[0]);
- if (bytesLeft < needed) {
+ byte_left = &buf_.back() - p_;
+ if (byte_left < needed) {
LOG(WARNING) << "Chunk is too big to transmit (chunk_len=" << length << ", "
<< needed << " bytes)";
return;
@@ -4330,43 +4249,38 @@
*p_++ = length - 1;
}
- uint8_t ExamineObject(mirror::Object* o, bool is_native_heap)
+ uint8_t ExamineNativeObject(const void* p) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return p == nullptr ? HPSG_STATE(SOLIDITY_FREE, 0) : HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
+ }
+
+ uint8_t ExamineJavaObject(mirror::Object* o)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
if (o == nullptr) {
return HPSG_STATE(SOLIDITY_FREE, 0);
}
-
// It's an allocated chunk. Figure out what it is.
-
- // If we're looking at the native heap, we'll just return
- // (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks.
- if (is_native_heap) {
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ if (!heap->IsLiveObjectLocked(o)) {
+ LOG(ERROR) << "Invalid object in managed heap: " << o;
return HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
}
-
- if (!Runtime::Current()->GetHeap()->IsLiveObjectLocked(o)) {
- return HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
- }
-
mirror::Class* c = o->GetClass();
if (c == nullptr) {
// The object was probably just created but hasn't been initialized yet.
return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
}
-
- if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(c)) {
+ if (!heap->IsValidObjectAddress(c)) {
LOG(ERROR) << "Invalid class for managed heap object: " << o << " " << c;
return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
}
-
+ if (c->GetClass() == nullptr) {
+ LOG(ERROR) << "Null class of class " << c << " for object " << o;
+ return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
+ }
if (c->IsClassClass()) {
return HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
}
-
if (c->IsArrayClass()) {
- if (o->IsObjectArray()) {
- return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
- }
switch (c->GetComponentSize()) {
case 1: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
case 2: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
@@ -4374,7 +4288,6 @@
case 8: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
}
}
-
return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
}
@@ -4393,41 +4306,33 @@
static void BumpPointerSpaceCallback(mirror::Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
const size_t size = RoundUp(obj->SizeOf(), kObjectAlignment);
- HeapChunkContext::HeapChunkCallback(
+ HeapChunkContext::HeapChunkJavaCallback(
obj, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(obj) + size), size, arg);
}
void Dbg::DdmSendHeapSegments(bool native) {
- Dbg::HpsgWhen when;
- Dbg::HpsgWhat what;
- if (!native) {
- when = gDdmHpsgWhen;
- what = gDdmHpsgWhat;
- } else {
- when = gDdmNhsgWhen;
- what = gDdmNhsgWhat;
- }
+ Dbg::HpsgWhen when = native ? gDdmNhsgWhen : gDdmHpsgWhen;
+ Dbg::HpsgWhat what = native ? gDdmNhsgWhat : gDdmHpsgWhat;
if (when == HPSG_WHEN_NEVER) {
return;
}
-
// Figure out what kind of chunks we'll be sending.
- CHECK(what == HPSG_WHAT_MERGED_OBJECTS || what == HPSG_WHAT_DISTINCT_OBJECTS) << static_cast<int>(what);
+ CHECK(what == HPSG_WHAT_MERGED_OBJECTS || what == HPSG_WHAT_DISTINCT_OBJECTS)
+ << static_cast<int>(what);
// First, send a heap start chunk.
uint8_t heap_id[4];
JDWP::Set4BE(&heap_id[0], 1); // Heap id (bogus; we only have one heap).
Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), sizeof(heap_id), heap_id);
-
Thread* self = Thread::Current();
-
Locks::mutator_lock_->AssertSharedHeld(self);
// Send a series of heap segment chunks.
- HeapChunkContext context((what == HPSG_WHAT_MERGED_OBJECTS), native);
+ HeapChunkContext context(what == HPSG_WHAT_MERGED_OBJECTS, native);
if (native) {
#if defined(HAVE_ANDROID_OS) && defined(USE_DLMALLOC)
- dlmalloc_inspect_all(HeapChunkContext::HeapChunkCallback, &context);
+ dlmalloc_inspect_all(HeapChunkContext::HeapChunkNativeCallback, &context);
+ HeapChunkContext::HeapChunkNativeCallback(nullptr, nullptr, 0, &context); // Indicate end of a space.
#else
UNIMPLEMENTED(WARNING) << "Native heap inspection is only supported with dlmalloc";
#endif
@@ -4439,7 +4344,7 @@
// dlmalloc's chunk header is 2 * sizeof(size_t), but if the previous chunk is in use for an
// allocation then the first sizeof(size_t) may belong to it.
context.SetChunkOverhead(sizeof(size_t));
- space->AsDlMallocSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context);
+ space->AsDlMallocSpace()->Walk(HeapChunkContext::HeapChunkJavaCallback, &context);
} else if (space->IsRosAllocSpace()) {
context.SetChunkOverhead(0);
// Need to acquire the mutator lock before the heap bitmap lock with exclusive access since
@@ -4449,7 +4354,7 @@
tl->SuspendAll();
{
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
- space->AsRosAllocSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context);
+ space->AsRosAllocSpace()->Walk(HeapChunkContext::HeapChunkJavaCallback, &context);
}
tl->ResumeAll();
self->TransitionFromSuspendedToRunnable();
@@ -4457,6 +4362,19 @@
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
context.SetChunkOverhead(0);
space->AsBumpPointerSpace()->Walk(BumpPointerSpaceCallback, &context);
+ HeapChunkContext::HeapChunkJavaCallback(nullptr, nullptr, 0, &context);
+ } else if (space->IsRegionSpace()) {
+ heap->IncrementDisableMovingGC(self);
+ self->TransitionFromRunnableToSuspended(kSuspended);
+ ThreadList* tl = Runtime::Current()->GetThreadList();
+ tl->SuspendAll();
+ ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ context.SetChunkOverhead(0);
+ space->AsRegionSpace()->Walk(BumpPointerSpaceCallback, &context);
+ HeapChunkContext::HeapChunkJavaCallback(nullptr, nullptr, 0, &context);
+ tl->ResumeAll();
+ self->TransitionFromSuspendedToRunnable();
+ heap->DecrementDisableMovingGC(self);
} else {
UNIMPLEMENTED(WARNING) << "Not counting objects in space " << *space;
}
@@ -4465,7 +4383,7 @@
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
// Walk the large objects, these are not in the AllocSpace.
context.SetChunkOverhead(0);
- heap->GetLargeObjectsSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context);
+ heap->GetLargeObjectsSpace()->Walk(HeapChunkContext::HeapChunkJavaCallback, &context);
}
// Finally, send a heap end chunk.
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 9203163..6762608 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -28,6 +28,7 @@
#include <string>
#include <vector>
+#include "gc_root.h"
#include "jdwp/jdwp.h"
#include "jni.h"
#include "jvalue.h"
@@ -87,7 +88,7 @@
Mutex lock DEFAULT_MUTEX_ACQUIRED_AFTER;
ConditionVariable cond GUARDED_BY(lock);
- void VisitRoots(RootCallback* callback, void* arg, uint32_t tid, RootType root_type)
+ void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void Clear();
@@ -97,39 +98,58 @@
};
// Thread local data-structure that holds fields for controlling single-stepping.
-struct SingleStepControl {
- SingleStepControl()
- : is_active(false), step_size(JDWP::SS_MIN), step_depth(JDWP::SD_INTO),
- method(nullptr), stack_depth(0) {
+class SingleStepControl {
+ public:
+ SingleStepControl(JDWP::JdwpStepSize step_size, JDWP::JdwpStepDepth step_depth,
+ int stack_depth, mirror::ArtMethod* method)
+ : step_size_(step_size), step_depth_(step_depth),
+ stack_depth_(stack_depth), method_(method) {
}
- // Are we single-stepping right now?
- bool is_active;
+ JDWP::JdwpStepSize GetStepSize() const {
+ return step_size_;
+ }
+ JDWP::JdwpStepDepth GetStepDepth() const {
+ return step_depth_;
+ }
+
+ int GetStackDepth() const {
+ return stack_depth_;
+ }
+
+ mirror::ArtMethod* GetMethod() const {
+ return method_;
+ }
+
+ const std::set<uint32_t>& GetDexPcs() const {
+ return dex_pcs_;
+ }
+
+ void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void AddDexPc(uint32_t dex_pc);
+
+ bool ContainsDexPc(uint32_t dex_pc) const;
+
+ private:
// See JdwpStepSize and JdwpStepDepth for details.
- JDWP::JdwpStepSize step_size;
- JDWP::JdwpStepDepth step_depth;
+ const JDWP::JdwpStepSize step_size_;
+ const JDWP::JdwpStepDepth step_depth_;
+
+ // The stack depth when this single-step was initiated. This is used to support SD_OVER and SD_OUT
+ // single-step depth.
+ const int stack_depth_;
// The location this single-step was initiated from.
// A single-step is initiated in a suspended thread. We save here the current method and the
// set of DEX pcs associated to the source line number where the suspension occurred.
// This is used to support SD_INTO and SD_OVER single-step depths so we detect when a single-step
// causes the execution of an instruction in a different method or at a different line number.
- mirror::ArtMethod* method;
- std::set<uint32_t> dex_pcs;
+ mirror::ArtMethod* method_;
+ std::set<uint32_t> dex_pcs_;
- // The stack depth when this single-step was initiated. This is used to support SD_OVER and SD_OUT
- // single-step depth.
- int stack_depth;
-
- void VisitRoots(RootCallback* callback, void* arg, uint32_t tid, RootType root_type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- bool ContainsDexPc(uint32_t dex_pc) const;
-
- void Clear();
-
- private:
DISALLOW_COPY_AND_ASSIGN(SingleStepControl);
};
@@ -206,10 +226,9 @@
std::multimap<int32_t, jobject> objects_;
};
- static bool ParseJdwpOptions(const std::string& options);
static void SetJdwpAllowed(bool allowed);
- static void StartJdwp();
+ static void StartJdwp(const JDWP::JdwpOptions* jdwp_options);
static void StopJdwp();
// Invoked by the GC in case we need to keep DDMS informed.
@@ -647,6 +666,8 @@
static void SetJdwpLocation(JDWP::JdwpLocation* location, mirror::ArtMethod* m, uint32_t dex_pc)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static JDWP::JdwpState* GetJdwpState();
+
private:
static JDWP::JdwpError GetLocalValue(const StackVisitor& visitor,
ScopedObjectAccessUnchecked& soa, int slot,
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 4f36c64..94d62db 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -125,7 +125,8 @@
}
bool DexFile::Open(const char* filename, const char* location, std::string* error_msg,
- std::vector<const DexFile*>* dex_files) {
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+ DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is NULL";
uint32_t magic;
ScopedFd fd(OpenAndReadMagic(filename, &magic, error_msg));
if (fd.get() == -1) {
@@ -139,7 +140,7 @@
std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.release(), location, true,
error_msg));
if (dex_file.get() != nullptr) {
- dex_files->push_back(dex_file.release());
+ dex_files->push_back(std::move(dex_file));
return true;
} else {
return false;
@@ -179,8 +180,8 @@
}
}
-const DexFile* DexFile::OpenFile(int fd, const char* location, bool verify,
- std::string* error_msg) {
+std::unique_ptr<const DexFile> DexFile::OpenFile(int fd, const char* location, bool verify,
+ std::string* error_msg) {
CHECK(location != nullptr);
std::unique_ptr<MemMap> map;
{
@@ -224,13 +225,14 @@
return nullptr;
}
- return dex_file.release();
+ return dex_file;
}
const char* DexFile::kClassesDex = "classes.dex";
bool DexFile::OpenZip(int fd, const std::string& location, std::string* error_msg,
- std::vector<const DexFile*>* dex_files) {
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+ DCHECK(dex_files != nullptr) << "DexFile::OpenZip: out-param is NULL";
std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg));
if (zip_archive.get() == nullptr) {
DCHECK(!error_msg->empty());
@@ -239,21 +241,22 @@
return DexFile::OpenFromZip(*zip_archive, location, error_msg, dex_files);
}
-const DexFile* DexFile::OpenMemory(const std::string& location,
- uint32_t location_checksum,
- MemMap* mem_map,
- std::string* error_msg) {
+std::unique_ptr<const DexFile> DexFile::OpenMemory(const std::string& location,
+ uint32_t location_checksum,
+ MemMap* mem_map,
+ std::string* error_msg) {
return OpenMemory(mem_map->Begin(),
mem_map->Size(),
location,
location_checksum,
mem_map,
+ nullptr,
error_msg);
}
-const DexFile* DexFile::Open(const ZipArchive& zip_archive, const char* entry_name,
- const std::string& location, std::string* error_msg,
- ZipOpenErrorCode* error_code) {
+std::unique_ptr<const DexFile> DexFile::Open(const ZipArchive& zip_archive, const char* entry_name,
+ const std::string& location, std::string* error_msg,
+ ZipOpenErrorCode* error_code) {
CHECK(!location.empty());
std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
if (zip_entry.get() == NULL) {
@@ -287,11 +290,13 @@
return nullptr;
}
*error_code = ZipOpenErrorCode::kNoError;
- return dex_file.release();
+ return dex_file;
}
bool DexFile::OpenFromZip(const ZipArchive& zip_archive, const std::string& location,
- std::string* error_msg, std::vector<const DexFile*>* dex_files) {
+ std::string* error_msg,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+ DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is NULL";
ZipOpenErrorCode error_code;
std::unique_ptr<const DexFile> dex_file(Open(zip_archive, kClassesDex, location, error_msg,
&error_code));
@@ -299,7 +304,7 @@
return false;
} else {
// Had at least classes.dex.
- dex_files->push_back(dex_file.release());
+ dex_files->push_back(std::move(dex_file));
// Now try some more.
size_t i = 2;
@@ -318,7 +323,7 @@
}
break;
} else {
- dex_files->push_back(next_dex_file.release());
+ dex_files->push_back(std::move(next_dex_file));
}
i++;
@@ -329,24 +334,27 @@
}
-const DexFile* DexFile::OpenMemory(const uint8_t* base,
- size_t size,
- const std::string& location,
- uint32_t location_checksum,
- MemMap* mem_map, std::string* error_msg) {
+std::unique_ptr<const DexFile> DexFile::OpenMemory(const uint8_t* base,
+ size_t size,
+ const std::string& location,
+ uint32_t location_checksum,
+ MemMap* mem_map,
+ const OatFile* oat_file,
+ std::string* error_msg) {
CHECK_ALIGNED(base, 4); // various dex file structures must be word aligned
- std::unique_ptr<DexFile> dex_file(new DexFile(base, size, location, location_checksum, mem_map));
+ std::unique_ptr<DexFile> dex_file(
+ new DexFile(base, size, location, location_checksum, mem_map, oat_file));
if (!dex_file->Init(error_msg)) {
- return nullptr;
- } else {
- return dex_file.release();
+ dex_file.reset();
}
+ return std::unique_ptr<const DexFile>(dex_file.release());
}
DexFile::DexFile(const uint8_t* base, size_t size,
const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map)
+ MemMap* mem_map,
+ const OatFile* oat_file)
: begin_(base),
size_(size),
location_(location),
@@ -360,7 +368,8 @@
proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)),
class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
find_class_def_misses_(0),
- class_def_index_(nullptr) {
+ class_def_index_(nullptr),
+ oat_file_(oat_file) {
CHECK(begin_ != NULL) << GetLocation();
CHECK_GT(size_, 0U) << GetLocation();
}
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index d8b1525..e121a08 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -44,6 +44,7 @@
} // namespace mirror
class ClassLinker;
class MemMap;
+class OatFile;
class Signature;
template<class T> class Handle;
class StringPiece;
@@ -385,19 +386,21 @@
// Opens .dex files found in the container, guessing the container format based on file extension.
static bool Open(const char* filename, const char* location, std::string* error_msg,
- std::vector<const DexFile*>* dex_files);
+ std::vector<std::unique_ptr<const DexFile>>* dex_files);
// Opens .dex file, backed by existing memory
- static const DexFile* Open(const uint8_t* base, size_t size,
- const std::string& location,
- uint32_t location_checksum,
- std::string* error_msg) {
- return OpenMemory(base, size, location, location_checksum, NULL, error_msg);
+ static std::unique_ptr<const DexFile> Open(const uint8_t* base, size_t size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatFile* oat_file,
+ std::string* error_msg) {
+ return OpenMemory(base, size, location, location_checksum, NULL, oat_file, error_msg);
}
// Open all classesXXX.dex files from a zip archive.
static bool OpenFromZip(const ZipArchive& zip_archive, const std::string& location,
- std::string* error_msg, std::vector<const DexFile*>* dex_files);
+ std::string* error_msg,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files);
// Closes a .dex file.
virtual ~DexFile();
@@ -890,13 +893,18 @@
// the dex_location where it's file name part has been made canonical.
static std::string GetDexCanonicalLocation(const char* dex_location);
+ const OatFile* GetOatFile() const {
+ return oat_file_;
+ }
+
private:
// Opens a .dex file
- static const DexFile* OpenFile(int fd, const char* location, bool verify, std::string* error_msg);
+ static std::unique_ptr<const DexFile> OpenFile(int fd, const char* location,
+ bool verify, std::string* error_msg);
// Opens dex files from within a .jar, .zip, or .apk file
static bool OpenZip(int fd, const std::string& location, std::string* error_msg,
- std::vector<const DexFile*>* dex_files);
+ std::vector<std::unique_ptr<const DexFile>>* dex_files);
enum class ZipOpenErrorCode { // private
kNoError,
@@ -909,28 +917,30 @@
// Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-nullptr
// return.
- static const DexFile* Open(const ZipArchive& zip_archive, const char* entry_name,
- const std::string& location, std::string* error_msg,
- ZipOpenErrorCode* error_code);
+ static std::unique_ptr<const DexFile> Open(const ZipArchive& zip_archive, const char* entry_name,
+ const std::string& location, std::string* error_msg,
+ ZipOpenErrorCode* error_code);
// Opens a .dex file at the given address backed by a MemMap
- static const DexFile* OpenMemory(const std::string& location,
- uint32_t location_checksum,
- MemMap* mem_map,
- std::string* error_msg);
+ static std::unique_ptr<const DexFile> OpenMemory(const std::string& location,
+ uint32_t location_checksum,
+ MemMap* mem_map,
+ std::string* error_msg);
// Opens a .dex file at the given address, optionally backed by a MemMap
- static const DexFile* OpenMemory(const uint8_t* dex_file,
- size_t size,
- const std::string& location,
- uint32_t location_checksum,
- MemMap* mem_map,
- std::string* error_msg);
+ static std::unique_ptr<const DexFile> OpenMemory(const uint8_t* dex_file,
+ size_t size,
+ const std::string& location,
+ uint32_t location_checksum,
+ MemMap* mem_map,
+ const OatFile* oat_file,
+ std::string* error_msg);
DexFile(const uint8_t* base, size_t size,
const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map);
+ MemMap* mem_map,
+ const OatFile* oat_file);
// Top-level initializer that calls other Init methods.
bool Init(std::string* error_msg);
@@ -1013,6 +1023,10 @@
};
typedef HashMap<const char*, const ClassDef*, UTF16EmptyFn, UTF16HashCmp, UTF16HashCmp> Index;
mutable Atomic<Index*> class_def_index_;
+
+ // The oat file this dex file was loaded from. May be null in case the dex file is not coming
+ // from an oat file, e.g., directly from an apk.
+ const OatFile* oat_file_;
};
std::ostream& operator<<(std::ostream& os, const DexFile& dex_file);
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index b304779..7f5a181 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -21,6 +21,7 @@
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "common_runtime_test.h"
+#include "dex_file-inl.h"
#include "os.h"
#include "scoped_thread_state_change.h"
#include "thread-inl.h"
@@ -31,8 +32,8 @@
TEST_F(DexFileTest, Open) {
ScopedObjectAccess soa(Thread::Current());
- const DexFile* dex(OpenTestDexFile("Nested"));
- ASSERT_TRUE(dex != NULL);
+ std::unique_ptr<const DexFile> dex(OpenTestDexFile("Nested"));
+ ASSERT_TRUE(dex.get() != NULL);
}
static const uint8_t kBase64Map[256] = {
@@ -132,8 +133,8 @@
"AAACAAAAQAEAAAEgAAACAAAAVAEAAAYgAAACAAAAiAEAAAEQAAABAAAAqAEAAAIgAAAPAAAArgEA"
"AAMgAAACAAAAiAIAAAQgAAADAAAAlAIAAAAgAAACAAAAqwIAAAAQAAABAAAAxAIAAA==";
-static const DexFile* OpenDexFileBase64(const char* base64,
- const char* location) {
+static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
+ const char* location) {
// decode base64
CHECK(base64 != NULL);
size_t length;
@@ -154,11 +155,11 @@
// read dex file
ScopedObjectAccess soa(Thread::Current());
std::string error_msg;
- std::vector<const DexFile*> tmp;
+ std::vector<std::unique_ptr<const DexFile>> tmp;
bool success = DexFile::Open(location, location, &error_msg, &tmp);
CHECK(success) << error_msg;
EXPECT_EQ(1U, tmp.size());
- const DexFile* dex_file = tmp[0];
+ std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
EXPECT_EQ(PROT_READ, dex_file->GetPermissions());
EXPECT_TRUE(dex_file->IsReadOnly());
return dex_file;
@@ -197,7 +198,7 @@
TEST_F(DexFileTest, GetLocationChecksum) {
ScopedObjectAccess soa(Thread::Current());
- const DexFile* raw(OpenTestDexFile("Main"));
+ std::unique_ptr<const DexFile> raw(OpenTestDexFile("Main"));
EXPECT_NE(raw->GetHeader().checksum_, raw->GetLocationChecksum());
}
@@ -212,8 +213,8 @@
TEST_F(DexFileTest, ClassDefs) {
ScopedObjectAccess soa(Thread::Current());
- const DexFile* raw(OpenTestDexFile("Nested"));
- ASSERT_TRUE(raw != NULL);
+ std::unique_ptr<const DexFile> raw(OpenTestDexFile("Nested"));
+ ASSERT_TRUE(raw.get() != nullptr);
EXPECT_EQ(2U, raw->NumClassDefs());
const DexFile::ClassDef& c0 = raw->GetClassDef(0);
@@ -225,8 +226,8 @@
TEST_F(DexFileTest, GetMethodSignature) {
ScopedObjectAccess soa(Thread::Current());
- const DexFile* raw(OpenTestDexFile("GetMethodSignature"));
- ASSERT_TRUE(raw != NULL);
+ std::unique_ptr<const DexFile> raw(OpenTestDexFile("GetMethodSignature"));
+ ASSERT_TRUE(raw.get() != nullptr);
EXPECT_EQ(1U, raw->NumClassDefs());
const DexFile::ClassDef& class_def = raw->GetClassDef(0);
@@ -275,8 +276,8 @@
TEST_F(DexFileTest, FindStringId) {
ScopedObjectAccess soa(Thread::Current());
- const DexFile* raw(OpenTestDexFile("GetMethodSignature"));
- ASSERT_TRUE(raw != NULL);
+ std::unique_ptr<const DexFile> raw(OpenTestDexFile("GetMethodSignature"));
+ ASSERT_TRUE(raw.get() != nullptr);
EXPECT_EQ(1U, raw->NumClassDefs());
const char* strings[] = { "LGetMethodSignature;", "Ljava/lang/Float;", "Ljava/lang/Object;",
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index ec1e5f0..00ca8a9 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -101,8 +101,9 @@
return dst.release();
}
-static const DexFile* OpenDexFileBase64(const char* base64, const char* location,
- std::string* error_msg) {
+static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
+ const char* location,
+ std::string* error_msg) {
// decode base64
CHECK(base64 != NULL);
size_t length;
@@ -122,11 +123,11 @@
// read dex file
ScopedObjectAccess soa(Thread::Current());
- std::vector<const DexFile*> tmp;
+ std::vector<std::unique_ptr<const DexFile>> tmp;
bool success = DexFile::Open(location, location, error_msg, &tmp);
CHECK(success) << error_msg;
EXPECT_EQ(1U, tmp.size());
- const DexFile* dex_file = tmp[0];
+ std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
EXPECT_EQ(PROT_READ, dex_file->GetPermissions());
EXPECT_TRUE(dex_file->IsReadOnly());
return dex_file;
@@ -166,8 +167,9 @@
header->checksum_ = adler_checksum;
}
-static const DexFile* FixChecksumAndOpen(uint8_t* bytes, size_t length, const char* location,
- std::string* error_msg) {
+static std::unique_ptr<const DexFile> FixChecksumAndOpen(uint8_t* bytes, size_t length,
+ const char* location,
+ std::string* error_msg) {
// Check data.
CHECK(bytes != nullptr);
@@ -187,12 +189,12 @@
// read dex file
ScopedObjectAccess soa(Thread::Current());
- std::vector<const DexFile*> tmp;
+ std::vector<std::unique_ptr<const DexFile>> tmp;
if (!DexFile::Open(location, location, error_msg, &tmp)) {
return nullptr;
}
EXPECT_EQ(1U, tmp.size());
- const DexFile* dex_file = tmp[0];
+ std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
EXPECT_EQ(PROT_READ, dex_file->GetPermissions());
EXPECT_TRUE(dex_file->IsReadOnly());
return dex_file;
diff --git a/runtime/dex_instruction_list.h b/runtime/dex_instruction_list.h
index 05214a4..a90f424 100644
--- a/runtime/dex_instruction_list.h
+++ b/runtime/dex_instruction_list.h
@@ -257,10 +257,10 @@
V(0xEC, IPUT_BYTE_QUICK, "iput-byte-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
V(0xED, IPUT_CHAR_QUICK, "iput-char-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
V(0xEE, IPUT_SHORT_QUICK, "iput-short-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
- V(0xEF, UNUSED_EF, "unused-ef", k10x, false, kUnknown, 0, kVerifyError) \
- V(0xF0, UNUSED_F0, "unused-f0", k10x, false, kUnknown, 0, kVerifyError) \
- V(0xF1, UNUSED_F1, "unused-f1", k10x, false, kUnknown, 0, kVerifyError) \
- V(0xF2, UNUSED_F2, "unused-f2", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xEF, IGET_BOOLEAN_QUICK, "iget-boolean-quick", k22c, true, kFieldRef, kContinue | kThrow | kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xF0, IGET_BYTE_QUICK, "iget-byte-quick", k22c, true, kFieldRef, kContinue | kThrow | kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xF1, IGET_CHAR_QUICK, "iget-char-quick", k22c, true, kFieldRef, kContinue | kThrow | kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xF2, IGET_SHORT_QUICK, "iget-short-quick", k22c, true, kFieldRef, kContinue | kThrow | kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
V(0xF3, UNUSED_F3, "unused-f3", k10x, false, kUnknown, 0, kVerifyError) \
V(0xF4, UNUSED_F4, "unused-f4", k10x, false, kUnknown, 0, kVerifyError) \
V(0xF5, UNUSED_F5, "unused-f5", k10x, false, kUnknown, 0, kVerifyError) \
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 6597235..a22e274 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -488,6 +488,20 @@
return false;
}
+ // We'd also like to confirm a shstrtab in program_header_only_ mode (else Open() does this for
+ // us). This is usually the last in an oat file, and a good indicator of whether writing was
+ // successful (or the process crashed and left garbage).
+ if (program_header_only_) {
+ // It might not be mapped, but we can compare against the file size.
+ int64_t offset = static_cast<int64_t>(GetHeader().e_shoff +
+ (GetHeader().e_shstrndx * GetHeader().e_shentsize));
+ if (offset >= file_->GetLength()) {
+ *error_msg = StringPrintf("Shstrtab is not in the mapped ELF file: '%s'",
+ file_->GetPath().c_str());
+ return false;
+ }
+ }
+
return true;
}
@@ -1299,30 +1313,7 @@
CHECK(program_header_only_) << file_->GetPath();
if (executable) {
- InstructionSet elf_ISA = kNone;
- switch (GetHeader().e_machine) {
- case EM_ARM: {
- elf_ISA = kArm;
- break;
- }
- case EM_AARCH64: {
- elf_ISA = kArm64;
- break;
- }
- case EM_386: {
- elf_ISA = kX86;
- break;
- }
- case EM_X86_64: {
- elf_ISA = kX86_64;
- break;
- }
- case EM_MIPS: {
- elf_ISA = kMips;
- break;
- }
- }
-
+ InstructionSet elf_ISA = GetInstructionSetFromELF(GetHeader().e_machine, GetHeader().e_flags);
if (elf_ISA != kRuntimeISA) {
std::ostringstream oss;
oss << "Expected ISA " << kRuntimeISA << " but found " << elf_ISA;
diff --git a/runtime/elf_utils.h b/runtime/elf_utils.h
index 7b00bad..418d937 100644
--- a/runtime/elf_utils.h
+++ b/runtime/elf_utils.h
@@ -30,6 +30,8 @@
#define EF_ARM_EABI_VER5 0x05000000
#define EF_MIPS_ABI_O32 0x00001000
#define EF_MIPS_ARCH_32R2 0x70000000
+#define EF_MIPS_ARCH_32R6 0x90000000
+#define EF_MIPS_ARCH_64R6 0xa0000000
#define EI_ABIVERSION 8
#define EM_ARM 40
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 67265a2..35579d6 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -172,8 +172,8 @@
template <bool kAccessCheck>
ALWAYS_INLINE
inline mirror::Class* CheckArrayAlloc(uint32_t type_idx,
- mirror::ArtMethod* method,
int32_t component_count,
+ mirror::ArtMethod* method,
bool* slow_path) {
if (UNLIKELY(component_count < 0)) {
ThrowNegativeArraySizeException(component_count);
@@ -208,12 +208,12 @@
template <bool kAccessCheck, bool kInstrumented>
ALWAYS_INLINE
inline mirror::Array* AllocArrayFromCode(uint32_t type_idx,
- mirror::ArtMethod* method,
int32_t component_count,
+ mirror::ArtMethod* method,
Thread* self,
gc::AllocatorType allocator_type) {
bool slow_path = false;
- mirror::Class* klass = CheckArrayAlloc<kAccessCheck>(type_idx, method, component_count,
+ mirror::Class* klass = CheckArrayAlloc<kAccessCheck>(type_idx, component_count, method,
&slow_path);
if (UNLIKELY(slow_path)) {
if (klass == nullptr) {
@@ -231,8 +231,8 @@
template <bool kAccessCheck, bool kInstrumented>
ALWAYS_INLINE
inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass,
- mirror::ArtMethod* method,
int32_t component_count,
+ mirror::ArtMethod* method,
Thread* self,
gc::AllocatorType allocator_type) {
DCHECK(klass != nullptr);
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index c329fe6..db51264 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -33,8 +33,8 @@
namespace art {
static inline mirror::Class* CheckFilledNewArrayAlloc(uint32_t type_idx,
- mirror::ArtMethod* referrer,
int32_t component_count,
+ mirror::ArtMethod* referrer,
Thread* self,
bool access_check)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -76,11 +76,11 @@
}
// Helper function to allocate array for FILLED_NEW_ARRAY.
-mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* referrer,
- int32_t component_count, Thread* self,
+mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, int32_t component_count,
+ mirror::ArtMethod* referrer, Thread* self,
bool access_check,
gc::AllocatorType /* allocator_type */) {
- mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self,
+ mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self,
access_check);
if (UNLIKELY(klass == nullptr)) {
return nullptr;
@@ -96,12 +96,12 @@
// Helper function to allocate array for FILLED_NEW_ARRAY.
mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx,
- mirror::ArtMethod* referrer,
int32_t component_count,
+ mirror::ArtMethod* referrer,
Thread* self,
bool access_check,
gc::AllocatorType /* allocator_type */) {
- mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self,
+ mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self,
access_check);
if (UNLIKELY(klass == nullptr)) {
return nullptr;
@@ -183,14 +183,12 @@
env->SetObjectField(exc.get(),
WellKnownClasses::java_lang_Throwable_stackTrace,
stack_trace_elem.get());
-
- // Throw the exception.
- ThrowLocation throw_location = self->GetCurrentLocationForThrow();
- self->SetException(throw_location,
- reinterpret_cast<mirror::Throwable*>(self->DecodeJObject(exc.get())));
} else {
error_msg = "Could not create stack trace.";
}
+ // Throw the exception.
+ self->SetException(self->GetCurrentLocationForThrow(),
+ reinterpret_cast<mirror::Throwable*>(self->DecodeJObject(exc.get())));
} else {
// Could not allocate a string object.
error_msg = "Couldn't throw new StackOverflowError because JNI NewStringUTF failed.";
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 0531122..77eec46 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -80,8 +80,8 @@
template <bool kAccessCheck>
ALWAYS_INLINE inline mirror::Class* CheckArrayAlloc(uint32_t type_idx,
- mirror::ArtMethod* method,
int32_t component_count,
+ mirror::ArtMethod* method,
bool* slow_path)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -91,29 +91,30 @@
// check.
template <bool kAccessCheck, bool kInstrumented>
ALWAYS_INLINE inline mirror::Array* AllocArrayFromCode(uint32_t type_idx,
- mirror::ArtMethod* method,
int32_t component_count,
+ mirror::ArtMethod* method,
Thread* self,
gc::AllocatorType allocator_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template <bool kAccessCheck, bool kInstrumented>
ALWAYS_INLINE inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass,
- mirror::ArtMethod* method,
int32_t component_count,
+ mirror::ArtMethod* method,
Thread* self,
gc::AllocatorType allocator_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-extern mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* method,
- int32_t component_count, Thread* self,
+extern mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, int32_t component_count,
+ mirror::ArtMethod* method, Thread* self,
bool access_check,
gc::AllocatorType allocator_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
extern mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx,
+ int32_t component_count,
mirror::ArtMethod* method,
- int32_t component_count, Thread* self,
+ Thread* self,
bool access_check,
gc::AllocatorType allocator_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
index 3b47f24..28e19d4 100644
--- a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
+++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
@@ -15,6 +15,7 @@
*/
#include "class_linker.h"
+#include "dex_file-inl.h"
#include "interpreter/interpreter.h"
#include "mirror/art_method-inl.h"
#include "mirror/object-inl.h"
@@ -47,13 +48,9 @@
}
}
uint16_t arg_offset = (code_item == NULL) ? 0 : code_item->registers_size_ - code_item->ins_size_;
- if (kUsePortableCompiler) {
- InvokeWithShadowFrame(self, shadow_frame, arg_offset, result);
- } else {
- method->Invoke(self, shadow_frame->GetVRegArgs(arg_offset),
- (shadow_frame->NumberOfVRegs() - arg_offset) * sizeof(uint32_t),
- result, method->GetShorty());
- }
+ method->Invoke(self, shadow_frame->GetVRegArgs(arg_offset),
+ (shadow_frame->NumberOfVRegs() - arg_offset) * sizeof(uint32_t),
+ result, method->GetShorty());
}
} // namespace art
diff --git a/runtime/entrypoints/portable/portable_alloc_entrypoints.cc b/runtime/entrypoints/portable/portable_alloc_entrypoints.cc
deleted file mode 100644
index de95f7d..0000000
--- a/runtime/entrypoints/portable/portable_alloc_entrypoints.cc
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
-
-namespace art {
-
-static constexpr gc::AllocatorType kPortableAllocatorType =
- gc::kUseRosAlloc ? gc::kAllocatorTypeRosAlloc : gc::kAllocatorTypeDlMalloc;
-
-extern "C" mirror::Object* art_portable_alloc_object_from_code(uint32_t type_idx,
- mirror::ArtMethod* referrer,
- Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return AllocObjectFromCode<false, true>(type_idx, referrer, thread, kPortableAllocatorType);
-}
-
-extern "C" mirror::Object* art_portable_alloc_object_from_code_with_access_check(uint32_t type_idx,
- mirror::ArtMethod* referrer,
- Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return AllocObjectFromCode<true, true>(type_idx, referrer, thread, kPortableAllocatorType);
-}
-
-extern "C" mirror::Object* art_portable_alloc_array_from_code(uint32_t type_idx,
- mirror::ArtMethod* referrer,
- uint32_t length,
- Thread* self)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return AllocArrayFromCode<false, true>(type_idx, referrer, length, self,
- kPortableAllocatorType);
-}
-
-extern "C" mirror::Object* art_portable_alloc_array_from_code_with_access_check(uint32_t type_idx,
- mirror::ArtMethod* referrer,
- uint32_t length,
- Thread* self)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return AllocArrayFromCode<true, true>(type_idx, referrer, length, self,
- kPortableAllocatorType);
-}
-
-extern "C" mirror::Object* art_portable_check_and_alloc_array_from_code(uint32_t type_idx,
- mirror::ArtMethod* referrer,
- uint32_t length,
- Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return CheckAndAllocArrayFromCodeInstrumented(type_idx, referrer, length, thread, false,
- kPortableAllocatorType);
-}
-
-extern "C" mirror::Object* art_portable_check_and_alloc_array_from_code_with_access_check(uint32_t type_idx,
- mirror::ArtMethod* referrer,
- uint32_t length,
- Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return CheckAndAllocArrayFromCodeInstrumented(type_idx, referrer, length, thread, true,
- kPortableAllocatorType);
-}
-
-} // namespace art
diff --git a/runtime/entrypoints/portable/portable_cast_entrypoints.cc b/runtime/entrypoints/portable/portable_cast_entrypoints.cc
deleted file mode 100644
index 151b178..0000000
--- a/runtime/entrypoints/portable/portable_cast_entrypoints.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "common_throws.h"
-#include "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/object-inl.h"
-
-namespace art {
-
-extern "C" int32_t art_portable_is_assignable_from_code(mirror::Class* dest_type,
- mirror::Class* src_type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(dest_type != NULL);
- DCHECK(src_type != NULL);
- return dest_type->IsAssignableFrom(src_type) ? 1 : 0;
-}
-
-extern "C" void art_portable_check_cast_from_code(mirror::Class* dest_type,
- mirror::Class* src_type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(dest_type->IsClass()) << PrettyClass(dest_type);
- DCHECK(src_type->IsClass()) << PrettyClass(src_type);
- if (UNLIKELY(!dest_type->IsAssignableFrom(src_type))) {
- ThrowClassCastException(dest_type, src_type);
- }
-}
-
-extern "C" void art_portable_check_put_array_element_from_code(mirror::Object* element,
- mirror::Object* array)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (element == NULL) {
- return;
- }
- DCHECK(array != NULL);
- mirror::Class* array_class = array->GetClass();
- DCHECK(array_class != NULL);
- mirror::Class* component_type = array_class->GetComponentType();
- mirror::Class* element_class = element->GetClass();
- if (UNLIKELY(!component_type->IsAssignableFrom(element_class))) {
- ThrowArrayStoreException(element_class, array_class);
- }
-}
-
-} // namespace art
diff --git a/runtime/entrypoints/portable/portable_dexcache_entrypoints.cc b/runtime/entrypoints/portable/portable_dexcache_entrypoints.cc
deleted file mode 100644
index 9364c46..0000000
--- a/runtime/entrypoints/portable/portable_dexcache_entrypoints.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "entrypoints/entrypoint_utils-inl.h"
-#include "gc/accounting/card_table-inl.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
-
-namespace art {
-
-extern "C" mirror::Object* art_portable_initialize_static_storage_from_code(uint32_t type_idx,
- mirror::ArtMethod* referrer,
- Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return ResolveVerifyAndClinit(type_idx, referrer, thread, true, false);
-}
-
-extern "C" mirror::Object* art_portable_initialize_type_from_code(uint32_t type_idx,
- mirror::ArtMethod* referrer,
- Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return ResolveVerifyAndClinit(type_idx, referrer, thread, false, false);
-}
-
-extern "C" mirror::Object* art_portable_initialize_type_and_verify_access_from_code(uint32_t type_idx,
- mirror::ArtMethod* referrer,
- Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- // Called when caller isn't guaranteed to have access to a type and the dex cache may be
- // unpopulated
- return ResolveVerifyAndClinit(type_idx, referrer, thread, false, true);
-}
-
-extern "C" mirror::Object* art_portable_resolve_string_from_code(mirror::ArtMethod* referrer,
- uint32_t string_idx)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return ResolveStringFromCode(referrer, string_idx);
-}
-
-} // namespace art
diff --git a/runtime/entrypoints/portable/portable_entrypoints.h b/runtime/entrypoints/portable/portable_entrypoints.h
deleted file mode 100644
index 6f77e1c..0000000
--- a/runtime/entrypoints/portable/portable_entrypoints.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_ENTRYPOINTS_PORTABLE_PORTABLE_ENTRYPOINTS_H_
-#define ART_RUNTIME_ENTRYPOINTS_PORTABLE_PORTABLE_ENTRYPOINTS_H_
-
-#include "dex_file-inl.h"
-#include "runtime.h"
-
-namespace art {
-namespace mirror {
- class ArtMethod;
- class Object;
-} // namespace mirror
-class Thread;
-
-#define PORTABLE_ENTRYPOINT_OFFSET(ptr_size, x) \
- Thread::PortableEntryPointOffset<ptr_size>(OFFSETOF_MEMBER(PortableEntryPoints, x))
-
-// Pointers to functions that are called by code generated by compiler's adhering to the portable
-// compiler ABI.
-struct PACKED(4) PortableEntryPoints {
- // Invocation
- void (*pPortableImtConflictTrampoline)(mirror::ArtMethod*);
- void (*pPortableResolutionTrampoline)(mirror::ArtMethod*);
- void (*pPortableToInterpreterBridge)(mirror::ArtMethod*);
-};
-
-} // namespace art
-
-#endif // ART_RUNTIME_ENTRYPOINTS_PORTABLE_PORTABLE_ENTRYPOINTS_H_
diff --git a/runtime/entrypoints/portable/portable_field_entrypoints.cc b/runtime/entrypoints/portable/portable_field_entrypoints.cc
deleted file mode 100644
index 371aca4..0000000
--- a/runtime/entrypoints/portable/portable_field_entrypoints.cc
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/art_field-inl.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
-
-namespace art {
-
-extern "C" int32_t art_portable_set32_static_from_code(uint32_t field_idx,
- mirror::ArtMethod* referrer,
- int32_t new_value)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtField* field = FindFieldFast(field_idx,
- referrer,
- StaticPrimitiveWrite,
- sizeof(uint32_t));
- if (LIKELY(field != NULL)) {
- // Compiled code can't use transactional mode.
- field->Set32<false>(field->GetDeclaringClass(), new_value);
- return 0;
- }
- field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
- sizeof(uint32_t));
- if (LIKELY(field != NULL)) {
- // Compiled code can't use transactional mode.
- field->Set32<false>(field->GetDeclaringClass(), new_value);
- return 0;
- }
- return -1;
-}
-
-extern "C" int32_t art_portable_set64_static_from_code(uint32_t field_idx,
- mirror::ArtMethod* referrer,
- int64_t new_value)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(uint64_t));
- if (LIKELY(field != NULL)) {
- // Compiled code can't use transactional mode.
- field->Set64<false>(field->GetDeclaringClass(), new_value);
- return 0;
- }
- field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
- sizeof(uint64_t));
- if (LIKELY(field != NULL)) {
- // Compiled code can't use transactional mode.
- field->Set64<false>(field->GetDeclaringClass(), new_value);
- return 0;
- }
- return -1;
-}
-
-extern "C" int32_t art_portable_set_obj_static_from_code(uint32_t field_idx,
- mirror::ArtMethod* referrer,
- mirror::Object* new_value)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticObjectWrite,
- sizeof(mirror::HeapReference<mirror::Object>));
- if (LIKELY(field != NULL)) {
- // Compiled code can't use transactional mode.
- field->SetObj<false>(field->GetDeclaringClass(), new_value);
- return 0;
- }
- field = FindFieldFromCode<StaticObjectWrite, true>(field_idx, referrer, Thread::Current(),
- sizeof(mirror::HeapReference<mirror::Object>));
- if (LIKELY(field != NULL)) {
- // Compiled code can't use transactional mode.
- field->SetObj<false>(field->GetDeclaringClass(), new_value);
- return 0;
- }
- return -1;
-}
-
-extern "C" int32_t art_portable_get32_static_from_code(uint32_t field_idx,
- mirror::ArtMethod* referrer)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(uint32_t));
- if (LIKELY(field != NULL)) {
- return field->Get32(field->GetDeclaringClass());
- }
- field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, Thread::Current(),
- sizeof(uint32_t));
- if (LIKELY(field != NULL)) {
- return field->Get32(field->GetDeclaringClass());
- }
- return 0;
-}
-
-extern "C" int64_t art_portable_get64_static_from_code(uint32_t field_idx,
- mirror::ArtMethod* referrer)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(uint64_t));
- if (LIKELY(field != NULL)) {
- return field->Get64(field->GetDeclaringClass());
- }
- field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, Thread::Current(),
- sizeof(uint64_t));
- if (LIKELY(field != NULL)) {
- return field->Get64(field->GetDeclaringClass());
- }
- return 0;
-}
-
-extern "C" mirror::Object* art_portable_get_obj_static_from_code(uint32_t field_idx,
- mirror::ArtMethod* referrer)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticObjectRead,
- sizeof(mirror::HeapReference<mirror::Object>));
- if (LIKELY(field != NULL)) {
- return field->GetObj(field->GetDeclaringClass());
- }
- field = FindFieldFromCode<StaticObjectRead, true>(field_idx, referrer, Thread::Current(),
- sizeof(mirror::HeapReference<mirror::Object>));
- if (LIKELY(field != NULL)) {
- return field->GetObj(field->GetDeclaringClass());
- }
- return 0;
-}
-
-extern "C" int32_t art_portable_set32_instance_from_code(uint32_t field_idx,
- mirror::ArtMethod* referrer,
- mirror::Object* obj, uint32_t new_value)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(uint32_t));
- if (LIKELY(field != NULL)) {
- // Compiled code can't use transactional mode.
- field->Set32<false>(obj, new_value);
- return 0;
- }
- field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
- sizeof(uint32_t));
- if (LIKELY(field != NULL)) {
- // Compiled code can't use transactional mode.
- field->Set32<false>(obj, new_value);
- return 0;
- }
- return -1;
-}
-
-extern "C" int32_t art_portable_set64_instance_from_code(uint32_t field_idx,
- mirror::ArtMethod* referrer,
- mirror::Object* obj, int64_t new_value)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(uint64_t));
- if (LIKELY(field != NULL)) {
- // Compiled code can't use transactional mode.
- field->Set64<false>(obj, new_value);
- return 0;
- }
- field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
- sizeof(uint64_t));
- if (LIKELY(field != NULL)) {
- // Compiled code can't use transactional mode.
- field->Set64<false>(obj, new_value);
- return 0;
- }
- return -1;
-}
-
-extern "C" int32_t art_portable_set_obj_instance_from_code(uint32_t field_idx,
- mirror::ArtMethod* referrer,
- mirror::Object* obj,
- mirror::Object* new_value)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectWrite,
- sizeof(mirror::HeapReference<mirror::Object>));
- if (LIKELY(field != NULL)) {
- // Compiled code can't use transactional mode.
- field->SetObj<false>(obj, new_value);
- return 0;
- }
- field = FindFieldFromCode<InstanceObjectWrite, true>(field_idx, referrer, Thread::Current(),
- sizeof(mirror::HeapReference<mirror::Object>));
- if (LIKELY(field != NULL)) {
- // Compiled code can't use transactional mode.
- field->SetObj<false>(obj, new_value);
- return 0;
- }
- return -1;
-}
-
-extern "C" int32_t art_portable_get32_instance_from_code(uint32_t field_idx,
- mirror::ArtMethod* referrer,
- mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(uint32_t));
- if (LIKELY(field != NULL)) {
- return field->Get32(obj);
- }
- field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, Thread::Current(),
- sizeof(uint32_t));
- if (LIKELY(field != NULL)) {
- return field->Get32(obj);
- }
- return 0;
-}
-
-extern "C" int64_t art_portable_get64_instance_from_code(uint32_t field_idx,
- mirror::ArtMethod* referrer,
- mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(uint64_t));
- if (LIKELY(field != NULL)) {
- return field->Get64(obj);
- }
- field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, Thread::Current(),
- sizeof(uint64_t));
- if (LIKELY(field != NULL)) {
- return field->Get64(obj);
- }
- return 0;
-}
-
-extern "C" mirror::Object* art_portable_get_obj_instance_from_code(uint32_t field_idx,
- mirror::ArtMethod* referrer,
- mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectRead,
- sizeof(mirror::HeapReference<mirror::Object>));
- if (LIKELY(field != NULL)) {
- return field->GetObj(obj);
- }
- field = FindFieldFromCode<InstanceObjectRead, true>(field_idx, referrer, Thread::Current(),
- sizeof(mirror::HeapReference<mirror::Object>));
- if (LIKELY(field != NULL)) {
- return field->GetObj(obj);
- }
- return 0;
-}
-
-} // namespace art
diff --git a/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc b/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc
deleted file mode 100644
index afe769e..0000000
--- a/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "dex_instruction.h"
-#include "entrypoints/entrypoint_utils.h"
-#include "mirror/art_method-inl.h"
-
-namespace art {
-
-extern "C" void art_portable_fill_array_data_from_code(mirror::ArtMethod* method,
- uint32_t dex_pc,
- mirror::Array* array,
- uint32_t payload_offset)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- UNUSED(dex_pc);
- const DexFile::CodeItem* code_item = method->GetCodeItem();
- const Instruction::ArrayDataPayload* payload =
- reinterpret_cast<const Instruction::ArrayDataPayload*>(code_item->insns_ + payload_offset);
- FillArrayData(array, payload);
-}
-
-} // namespace art
diff --git a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
deleted file mode 100644
index 6f9c083..0000000
--- a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/dex_cache-inl.h"
-#include "mirror/object-inl.h"
-
-namespace art {
-
-template<InvokeType type, bool access_check>
-mirror::ArtMethod* FindMethodHelper(uint32_t method_idx, mirror::Object* this_object,
- mirror::ArtMethod* caller_method, Thread* self) {
- mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method,
- access_check, type);
- if (UNLIKELY(method == NULL)) {
- // Note: This can cause thread suspension.
- self->AssertThreadSuspensionIsAllowable();
- method = FindMethodFromCode<type, access_check>(method_idx, &this_object, &caller_method,
- self);
- if (UNLIKELY(method == NULL)) {
- CHECK(self->IsExceptionPending());
- return 0; // failure
- }
- }
- DCHECK(!self->IsExceptionPending());
- const void* code = method->GetEntryPointFromPortableCompiledCode();
-
- // When we return, the caller will branch to this address, so it had better not be 0!
- if (UNLIKELY(code == NULL)) {
- LOG(FATAL) << "Code was NULL in method: " << PrettyMethod(method)
- << " location: " << method->GetDexFile()->GetLocation();
- }
- return method;
-}
-
-// Explicit template declarations of FindMethodHelper for all invoke types.
-#define EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, _access_check) \
- template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \
- mirror::ArtMethod* FindMethodHelper<_type, _access_check>(uint32_t method_idx, \
- mirror::Object* this_object, \
- mirror::ArtMethod* caller_method, \
- Thread* thread)
-#define EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(_type) \
- EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, false); \
- EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, true)
-
-EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kStatic);
-EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kDirect);
-EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kVirtual);
-EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kSuper);
-EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kInterface);
-
-#undef EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL
-#undef EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL
-
-extern "C" mirror::Object* art_portable_find_static_method_from_code_with_access_check(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* referrer,
- Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper<kStatic, true>(method_idx, this_object, referrer, thread);
-}
-
-extern "C" mirror::Object* art_portable_find_direct_method_from_code_with_access_check(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* referrer,
- Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper<kDirect, true>(method_idx, this_object, referrer, thread);
-}
-
-extern "C" mirror::Object* art_portable_find_virtual_method_from_code_with_access_check(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* referrer,
- Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper<kVirtual, true>(method_idx, this_object, referrer, thread);
-}
-
-extern "C" mirror::Object* art_portable_find_super_method_from_code_with_access_check(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* referrer,
- Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper<kSuper, true>(method_idx, this_object, referrer, thread);
-}
-
-extern "C" mirror::Object* art_portable_find_interface_method_from_code_with_access_check(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* referrer,
- Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper<kInterface, true>(method_idx, this_object, referrer, thread);
-}
-
-extern "C" mirror::Object* art_portable_find_interface_method_from_code(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* referrer,
- Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper<kInterface, false>(method_idx, this_object, referrer, thread);
-}
-
-} // namespace art
diff --git a/runtime/entrypoints/portable/portable_jni_entrypoints.cc b/runtime/entrypoints/portable/portable_jni_entrypoints.cc
deleted file mode 100644
index 0d0f21b..0000000
--- a/runtime/entrypoints/portable/portable_jni_entrypoints.cc
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
-#include "thread-inl.h"
-
-namespace art {
-
-// Called on entry to JNI, transition out of Runnable and release share of mutator_lock_.
-extern "C" uint32_t art_portable_jni_method_start(Thread* self)
- UNLOCK_FUNCTION(Locks::mutator_lock_) {
- JNIEnvExt* env = self->GetJniEnv();
- uint32_t saved_local_ref_cookie = env->local_ref_cookie;
- env->local_ref_cookie = env->locals.GetSegmentState();
- self->TransitionFromRunnableToSuspended(kNative);
- return saved_local_ref_cookie;
-}
-
-extern "C" uint32_t art_portable_jni_method_start_synchronized(jobject to_lock, Thread* self)
- UNLOCK_FUNCTION(Locks::mutator_lock_) NO_THREAD_SAFETY_ANALYSIS {
- self->DecodeJObject(to_lock)->MonitorEnter(self);
- return art_portable_jni_method_start(self);
-}
-
-static void PopLocalReferences(uint32_t saved_local_ref_cookie, Thread* self)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- JNIEnvExt* env = self->GetJniEnv();
- env->locals.SetSegmentState(env->local_ref_cookie);
- env->local_ref_cookie = saved_local_ref_cookie;
-}
-
-extern "C" void art_portable_jni_method_end(uint32_t saved_local_ref_cookie, Thread* self)
- SHARED_LOCK_FUNCTION(Locks::mutator_lock_) {
- self->TransitionFromSuspendedToRunnable();
- PopLocalReferences(saved_local_ref_cookie, self);
-}
-
-
-extern "C" void art_portable_jni_method_end_synchronized(uint32_t saved_local_ref_cookie,
- jobject locked,
- Thread* self)
- SHARED_LOCK_FUNCTION(Locks::mutator_lock_) {
- self->TransitionFromSuspendedToRunnable();
- UnlockJniSynchronizedMethod(locked, self); // Must decode before pop.
- PopLocalReferences(saved_local_ref_cookie, self);
-}
-
-extern "C" mirror::Object* art_portable_jni_method_end_with_reference(jobject result,
- uint32_t saved_local_ref_cookie,
- Thread* self)
- SHARED_LOCK_FUNCTION(Locks::mutator_lock_) {
- self->TransitionFromSuspendedToRunnable();
- mirror::Object* o = self->DecodeJObject(result); // Must decode before pop.
- PopLocalReferences(saved_local_ref_cookie, self);
- // Process result.
- if (UNLIKELY(self->GetJniEnv()->check_jni)) {
- if (self->IsExceptionPending()) {
- return NULL;
- }
- CheckReferenceResult(o, self);
- }
- return o;
-}
-
-extern "C" mirror::Object* art_portable_jni_method_end_with_reference_synchronized(jobject result,
- uint32_t saved_local_ref_cookie,
- jobject locked,
- Thread* self)
- SHARED_LOCK_FUNCTION(Locks::mutator_lock_) {
- self->TransitionFromSuspendedToRunnable();
- UnlockJniSynchronizedMethod(locked, self); // Must decode before pop.
- mirror::Object* o = self->DecodeJObject(result);
- PopLocalReferences(saved_local_ref_cookie, self);
- // Process result.
- if (UNLIKELY(self->GetJniEnv()->check_jni)) {
- if (self->IsExceptionPending()) {
- return NULL;
- }
- CheckReferenceResult(o, self);
- }
- return o;
-}
-
-} // namespace art
diff --git a/runtime/entrypoints/portable/portable_lock_entrypoints.cc b/runtime/entrypoints/portable/portable_lock_entrypoints.cc
deleted file mode 100644
index fcd3e9d..0000000
--- a/runtime/entrypoints/portable/portable_lock_entrypoints.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/object-inl.h"
-
-namespace art {
-
-extern "C" void art_portable_lock_object_from_code(mirror::Object* obj, Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- NO_THREAD_SAFETY_ANALYSIS /* EXCLUSIVE_LOCK_FUNCTION(Monitor::monitor_lock_) */ {
- DCHECK(obj != nullptr); // Assumed to have been checked before entry.
- obj->MonitorEnter(thread); // May block.
- DCHECK(thread->HoldsLock(obj));
- // Only possible exception is NPE and is handled before entry.
- DCHECK(!thread->IsExceptionPending());
-}
-
-extern "C" void art_portable_unlock_object_from_code(mirror::Object* obj, Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- NO_THREAD_SAFETY_ANALYSIS /* UNLOCK_FUNCTION(Monitor::monitor_lock_) */ {
- DCHECK(obj != nullptr); // Assumed to have been checked before entry.
- // MonitorExit may throw exception.
- obj->MonitorExit(thread);
-}
-
-} // namespace art
diff --git a/runtime/entrypoints/portable/portable_thread_entrypoints.cc b/runtime/entrypoints/portable/portable_thread_entrypoints.cc
deleted file mode 100644
index 95ac66c..0000000
--- a/runtime/entrypoints/portable/portable_thread_entrypoints.cc
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "mirror/art_method-inl.h"
-#include "verifier/dex_gc_map.h"
-#include "stack.h"
-#include "thread-inl.h"
-
-namespace art {
-
-class ShadowFrameCopyVisitor : public StackVisitor {
- public:
- explicit ShadowFrameCopyVisitor(Thread* self) : StackVisitor(self, NULL), prev_frame_(NULL),
- top_frame_(NULL) {}
-
- bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (IsShadowFrame()) {
- ShadowFrame* cur_frame = GetCurrentShadowFrame();
- size_t num_regs = cur_frame->NumberOfVRegs();
- mirror::ArtMethod* method = cur_frame->GetMethod();
- uint32_t dex_pc = cur_frame->GetDexPC();
- ShadowFrame* new_frame = ShadowFrame::Create(num_regs, NULL, method, dex_pc);
-
- const uint8_t* gc_map = method->GetNativeGcMap(sizeof(void*));
- verifier::DexPcToReferenceMap dex_gc_map(gc_map);
- const uint8_t* reg_bitmap = dex_gc_map.FindBitMap(dex_pc);
- for (size_t reg = 0; reg < num_regs; ++reg) {
- if (TestBitmap(reg, reg_bitmap)) {
- new_frame->SetVRegReference(reg, cur_frame->GetVRegReference(reg));
- } else {
- new_frame->SetVReg(reg, cur_frame->GetVReg(reg));
- }
- }
-
- if (prev_frame_ != NULL) {
- prev_frame_->SetLink(new_frame);
- } else {
- top_frame_ = new_frame;
- }
- prev_frame_ = new_frame;
- }
- return true;
- }
-
- ShadowFrame* GetShadowFrameCopy() {
- return top_frame_;
- }
-
- private:
- static bool TestBitmap(int reg, const uint8_t* reg_vector) {
- return ((reg_vector[reg / 8] >> (reg % 8)) & 0x01) != 0;
- }
-
- ShadowFrame* prev_frame_;
- ShadowFrame* top_frame_;
-};
-
-extern "C" void art_portable_test_suspend_from_code(Thread* self)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- self->CheckSuspend();
- if (Runtime::Current()->GetInstrumentation()->ShouldPortableCodeDeoptimize()) {
- // Save out the shadow frame to the heap
- ShadowFrameCopyVisitor visitor(self);
- visitor.WalkStack(true);
- self->SetDeoptimizationShadowFrame(visitor.GetShadowFrameCopy());
- self->SetDeoptimizationReturnValue(JValue());
- self->SetException(ThrowLocation(), Thread::GetDeoptimizationException());
- }
-}
-
-extern "C" ShadowFrame* art_portable_push_shadow_frame_from_code(Thread* thread,
- ShadowFrame* new_shadow_frame,
- mirror::ArtMethod* method,
- uint32_t num_vregs) {
- ShadowFrame* old_frame = thread->PushShadowFrame(new_shadow_frame);
- new_shadow_frame->SetMethod(method);
- new_shadow_frame->SetNumberOfVRegs(num_vregs);
- return old_frame;
-}
-
-} // namespace art
diff --git a/runtime/entrypoints/portable/portable_throw_entrypoints.cc b/runtime/entrypoints/portable/portable_throw_entrypoints.cc
deleted file mode 100644
index 4317358..0000000
--- a/runtime/entrypoints/portable/portable_throw_entrypoints.cc
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "dex_instruction.h"
-#include "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
-
-namespace art {
-
-extern "C" void art_portable_throw_div_zero_from_code() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ThrowArithmeticExceptionDivideByZero();
-}
-
-extern "C" void art_portable_throw_array_bounds_from_code(int32_t index, int32_t length)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ThrowArrayIndexOutOfBoundsException(index, length);
-}
-
-extern "C" void art_portable_throw_no_such_method_from_code(int32_t method_idx)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ThrowNoSuchMethodError(method_idx);
-}
-
-extern "C" void art_portable_throw_null_pointer_exception_from_code(uint32_t dex_pc)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- // TODO: remove dex_pc argument from caller.
- UNUSED(dex_pc);
- Thread* self = Thread::Current();
- ThrowLocation throw_location = self->GetCurrentLocationForThrow();
- ThrowNullPointerExceptionFromDexPC(throw_location);
-}
-
-extern "C" void art_portable_throw_stack_overflow_from_code() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ThrowStackOverflowError(Thread::Current());
-}
-
-extern "C" void art_portable_throw_exception_from_code(mirror::Throwable* exception)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- Thread* self = Thread::Current();
- ThrowLocation throw_location = self->GetCurrentLocationForThrow();
- if (exception == NULL) {
- ThrowNullPointerException(NULL, "throw with null exception");
- } else {
- self->SetException(throw_location, exception);
- }
-}
-
-extern "C" void* art_portable_get_and_clear_exception(Thread* self)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(self->IsExceptionPending());
- // TODO: make this inline.
- mirror::Throwable* exception = self->GetException(NULL);
- self->ClearException();
- return exception;
-}
-
-extern "C" int32_t art_portable_find_catch_block_from_code(mirror::ArtMethod* current_method,
- uint32_t ti_offset)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- Thread* self = Thread::Current(); // TODO: make an argument.
- ThrowLocation throw_location;
- mirror::Throwable* exception = self->GetException(&throw_location);
- // Check for special deoptimization exception.
- if (UNLIKELY(reinterpret_cast<intptr_t>(exception) == -1)) {
- return -1;
- }
- mirror::Class* exception_type = exception->GetClass();
- StackHandleScope<1> hs(self);
- const DexFile::CodeItem* code_item = current_method->GetCodeItem();
- DCHECK_LT(ti_offset, code_item->tries_size_);
- const DexFile::TryItem* try_item = DexFile::GetTryItems(*code_item, ti_offset);
-
- int iter_index = 0;
- int result = -1;
- uint32_t catch_dex_pc = -1;
- // Iterate over the catch handlers associated with dex_pc
- for (CatchHandlerIterator it(*code_item, *try_item); it.HasNext(); it.Next()) {
- uint16_t iter_type_idx = it.GetHandlerTypeIndex();
- // Catch all case
- if (iter_type_idx == DexFile::kDexNoIndex16) {
- catch_dex_pc = it.GetHandlerAddress();
- result = iter_index;
- break;
- }
- // Does this catch exception type apply?
- mirror::Class* iter_exception_type =
- current_method->GetDexCacheResolvedType(iter_type_idx);
- if (UNLIKELY(iter_exception_type == NULL)) {
- // TODO: check, the verifier (class linker?) should take care of resolving all exception
- // classes early.
- LOG(WARNING) << "Unresolved exception class when finding catch block: "
- << current_method->GetTypeDescriptorFromTypeIdx(iter_type_idx);
- } else if (iter_exception_type->IsAssignableFrom(exception_type)) {
- catch_dex_pc = it.GetHandlerAddress();
- result = iter_index;
- break;
- }
- ++iter_index;
- }
- if (result != -1) {
- // Handler found.
- Runtime::Current()->GetInstrumentation()->ExceptionCaughtEvent(
- self, throw_location, current_method, catch_dex_pc, exception);
- // If the catch block has no move-exception then clear the exception for it.
- const Instruction* first_catch_instr = Instruction::At(
- ¤t_method->GetCodeItem()->insns_[catch_dex_pc]);
- if (first_catch_instr->Opcode() != Instruction::MOVE_EXCEPTION) {
- self->ClearException();
- }
- }
- return result;
-}
-
-} // namespace art
diff --git a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc b/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc
deleted file mode 100644
index 2a2771f..0000000
--- a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc
+++ /dev/null
@@ -1,496 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_ENTRYPOINTS_PORTABLE_PORTABLE_ARGUMENT_VISITOR_H_
-#define ART_RUNTIME_ENTRYPOINTS_PORTABLE_PORTABLE_ARGUMENT_VISITOR_H_
-
-#include "dex_instruction-inl.h"
-#include "entrypoints/entrypoint_utils-inl.h"
-#include "entrypoints/runtime_asm_entrypoints.h"
-#include "interpreter/interpreter.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
-
-namespace art {
-
-class ShortyHelper {
- public:
- ShortyHelper(const char* shorty, uint32_t shorty_len, bool is_static)
- : shorty_(shorty), shorty_len_(shorty_len), is_static_(is_static) {
- }
-
- const char* GetShorty() const {
- return shorty_;
- }
-
- uint32_t GetShortyLength() const {
- return shorty_len_;
- }
-
- size_t NumArgs() const {
- // "1 +" because the first in Args is the receiver.
- // "- 1" because we don't count the return type.
- return (is_static_ ? 0 : 1) + GetShortyLength() - 1;
- }
-
- // Get the primitive type associated with the given parameter.
- Primitive::Type GetParamPrimitiveType(size_t param) const {
- CHECK_LT(param, NumArgs());
- if (is_static_) {
- param++; // 0th argument must skip return value at start of the shorty.
- } else if (param == 0) {
- return Primitive::kPrimNot;
- }
- return Primitive::GetType(shorty_[param]);
- }
-
- // Is the specified parameter a long or double, where parameter 0 is 'this' for instance methods.
- bool IsParamALongOrDouble(size_t param) const {
- Primitive::Type type = GetParamPrimitiveType(param);
- return type == Primitive::kPrimLong || type == Primitive::kPrimDouble;
- }
-
- // Is the specified parameter a reference, where parameter 0 is 'this' for instance methods.
- bool IsParamAReference(size_t param) const {
- return GetParamPrimitiveType(param) == Primitive::kPrimNot;
- }
-
- private:
- const char* const shorty_;
- const uint32_t shorty_len_;
- const bool is_static_;
-
- DISALLOW_COPY_AND_ASSIGN(ShortyHelper);
-};
-
-// Visits the arguments as saved to the stack by a Runtime::kRefAndArgs callee save frame.
-class PortableArgumentVisitor {
- public:
-// Offset to first (not the Method*) argument in a Runtime::kRefAndArgs callee save frame.
-// Size of Runtime::kRefAndArgs callee save frame.
-// Size of Method* and register parameters in out stack arguments.
-#if defined(__arm__)
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 8
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 48
-#define PORTABLE_STACK_ARG_SKIP 0
-#elif defined(__mips__)
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 64
-#define PORTABLE_STACK_ARG_SKIP 16
-#elif defined(__i386__)
-// For x86 there are no register arguments and the stack pointer will point directly to the called
-// method argument passed by the caller.
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 0
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 0
-#define PORTABLE_STACK_ARG_SKIP 4
-#elif defined(__x86_64__)
-// TODO: implement and check these.
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 16
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 96
-#define PORTABLE_STACK_ARG_SKIP 0
-#else
-// TODO: portable should be disabled for aarch64 for now.
-// #error "Unsupported architecture"
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 0
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 0
-#define PORTABLE_STACK_ARG_SKIP 0
-#endif
-
- PortableArgumentVisitor(ShortyHelper& caller_mh, mirror::ArtMethod** sp)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
- caller_mh_(caller_mh),
- args_in_regs_(ComputeArgsInRegs(caller_mh)),
- num_params_(caller_mh.NumArgs()),
- reg_args_(reinterpret_cast<uint8_t*>(sp) + PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET),
- stack_args_(reinterpret_cast<uint8_t*>(sp) + PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE
- + PORTABLE_STACK_ARG_SKIP),
- cur_args_(reg_args_),
- cur_arg_index_(0),
- param_index_(0) {
- }
-
- virtual ~PortableArgumentVisitor() {}
-
- virtual void Visit() = 0;
-
- bool IsParamAReference() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return caller_mh_.IsParamAReference(param_index_);
- }
-
- bool IsParamALongOrDouble() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return caller_mh_.IsParamALongOrDouble(param_index_);
- }
-
- Primitive::Type GetParamPrimitiveType() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return caller_mh_.GetParamPrimitiveType(param_index_);
- }
-
- uint8_t* GetParamAddress() const {
- return cur_args_ + (cur_arg_index_ * sizeof(void*));
- }
-
- void VisitArguments() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- for (cur_arg_index_ = 0; cur_arg_index_ < args_in_regs_ && param_index_ < num_params_; ) {
-#if (defined(__arm__) || defined(__mips__))
- if (IsParamALongOrDouble() && cur_arg_index_ == 2) {
- break;
- }
-#endif
- Visit();
- cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1);
- param_index_++;
- }
- cur_args_ = stack_args_;
- cur_arg_index_ = 0;
- while (param_index_ < num_params_) {
-#if (defined(__arm__) || defined(__mips__))
- if (IsParamALongOrDouble() && cur_arg_index_ % 2 != 0) {
- cur_arg_index_++;
- }
-#endif
- Visit();
- cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1);
- param_index_++;
- }
- }
-
- private:
- static size_t ComputeArgsInRegs(ShortyHelper& mh) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-#if (defined(__i386__))
- UNUSED(mh);
- return 0;
-#else
- size_t args_in_regs = 0;
- size_t num_params = mh.NumArgs();
- for (size_t i = 0; i < num_params; i++) {
- args_in_regs = args_in_regs + (mh.IsParamALongOrDouble(i) ? 2 : 1);
- if (args_in_regs > 3) {
- args_in_regs = 3;
- break;
- }
- }
- return args_in_regs;
-#endif
- }
- ShortyHelper& caller_mh_;
- const size_t args_in_regs_;
- const size_t num_params_;
- uint8_t* const reg_args_;
- uint8_t* const stack_args_;
- uint8_t* cur_args_;
- size_t cur_arg_index_;
- size_t param_index_;
-};
-
-// Visits arguments on the stack placing them into the shadow frame.
-class BuildPortableShadowFrameVisitor : public PortableArgumentVisitor {
- public:
- BuildPortableShadowFrameVisitor(ShortyHelper& caller_mh, mirror::ArtMethod** sp,
- ShadowFrame& sf, size_t first_arg_reg) :
- PortableArgumentVisitor(caller_mh, sp), sf_(sf), cur_reg_(first_arg_reg) { }
- virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- Primitive::Type type = GetParamPrimitiveType();
- switch (type) {
- case Primitive::kPrimLong: // Fall-through.
- case Primitive::kPrimDouble:
- sf_.SetVRegLong(cur_reg_, *reinterpret_cast<jlong*>(GetParamAddress()));
- ++cur_reg_;
- break;
- case Primitive::kPrimNot:
- sf_.SetVRegReference(cur_reg_, *reinterpret_cast<mirror::Object**>(GetParamAddress()));
- break;
- case Primitive::kPrimBoolean: // Fall-through.
- case Primitive::kPrimByte: // Fall-through.
- case Primitive::kPrimChar: // Fall-through.
- case Primitive::kPrimShort: // Fall-through.
- case Primitive::kPrimInt: // Fall-through.
- case Primitive::kPrimFloat:
- sf_.SetVReg(cur_reg_, *reinterpret_cast<jint*>(GetParamAddress()));
- break;
- case Primitive::kPrimVoid:
- LOG(FATAL) << "UNREACHABLE";
- UNREACHABLE();
- }
- ++cur_reg_;
- }
-
- private:
- ShadowFrame& sf_;
- size_t cur_reg_;
-
- DISALLOW_COPY_AND_ASSIGN(BuildPortableShadowFrameVisitor);
-};
-
-extern "C" uint64_t artPortableToInterpreterBridge(mirror::ArtMethod* method, Thread* self,
- mirror::ArtMethod** sp)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- // Ensure we don't get thread suspension until the object arguments are safely in the shadow
- // frame.
- // FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
-
- if (method->IsAbstract()) {
- ThrowAbstractMethodError(method);
- return 0;
- } else {
- const char* old_cause = self->StartAssertNoThreadSuspension("Building interpreter shadow frame");
- StackHandleScope<2> hs(self);
- uint32_t shorty_len;
- const char* shorty = method->GetShorty(&shorty_len);
- ShortyHelper mh(shorty, shorty_len, method->IsStatic());
- const DexFile::CodeItem* code_item = method->GetCodeItem();
- uint16_t num_regs = code_item->registers_size_;
- void* memory = alloca(ShadowFrame::ComputeSize(num_regs));
- ShadowFrame* shadow_frame(ShadowFrame::Create(num_regs, NULL, // No last shadow coming from quick.
- method, 0, memory));
- size_t first_arg_reg = code_item->registers_size_ - code_item->ins_size_;
- BuildPortableShadowFrameVisitor shadow_frame_builder(mh, sp,
- *shadow_frame, first_arg_reg);
- shadow_frame_builder.VisitArguments();
- // Push a transition back into managed code onto the linked list in thread.
- ManagedStack fragment;
- self->PushManagedStackFragment(&fragment);
- self->PushShadowFrame(shadow_frame);
- self->EndAssertNoThreadSuspension(old_cause);
-
- if (method->IsStatic() && !method->GetDeclaringClass()->IsInitialized()) {
- // Ensure static method's class is initialized.
- Handle<mirror::Class> h_class(hs.NewHandle(method->GetDeclaringClass()));
- if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
- DCHECK(Thread::Current()->IsExceptionPending());
- self->PopManagedStackFragment(fragment);
- return 0;
- }
- }
-
- JValue result = interpreter::EnterInterpreterFromEntryPoint(self, code_item, shadow_frame);
- // Pop transition.
- self->PopManagedStackFragment(fragment);
- return result.GetJ();
- }
-}
-
-// Visits arguments on the stack placing them into the args vector, Object* arguments are converted
-// to jobjects.
-class BuildPortableArgumentVisitor : public PortableArgumentVisitor {
- public:
- BuildPortableArgumentVisitor(ShortyHelper& caller_mh, mirror::ArtMethod** sp,
- ScopedObjectAccessUnchecked& soa, std::vector<jvalue>& args) :
- PortableArgumentVisitor(caller_mh, sp), soa_(soa), args_(args) {}
-
- virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- jvalue val;
- Primitive::Type type = GetParamPrimitiveType();
- switch (type) {
- case Primitive::kPrimNot: {
- mirror::Object* obj = *reinterpret_cast<mirror::Object**>(GetParamAddress());
- val.l = soa_.AddLocalReference<jobject>(obj);
- break;
- }
- case Primitive::kPrimLong: // Fall-through.
- case Primitive::kPrimDouble:
- val.j = *reinterpret_cast<jlong*>(GetParamAddress());
- break;
- case Primitive::kPrimBoolean: // Fall-through.
- case Primitive::kPrimByte: // Fall-through.
- case Primitive::kPrimChar: // Fall-through.
- case Primitive::kPrimShort: // Fall-through.
- case Primitive::kPrimInt: // Fall-through.
- case Primitive::kPrimFloat:
- val.i = *reinterpret_cast<jint*>(GetParamAddress());
- break;
- case Primitive::kPrimVoid:
- LOG(FATAL) << "UNREACHABLE";
- UNREACHABLE();
- }
- args_.push_back(val);
- }
-
- private:
- ScopedObjectAccessUnchecked& soa_;
- std::vector<jvalue>& args_;
-
- DISALLOW_COPY_AND_ASSIGN(BuildPortableArgumentVisitor);
-};
-
-// Handler for invocation on proxy methods. On entry a frame will exist for the proxy object method
-// which is responsible for recording callee save registers. We explicitly place into jobjects the
-// incoming reference arguments (so they survive GC). We invoke the invocation handler, which is a
-// field within the proxy object, which will box the primitive arguments and deal with error cases.
-extern "C" uint64_t artPortableProxyInvokeHandler(mirror::ArtMethod* proxy_method,
- mirror::Object* receiver,
- Thread* self, mirror::ArtMethod** sp)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- // Ensure we don't get thread suspension until the object arguments are safely in jobjects.
- const char* old_cause =
- self->StartAssertNoThreadSuspension("Adding to IRT proxy object arguments");
- self->VerifyStack();
- // Start new JNI local reference state.
- JNIEnvExt* env = self->GetJniEnv();
- ScopedObjectAccessUnchecked soa(env);
- ScopedJniEnvLocalRefState env_state(env);
- // Create local ref. copies of proxy method and the receiver.
- jobject rcvr_jobj = soa.AddLocalReference<jobject>(receiver);
-
- // Placing arguments into args vector and remove the receiver.
- uint32_t shorty_len;
- const char* shorty = proxy_method->GetShorty(&shorty_len);
- ShortyHelper proxy_mh(shorty, shorty_len, false);
- std::vector<jvalue> args;
- BuildPortableArgumentVisitor local_ref_visitor(proxy_mh, sp, soa, args);
- local_ref_visitor.VisitArguments();
- args.erase(args.begin());
-
- // Convert proxy method into expected interface method.
- mirror::ArtMethod* interface_method = proxy_method->FindOverriddenMethod();
- DCHECK(interface_method != NULL);
- DCHECK(!interface_method->IsProxyMethod()) << PrettyMethod(interface_method);
- jobject interface_method_jobj = soa.AddLocalReference<jobject>(interface_method);
-
- // All naked Object*s should now be in jobjects, so its safe to go into the main invoke code
- // that performs allocations.
- self->EndAssertNoThreadSuspension(old_cause);
- JValue result = InvokeProxyInvocationHandler(soa, proxy_mh.GetShorty(),
- rcvr_jobj, interface_method_jobj, args);
- return result.GetJ();
-}
-
-// Lazily resolve a method for portable. Called by stub code.
-extern "C" const void* artPortableResolutionTrampoline(mirror::ArtMethod* called,
- mirror::Object* receiver,
- Thread* self,
- mirror::ArtMethod** called_addr)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- uint32_t dex_pc;
- mirror::ArtMethod* caller = self->GetCurrentMethod(&dex_pc);
-
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- InvokeType invoke_type;
- bool is_range;
- if (called->IsRuntimeMethod()) {
- const DexFile::CodeItem* code = caller->GetCodeItem();
- CHECK_LT(dex_pc, code->insns_size_in_code_units_);
- const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
- Instruction::Code instr_code = instr->Opcode();
- switch (instr_code) {
- case Instruction::INVOKE_DIRECT:
- invoke_type = kDirect;
- is_range = false;
- break;
- case Instruction::INVOKE_DIRECT_RANGE:
- invoke_type = kDirect;
- is_range = true;
- break;
- case Instruction::INVOKE_STATIC:
- invoke_type = kStatic;
- is_range = false;
- break;
- case Instruction::INVOKE_STATIC_RANGE:
- invoke_type = kStatic;
- is_range = true;
- break;
- case Instruction::INVOKE_SUPER:
- invoke_type = kSuper;
- is_range = false;
- break;
- case Instruction::INVOKE_SUPER_RANGE:
- invoke_type = kSuper;
- is_range = true;
- break;
- case Instruction::INVOKE_VIRTUAL:
- invoke_type = kVirtual;
- is_range = false;
- break;
- case Instruction::INVOKE_VIRTUAL_RANGE:
- invoke_type = kVirtual;
- is_range = true;
- break;
- case Instruction::INVOKE_INTERFACE:
- invoke_type = kInterface;
- is_range = false;
- break;
- case Instruction::INVOKE_INTERFACE_RANGE:
- invoke_type = kInterface;
- is_range = true;
- break;
- default:
- LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(NULL);
- // Avoid used uninitialized warnings.
- invoke_type = kDirect;
- is_range = true;
- }
- uint32_t dex_method_idx = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c();
- called = class_linker->ResolveMethod(Thread::Current(), dex_method_idx, &caller, invoke_type);
- // Incompatible class change should have been handled in resolve method.
- CHECK(!called->CheckIncompatibleClassChange(invoke_type));
- // Refine called method based on receiver.
- if (invoke_type == kVirtual) {
- called = receiver->GetClass()->FindVirtualMethodForVirtual(called);
- } else if (invoke_type == kInterface) {
- called = receiver->GetClass()->FindVirtualMethodForInterface(called);
- }
- } else {
- CHECK(called->IsStatic()) << PrettyMethod(called);
- invoke_type = kStatic;
- // Incompatible class change should have been handled in resolve method.
- CHECK(!called->CheckIncompatibleClassChange(invoke_type));
- }
- const void* code = nullptr;
- if (LIKELY(!self->IsExceptionPending())) {
- // Ensure that the called method's class is initialized.
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> called_class(hs.NewHandle(called->GetDeclaringClass()));
- class_linker->EnsureInitialized(self, called_class, true, true);
- if (LIKELY(called_class->IsInitialized())) {
- code = called->GetEntryPointFromPortableCompiledCode();
- // TODO: remove this after we solve the link issue.
- if (code == nullptr) {
- bool have_portable_code;
- code = class_linker->GetPortableOatCodeFor(called, &have_portable_code);
- }
- } else if (called_class->IsInitializing()) {
- if (invoke_type == kStatic) {
- // Class is still initializing, go to oat and grab code (trampoline must be left in place
- // until class is initialized to stop races between threads).
- bool have_portable_code;
- code = class_linker->GetPortableOatCodeFor(called, &have_portable_code);
- } else {
- // No trampoline for non-static methods.
- code = called->GetEntryPointFromPortableCompiledCode();
- // TODO: remove this after we solve the link issue.
- if (code == nullptr) {
- bool have_portable_code;
- code = class_linker->GetPortableOatCodeFor(called, &have_portable_code);
- }
- }
- } else {
- DCHECK(called_class->IsErroneous());
- }
- }
- if (LIKELY(code != nullptr)) {
- // Expect class to at least be initializing.
- DCHECK(called->GetDeclaringClass()->IsInitializing());
- // Don't want infinite recursion.
- DCHECK(!class_linker->IsPortableResolutionStub(code));
- // Set up entry into main method
- *called_addr = called;
- }
- return code;
-}
-
-} // namespace art
-
-#endif // ART_RUNTIME_ENTRYPOINTS_PORTABLE_PORTABLE_ARGUMENT_VISITOR_H_
diff --git a/runtime/entrypoints/quick/callee_save_frame.h b/runtime/entrypoints/quick/callee_save_frame.h
index 9ffd199..8cd6ca6 100644
--- a/runtime/entrypoints/quick/callee_save_frame.h
+++ b/runtime/entrypoints/quick/callee_save_frame.h
@@ -27,6 +27,7 @@
#include "arch/arm/quick_method_frame_info_arm.h"
#include "arch/arm64/quick_method_frame_info_arm64.h"
#include "arch/mips/quick_method_frame_info_mips.h"
+#include "arch/mips64/quick_method_frame_info_mips64.h"
#include "arch/x86/quick_method_frame_info_x86.h"
#include "arch/x86_64/quick_method_frame_info_x86_64.h"
@@ -76,6 +77,7 @@
return (isa == kArm || isa == kThumb2) ? arm::ArmCalleeSaveFrameSize(type) :
isa == kArm64 ? arm64::Arm64CalleeSaveFrameSize(type) :
isa == kMips ? mips::MipsCalleeSaveFrameSize(type) :
+ isa == kMips64 ? mips64::Mips64CalleeSaveFrameSize(type) :
isa == kX86 ? x86::X86CalleeSaveFrameSize(type) :
isa == kX86_64 ? x86_64::X86_64CalleeSaveFrameSize(type) :
isa == kNone ? (LOG(FATAL) << "kNone has no frame size", 0) :
@@ -88,6 +90,7 @@
return (isa == kArm || isa == kThumb2) ? kArmPointerSize :
isa == kArm64 ? kArm64PointerSize :
isa == kMips ? kMipsPointerSize :
+ isa == kMips64 ? kMips64PointerSize :
isa == kX86 ? kX86PointerSize :
isa == kX86_64 ? kX86_64PointerSize :
isa == kNone ? (LOG(FATAL) << "kNone has no pointer size", 0) :
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index c0b79b2..c049e3d 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -114,44 +114,44 @@
return AllocObjectFromCode<true, instrumented_bool>(type_idx, method, self, allocator_type); \
} \
extern "C" mirror::Array* artAllocArrayFromCode##suffix##suffix2( \
- uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self) \
+ uint32_t type_idx, int32_t component_count, mirror::ArtMethod* method, Thread* self) \
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
ScopedQuickEntrypointChecks sqec(self); \
- return AllocArrayFromCode<false, instrumented_bool>(type_idx, method, component_count, self, \
+ return AllocArrayFromCode<false, instrumented_bool>(type_idx, component_count, method, self, \
allocator_type); \
} \
extern "C" mirror::Array* artAllocArrayFromCodeResolved##suffix##suffix2( \
- mirror::Class* klass, mirror::ArtMethod* method, int32_t component_count, Thread* self) \
+ mirror::Class* klass, int32_t component_count, mirror::ArtMethod* method, Thread* self) \
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
ScopedQuickEntrypointChecks sqec(self); \
- return AllocArrayFromCodeResolved<false, instrumented_bool>(klass, method, component_count, self, \
+ return AllocArrayFromCodeResolved<false, instrumented_bool>(klass, component_count, method, self, \
allocator_type); \
} \
extern "C" mirror::Array* artAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \
- uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self) \
+ uint32_t type_idx, int32_t component_count, mirror::ArtMethod* method, Thread* self) \
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
ScopedQuickEntrypointChecks sqec(self); \
- return AllocArrayFromCode<true, instrumented_bool>(type_idx, method, component_count, self, \
+ return AllocArrayFromCode<true, instrumented_bool>(type_idx, component_count, method, self, \
allocator_type); \
} \
extern "C" mirror::Array* artCheckAndAllocArrayFromCode##suffix##suffix2( \
- uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self) \
+ uint32_t type_idx, int32_t component_count, mirror::ArtMethod* method, Thread* self) \
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
ScopedQuickEntrypointChecks sqec(self); \
if (!instrumented_bool) { \
- return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, false, allocator_type); \
+ return CheckAndAllocArrayFromCode(type_idx, component_count, method, self, false, allocator_type); \
} else { \
- return CheckAndAllocArrayFromCodeInstrumented(type_idx, method, component_count, self, false, allocator_type); \
+ return CheckAndAllocArrayFromCodeInstrumented(type_idx, component_count, method, self, false, allocator_type); \
} \
} \
extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \
- uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self) \
+ uint32_t type_idx, int32_t component_count, mirror::ArtMethod* method, Thread* self) \
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
ScopedQuickEntrypointChecks sqec(self); \
if (!instrumented_bool) { \
- return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, true, allocator_type); \
+ return CheckAndAllocArrayFromCode(type_idx, component_count, method, self, true, allocator_type); \
} else { \
- return CheckAndAllocArrayFromCodeInstrumented(type_idx, method, component_count, self, true, allocator_type); \
+ return CheckAndAllocArrayFromCodeInstrumented(type_idx, component_count, method, self, true, allocator_type); \
} \
}
@@ -163,26 +163,28 @@
GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(RosAlloc, gc::kAllocatorTypeRosAlloc)
GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(BumpPointer, gc::kAllocatorTypeBumpPointer)
GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(TLAB, gc::kAllocatorTypeTLAB)
+GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(Region, gc::kAllocatorTypeRegion)
+GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(RegionTLAB, gc::kAllocatorTypeRegionTLAB)
#define GENERATE_ENTRYPOINTS(suffix) \
-extern "C" void* art_quick_alloc_array##suffix(uint32_t, void*, int32_t); \
-extern "C" void* art_quick_alloc_array_resolved##suffix(void* klass, void*, int32_t); \
-extern "C" void* art_quick_alloc_array_with_access_check##suffix(uint32_t, void*, int32_t); \
-extern "C" void* art_quick_alloc_object##suffix(uint32_t type_idx, void* method); \
-extern "C" void* art_quick_alloc_object_resolved##suffix(void* klass, void* method); \
-extern "C" void* art_quick_alloc_object_initialized##suffix(void* klass, void* method); \
-extern "C" void* art_quick_alloc_object_with_access_check##suffix(uint32_t type_idx, void* method); \
-extern "C" void* art_quick_check_and_alloc_array##suffix(uint32_t, void*, int32_t); \
-extern "C" void* art_quick_check_and_alloc_array_with_access_check##suffix(uint32_t, void*, int32_t); \
-extern "C" void* art_quick_alloc_array##suffix##_instrumented(uint32_t, void*, int32_t); \
-extern "C" void* art_quick_alloc_array_resolved##suffix##_instrumented(void* klass, void*, int32_t); \
-extern "C" void* art_quick_alloc_array_with_access_check##suffix##_instrumented(uint32_t, void*, int32_t); \
-extern "C" void* art_quick_alloc_object##suffix##_instrumented(uint32_t type_idx, void* method); \
-extern "C" void* art_quick_alloc_object_resolved##suffix##_instrumented(void* klass, void* method); \
-extern "C" void* art_quick_alloc_object_initialized##suffix##_instrumented(void* klass, void* method); \
-extern "C" void* art_quick_alloc_object_with_access_check##suffix##_instrumented(uint32_t type_idx, void* method); \
-extern "C" void* art_quick_check_and_alloc_array##suffix##_instrumented(uint32_t, void*, int32_t); \
-extern "C" void* art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented(uint32_t, void*, int32_t); \
+extern "C" void* art_quick_alloc_array##suffix(uint32_t, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_array_resolved##suffix(mirror::Class* klass, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_array_with_access_check##suffix(uint32_t, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object##suffix(uint32_t type_idx, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object_resolved##suffix(mirror::Class* klass, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object_initialized##suffix(mirror::Class* klass, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object_with_access_check##suffix(uint32_t type_idx, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_check_and_alloc_array##suffix(uint32_t, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_check_and_alloc_array_with_access_check##suffix(uint32_t, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_array##suffix##_instrumented(uint32_t, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_array_resolved##suffix##_instrumented(mirror::Class* klass, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_array_with_access_check##suffix##_instrumented(uint32_t, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object##suffix##_instrumented(uint32_t type_idx, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object_resolved##suffix##_instrumented(mirror::Class* klass, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object_initialized##suffix##_instrumented(mirror::Class* klass, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object_with_access_check##suffix##_instrumented(uint32_t type_idx, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_check_and_alloc_array##suffix##_instrumented(uint32_t, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented(uint32_t, int32_t, mirror::ArtMethod* ref); \
void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrumented) { \
if (instrumented) { \
qpoints->pAllocArray = art_quick_alloc_array##suffix##_instrumented; \
@@ -213,6 +215,8 @@
GENERATE_ENTRYPOINTS(_rosalloc)
GENERATE_ENTRYPOINTS(_bump_pointer)
GENERATE_ENTRYPOINTS(_tlab)
+GENERATE_ENTRYPOINTS(_region)
+GENERATE_ENTRYPOINTS(_region_tlab)
#endif
static bool entry_points_instrumented = false;
@@ -247,6 +251,16 @@
SetQuickAllocEntryPoints_tlab(qpoints, entry_points_instrumented);
return;
}
+ case gc::kAllocatorTypeRegion: {
+ CHECK(kMovingCollector);
+ SetQuickAllocEntryPoints_region(qpoints, entry_points_instrumented);
+ return;
+ }
+ case gc::kAllocatorTypeRegionTLAB: {
+ CHECK(kMovingCollector);
+ SetQuickAllocEntryPoints_region_tlab(qpoints, entry_points_instrumented);
+ return;
+ }
default:
break;
}
diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h
index 7d77721..b7e8d50 100644
--- a/runtime/entrypoints/quick/quick_default_externs.h
+++ b/runtime/entrypoints/quick/quick_default_externs.h
@@ -19,16 +19,25 @@
#include <cstdint>
+namespace art {
+namespace mirror {
+class Array;
+class ArtMethod;
+class Class;
+class Object;
+} // namespace mirror
+} // namespace art
+
// These are extern declarations of assembly stubs with common names.
// Cast entrypoints.
-extern "C" void art_quick_check_cast(void*, void*);
+extern "C" void art_quick_check_cast(const art::mirror::Class*, const art::mirror::Class*);
// DexCache entrypoints.
-extern "C" void* art_quick_initialize_static_storage(uint32_t, void*);
-extern "C" void* art_quick_initialize_type(uint32_t, void*);
-extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t, void*);
-extern "C" void* art_quick_resolve_string(void*, uint32_t);
+extern "C" void* art_quick_initialize_static_storage(uint32_t, art::mirror::ArtMethod*);
+extern "C" void* art_quick_initialize_type(uint32_t, art::mirror::ArtMethod*);
+extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t, art::mirror::ArtMethod*);
+extern "C" void* art_quick_resolve_string(uint32_t, art::mirror::ArtMethod*);
// Field entrypoints.
extern "C" int art_quick_set8_instance(uint32_t, void*, int8_t);
@@ -57,14 +66,16 @@
extern "C" void* art_quick_get_obj_static(uint32_t);
// Array entrypoints.
-extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*);
-extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*);
-extern "C" void art_quick_aput_obj(void*, uint32_t, void*);
+extern "C" void art_quick_aput_obj_with_null_and_bound_check(art::mirror::Array*, int32_t,
+ art::mirror::Object*);
+extern "C" void art_quick_aput_obj_with_bound_check(art::mirror::Array*, int32_t,
+ art::mirror::Object*);
+extern "C" void art_quick_aput_obj(art::mirror::Array*, int32_t, art::mirror::Object*);
extern "C" void art_quick_handle_fill_data(void*, void*);
// Lock entrypoints.
-extern "C" void art_quick_lock_object(void*);
-extern "C" void art_quick_unlock_object(void*);
+extern "C" void art_quick_lock_object(art::mirror::Object*);
+extern "C" void art_quick_unlock_object(art::mirror::Object*);
// Math entrypoints.
extern "C" int64_t art_quick_d2l(double);
@@ -99,7 +110,7 @@
extern "C" void art_quick_test_suspend();
// Throw entrypoints.
-extern "C" void art_quick_deliver_exception(void*);
+extern "C" void art_quick_deliver_exception(art::mirror::Object*);
extern "C" void art_quick_throw_array_bounds(int32_t index, int32_t limit);
extern "C" void art_quick_throw_div_zero();
extern "C" void art_quick_throw_no_such_method(int32_t method_idx);
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index 2e7c8ba..348495d 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -55,8 +55,8 @@
return ResolveVerifyAndClinit(type_idx, referrer, self, false, true);
}
-extern "C" mirror::String* artResolveStringFromCode(mirror::ArtMethod* referrer,
- int32_t string_idx,
+extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx,
+ mirror::ArtMethod* referrer,
Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
diff --git a/runtime/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h
index 8c108a8..db8c0e3 100644
--- a/runtime/entrypoints/quick/quick_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_entrypoints.h
@@ -28,6 +28,7 @@
namespace art {
namespace mirror {
+class Array;
class ArtMethod;
class Class;
class Object;
diff --git a/runtime/entrypoints/quick/quick_entrypoints_enum.h b/runtime/entrypoints/quick/quick_entrypoints_enum.h
index 84158cd..5a95491 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_enum.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_enum.h
@@ -18,6 +18,7 @@
#define ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_ENUM_H_
#include "quick_entrypoints.h"
+#include "quick_entrypoints_enum.h"
#include "thread.h"
namespace art {
@@ -47,10 +48,20 @@
#undef ENTRYPOINT_ENUM
};
LOG(FATAL) << "Unexpected trampoline " << static_cast<int>(trampoline);
- return ThreadOffset<pointer_size>(-1);
+ UNREACHABLE();
}
-} // namespace art
+// Do a check functions to be able to test whether the right signature is used.
+template <QuickEntrypointEnum entrypoint, typename... Types>
+void CheckEntrypointTypes();
+#define ENTRYPOINT_ENUM(name, ...) \
+template <> inline void CheckEntrypointTypes<kQuick ## name, __VA_ARGS__>() {}; // NOLINT [readability/braces] [4]
+#include "quick_entrypoints_list.h"
+ QUICK_ENTRYPOINT_LIST(ENTRYPOINT_ENUM)
+#undef QUICK_ENTRYPOINT_LIST
+#undef ENTRYPOINT_ENUM
+
+} // namespace art
#endif // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_ENUM_H_
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index fbc7913..da454f3 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -20,23 +20,23 @@
// All quick entrypoints. Format is name, return type, argument types.
#define QUICK_ENTRYPOINT_LIST(V) \
- V(AllocArray, void*, uint32_t, void*, int32_t) \
- V(AllocArrayResolved, void*, void*, void*, int32_t) \
- V(AllocArrayWithAccessCheck, void*, uint32_t, void*, int32_t) \
- V(AllocObject, void*, uint32_t, void*) \
- V(AllocObjectResolved, void*, void*, void*) \
- V(AllocObjectInitialized, void*, void*, void*) \
- V(AllocObjectWithAccessCheck, void*, uint32_t, void*) \
- V(CheckAndAllocArray, void*, uint32_t, void*, int32_t) \
- V(CheckAndAllocArrayWithAccessCheck, void*, uint32_t, void*, int32_t) \
+ V(AllocArray, void*, uint32_t, int32_t, mirror::ArtMethod*) \
+ V(AllocArrayResolved, void*, mirror::Class*, int32_t, mirror::ArtMethod*) \
+ V(AllocArrayWithAccessCheck, void*, uint32_t, int32_t, mirror::ArtMethod*) \
+ V(AllocObject, void*, uint32_t, mirror::ArtMethod*) \
+ V(AllocObjectResolved, void*, mirror::Class*, mirror::ArtMethod*) \
+ V(AllocObjectInitialized, void*, mirror::Class*, mirror::ArtMethod*) \
+ V(AllocObjectWithAccessCheck, void*, uint32_t, mirror::ArtMethod*) \
+ V(CheckAndAllocArray, void*, uint32_t, int32_t, mirror::ArtMethod*) \
+ V(CheckAndAllocArrayWithAccessCheck, void*, uint32_t, int32_t, mirror::ArtMethod*) \
\
V(InstanceofNonTrivial, uint32_t, const mirror::Class*, const mirror::Class*) \
- V(CheckCast, void , void*, void*) \
+ V(CheckCast, void, const mirror::Class*, const mirror::Class*) \
\
- V(InitializeStaticStorage, void*, uint32_t, void*) \
- V(InitializeTypeAndVerifyAccess, void*, uint32_t, void*) \
- V(InitializeType, void*, uint32_t, void*) \
- V(ResolveString, void*, void*, uint32_t) \
+ V(InitializeStaticStorage, void*, uint32_t, mirror::ArtMethod*) \
+ V(InitializeTypeAndVerifyAccess, void*, uint32_t, mirror::ArtMethod*) \
+ V(InitializeType, void*, uint32_t, mirror::ArtMethod*) \
+ V(ResolveString, void*, uint32_t, mirror::ArtMethod*) \
\
V(Set8Instance, int, uint32_t, void*, int8_t) \
V(Set8Static, int, uint32_t, int8_t) \
@@ -63,21 +63,21 @@
V(GetObjInstance, void*, uint32_t, void*) \
V(GetObjStatic, void*, uint32_t) \
\
- V(AputObjectWithNullAndBoundCheck, void, void*, uint32_t, void*) \
- V(AputObjectWithBoundCheck, void, void*, uint32_t, void*) \
- V(AputObject, void, void*, uint32_t, void*) \
+ V(AputObjectWithNullAndBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \
+ V(AputObjectWithBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \
+ V(AputObject, void, mirror::Array*, int32_t, mirror::Object*) \
V(HandleFillArrayData, void, void*, void*) \
\
V(JniMethodStart, uint32_t, Thread*) \
- V(JniMethodStartSynchronized, uint32_t, jobject to_lock, Thread* self) \
- V(JniMethodEnd, void, uint32_t cookie, Thread* self) \
- V(JniMethodEndSynchronized, void, uint32_t cookie, jobject locked, Thread* self) \
- V(JniMethodEndWithReference, mirror::Object*, jobject result, uint32_t cookie, Thread* self) \
- V(JniMethodEndWithReferenceSynchronized, mirror::Object*, jobject result, uint32_t cookie, jobject locked, Thread* self) \
+ V(JniMethodStartSynchronized, uint32_t, jobject, Thread*) \
+ V(JniMethodEnd, void, uint32_t, Thread*) \
+ V(JniMethodEndSynchronized, void, uint32_t, jobject, Thread*) \
+ V(JniMethodEndWithReference, mirror::Object*, jobject, uint32_t, Thread*) \
+ V(JniMethodEndWithReferenceSynchronized, mirror::Object*, jobject, uint32_t, jobject, Thread*) \
V(QuickGenericJniTrampoline, void, mirror::ArtMethod*) \
\
- V(LockObject, void, void*) \
- V(UnlockObject, void, void*) \
+ V(LockObject, void, mirror::Object*) \
+ V(UnlockObject, void, mirror::Object*) \
\
V(CmpgDouble, int32_t, double, double) \
V(CmpgFloat, int32_t, float, float) \
@@ -114,7 +114,7 @@
\
V(TestSuspend, void, void) \
\
- V(DeliverException, void, void*) \
+ V(DeliverException, void, mirror::Object*) \
V(ThrowArrayBounds, void, int32_t, int32_t) \
V(ThrowDivZero, void, void) \
V(ThrowNoSuchMethod, void, int32_t) \
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index cb81629..00251ff 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -59,10 +59,13 @@
// | S0 |
// | | 4x2 bytes padding
// | Method* | <- sp
+ static constexpr bool kSplitPairAcrossRegisterAndStack = kArm32QuickCodeUseSoftFloat;
+ static constexpr bool kAlignPairRegister = !kArm32QuickCodeUseSoftFloat;
static constexpr bool kQuickSoftFloatAbi = kArm32QuickCodeUseSoftFloat;
static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = !kArm32QuickCodeUseSoftFloat;
static constexpr size_t kNumQuickGprArgs = 3;
static constexpr size_t kNumQuickFprArgs = kArm32QuickCodeUseSoftFloat ? 0 : 16;
+ static constexpr bool kGprFprLockstep = false;
static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset =
arm::ArmCalleeSaveFpr1Offset(Runtime::kRefsAndArgs); // Offset of first FPR arg.
static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset =
@@ -93,10 +96,13 @@
// | D0 |
// | | padding
// | Method* | <- sp
+ static constexpr bool kSplitPairAcrossRegisterAndStack = false;
+ static constexpr bool kAlignPairRegister = false;
static constexpr bool kQuickSoftFloatAbi = false; // This is a hard float ABI.
static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false;
static constexpr size_t kNumQuickGprArgs = 7; // 7 arguments passed in GPRs.
static constexpr size_t kNumQuickFprArgs = 8; // 8 arguments passed in FPRs.
+ static constexpr bool kGprFprLockstep = false;
static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset =
arm64::Arm64CalleeSaveFpr1Offset(Runtime::kRefsAndArgs); // Offset of first FPR arg.
static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset =
@@ -106,7 +112,7 @@
static size_t GprIndexToGprOffset(uint32_t gpr_index) {
return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA);
}
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__)
// The callee save frame is pointed to by SP.
// | argN | |
// | ... | |
@@ -121,16 +127,67 @@
// | A2 | arg2
// | A1 | arg1
// | A0/Method* | <- sp
+ static constexpr bool kSplitPairAcrossRegisterAndStack = true;
+ static constexpr bool kAlignPairRegister = false;
static constexpr bool kQuickSoftFloatAbi = true; // This is a soft float ABI.
static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false;
static constexpr size_t kNumQuickGprArgs = 3; // 3 arguments passed in GPRs.
static constexpr size_t kNumQuickFprArgs = 0; // 0 arguments passed in FPRs.
+ static constexpr bool kGprFprLockstep = false;
static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 0; // Offset of first FPR arg.
- static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 4; // Offset of first GPR arg.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 16; // Offset of first GPR arg.
static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 60; // Offset of return address.
static size_t GprIndexToGprOffset(uint32_t gpr_index) {
return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA);
}
+#elif defined(__mips__) && defined(__LP64__)
+ // The callee save frame is pointed to by SP.
+ // | argN | |
+ // | ... | |
+ // | arg4 | |
+ // | arg3 spill | | Caller's frame
+ // | arg2 spill | |
+ // | arg1 spill | |
+ // | Method* | ---
+ // | RA |
+ // | ... | callee saves
+ // | F7 | f_arg7
+ // | F6 | f_arg6
+ // | F5 | f_arg5
+ // | F6 | f_arg6
+ // | F5 | f_arg5
+ // | F4 | f_arg4
+ // | F3 | f_arg3
+ // | F2 | f_arg2
+ // | F1 | f_arg1
+ // | F0 | f_arg0
+ // | A7 | arg7
+ // | A6 | arg6
+ // | A5 | arg5
+ // | A4 | arg4
+ // | A3 | arg3
+ // | A2 | arg2
+ // | A1 | arg1
+ // | | padding
+ // | A0/Method* | <- sp
+ // NOTE: for Mip64, when A0 is skipped, F0 is also skipped.
+ static constexpr bool kSplitPairAcrossRegisterAndStack = false;
+ static constexpr bool kAlignPairRegister = false;
+ static constexpr bool kQuickSoftFloatAbi = false;
+ static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false;
+ // These values are set to zeros because GPR and FPR register
+ // assignments for Mips64 are interleaved, which the current VisitArguments()
+ // function does not support.
+ static constexpr size_t kNumQuickGprArgs = 7; // 7 arguments passed in GPRs.
+ static constexpr size_t kNumQuickFprArgs = 7; // 7 arguments passed in FPRs.
+ static constexpr bool kGprFprLockstep = true;
+
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 24; // Offset of first FPR arg (F1).
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 80; // Offset of first GPR arg (A1).
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 200; // Offset of return address.
+ static size_t GprIndexToGprOffset(uint32_t gpr_index) {
+ return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA);
+ }
#elif defined(__i386__)
// The callee save frame is pointed to by SP.
// | argN | |
@@ -145,14 +202,21 @@
// | EBX | arg3
// | EDX | arg2
// | ECX | arg1
+ // | XMM3 | float arg 4
+ // | XMM2 | float arg 3
+ // | XMM1 | float arg 2
+ // | XMM0 | float arg 1
// | EAX/Method* | <- sp
- static constexpr bool kQuickSoftFloatAbi = true; // This is a soft float ABI.
+ static constexpr bool kSplitPairAcrossRegisterAndStack = false;
+ static constexpr bool kAlignPairRegister = false;
+ static constexpr bool kQuickSoftFloatAbi = false; // This is a hard float ABI.
static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false;
static constexpr size_t kNumQuickGprArgs = 3; // 3 arguments passed in GPRs.
- static constexpr size_t kNumQuickFprArgs = 0; // 0 arguments passed in FPRs.
- static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 0; // Offset of first FPR arg.
- static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 4; // Offset of first GPR arg.
- static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 28; // Offset of return address.
+ static constexpr size_t kNumQuickFprArgs = 4; // 4 arguments passed in FPRs.
+ static constexpr bool kGprFprLockstep = false;
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 4; // Offset of first FPR arg.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 4 + 4*8; // Offset of first GPR arg.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 28 + 4*8; // Offset of return address.
static size_t GprIndexToGprOffset(uint32_t gpr_index) {
return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA);
}
@@ -184,10 +248,13 @@
// | XMM0 | float arg 1
// | Padding |
// | RDI/Method* | <- sp
+ static constexpr bool kSplitPairAcrossRegisterAndStack = false;
+ static constexpr bool kAlignPairRegister = false;
static constexpr bool kQuickSoftFloatAbi = false; // This is a hard float ABI.
static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false;
static constexpr size_t kNumQuickGprArgs = 5; // 5 arguments passed in GPRs.
static constexpr size_t kNumQuickFprArgs = 8; // 8 arguments passed in FPRs.
+ static constexpr bool kGprFprLockstep = false;
static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 16; // Offset of first FPR arg.
static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 80 + 4*8; // Offset of first GPR arg.
static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 168 + 4*8; // Offset of return address.
@@ -208,6 +275,22 @@
#endif
public:
+ // Special handling for proxy methods. Proxy methods are instance methods so the
+ // 'this' object is the 1st argument. They also have the same frame layout as the
+ // kRefAndArgs runtime method. Since 'this' is a reference, it is located in the
+ // 1st GPR.
+ static mirror::Object* GetProxyThisObject(StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ CHECK(sp->AsMirrorPtr()->IsProxyMethod());
+ CHECK_EQ(kQuickCalleeSaveFrame_RefAndArgs_FrameSize, sp->AsMirrorPtr()->GetFrameSizeInBytes());
+ CHECK_GT(kNumQuickGprArgs, 0u);
+ constexpr uint32_t kThisGprIndex = 0u; // 'this' is in the 1st GPR.
+ size_t this_arg_offset = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset +
+ GprIndexToGprOffset(kThisGprIndex);
+ uint8_t* this_arg_address = reinterpret_cast<uint8_t*>(sp) + this_arg_offset;
+ return reinterpret_cast<StackReference<mirror::Object>*>(this_arg_address)->AsMirrorPtr();
+ }
+
static mirror::ArtMethod* GetCallingMethod(StackReference<mirror::ArtMethod>* sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(sp->AsMirrorPtr()->IsCalleeSaveMethod());
@@ -288,14 +371,23 @@
}
uint64_t ReadSplitLongParam() const {
- DCHECK(IsSplitLongOrDouble());
- // Read low half from register.
- uint64_t low_half = *reinterpret_cast<uint32_t*>(GetParamAddress());
- // Read high half from the stack. As current stack_index_ indexes the argument, the high part
- // index should be (stack_index_ + 1).
- uint64_t high_half = *reinterpret_cast<uint32_t*>(stack_args_
- + (stack_index_ + 1) * kBytesStackArgLocation);
- return (low_half & 0xffffffffULL) | (high_half << 32);
+ // The splitted long is always available through the stack.
+ return *reinterpret_cast<uint64_t*>(stack_args_
+ + stack_index_ * kBytesStackArgLocation);
+ }
+
+ void IncGprIndex() {
+ gpr_index_++;
+ if (kGprFprLockstep) {
+ fpr_index_++;
+ }
+ }
+
+ void IncFprIndex() {
+ fpr_index_++;
+ if (kGprFprLockstep) {
+ gpr_index_++;
+ }
}
void VisitArguments() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -314,7 +406,7 @@
Visit();
stack_index_++;
if (kNumQuickGprArgs > 0) {
- gpr_index_++;
+ IncGprIndex();
}
}
for (uint32_t shorty_index = 1; shorty_index < shorty_len_; ++shorty_index) {
@@ -330,7 +422,7 @@
Visit();
stack_index_++;
if (gpr_index_ < kNumQuickGprArgs) {
- gpr_index_++;
+ IncGprIndex();
}
break;
case Primitive::kPrimFloat:
@@ -339,11 +431,11 @@
stack_index_++;
if (kQuickSoftFloatAbi) {
if (gpr_index_ < kNumQuickGprArgs) {
- gpr_index_++;
+ IncGprIndex();
}
} else {
if (fpr_index_ + 1 < kNumQuickFprArgs + 1) {
- fpr_index_++;
+ IncFprIndex();
if (kQuickDoubleRegAlignedFloatBackFilled) {
// Double should not overlap with float.
// For example, if fpr_index_ = 3, fpr_double_index_ should be at least 4.
@@ -359,8 +451,18 @@
case Primitive::kPrimDouble:
case Primitive::kPrimLong:
if (kQuickSoftFloatAbi || (cur_type_ == Primitive::kPrimLong)) {
+ if (cur_type_ == Primitive::kPrimLong && kAlignPairRegister && gpr_index_ == 0) {
+ // Currently, this is only for ARM, where the first available parameter register
+ // is R1. So we skip it, and use R2 instead.
+ IncGprIndex();
+ }
is_split_long_or_double_ = (GetBytesPerGprSpillLocation(kRuntimeISA) == 4) &&
((gpr_index_ + 1) == kNumQuickGprArgs);
+ if (!kSplitPairAcrossRegisterAndStack && is_split_long_or_double_) {
+ // We don't want to split this. Pass over this register.
+ gpr_index_++;
+ is_split_long_or_double_ = false;
+ }
Visit();
if (kBytesStackArgLocation == 4) {
stack_index_+= 2;
@@ -369,10 +471,10 @@
stack_index_++;
}
if (gpr_index_ < kNumQuickGprArgs) {
- gpr_index_++;
+ IncGprIndex();
if (GetBytesPerGprSpillLocation(kRuntimeISA) == 4) {
if (gpr_index_ < kNumQuickGprArgs) {
- gpr_index_++;
+ IncGprIndex();
}
}
}
@@ -395,10 +497,10 @@
}
}
} else if (fpr_index_ + 1 < kNumQuickFprArgs + 1) {
- fpr_index_++;
+ IncFprIndex();
if (GetBytesPerFprSpillLocation(kRuntimeISA) == 4) {
if (fpr_index_ + 1 < kNumQuickFprArgs + 1) {
- fpr_index_++;
+ IncFprIndex();
}
}
}
@@ -435,6 +537,13 @@
bool is_split_long_or_double_;
};
+// Returns the 'this' object of a proxy method. This function is only used by StackVisitor. It
+// allows to use the QuickArgumentVisitor constants without moving all the code in its own module.
+extern "C" mirror::Object* artQuickGetProxyThisObject(StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return QuickArgumentVisitor::GetProxyThisObject(sp);
+}
+
// Visits arguments on the stack placing them into the shadow frame.
class BuildQuickShadowFrameVisitor FINAL : public QuickArgumentVisitor {
public:
@@ -832,6 +941,16 @@
(caller->GetDexCacheResolvedMethod(update_dex_cache_method_index) != called)) {
caller->SetDexCacheResolvedMethod(update_dex_cache_method_index, called);
}
+ } else if (invoke_type == kStatic) {
+ const auto called_dex_method_idx = called->GetDexMethodIndex();
+ // For static invokes, we may dispatch to the static method in the superclass but resolve
+ // using the subclass. To prevent getting slow paths on each invoke, we force set the
+ // resolved method for the super class dex method index if we are in the same dex file.
+ // b/19175856
+ if (called->GetDexFile() == called_method.dex_file &&
+ called_method.dex_method_index != called_dex_method_idx) {
+ called->GetDexCache()->SetResolvedMethod(called_dex_method_idx, called);
+ }
}
// Ensure that the called method's class is initialized.
StackHandleScope<1> hs(soa.Self());
@@ -911,7 +1030,8 @@
static constexpr size_t kRegistersNeededForLong = 2;
static constexpr size_t kRegistersNeededForDouble = 2;
static constexpr bool kMultiRegistersAligned = true;
- static constexpr bool kMultiRegistersWidened = false;
+ static constexpr bool kMultiFPRegistersWidened = false;
+ static constexpr bool kMultiGPRegistersWidened = false;
static constexpr bool kAlignLongOnStack = true;
static constexpr bool kAlignDoubleOnStack = true;
#elif defined(__aarch64__)
@@ -922,10 +1042,11 @@
static constexpr size_t kRegistersNeededForLong = 1;
static constexpr size_t kRegistersNeededForDouble = 1;
static constexpr bool kMultiRegistersAligned = false;
- static constexpr bool kMultiRegistersWidened = false;
+ static constexpr bool kMultiFPRegistersWidened = false;
+ static constexpr bool kMultiGPRegistersWidened = false;
static constexpr bool kAlignLongOnStack = false;
static constexpr bool kAlignDoubleOnStack = false;
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__)
static constexpr bool kNativeSoftFloatAbi = true; // This is a hard float ABI.
static constexpr size_t kNumNativeGprArgs = 4; // 4 arguments passed in GPRs.
static constexpr size_t kNumNativeFprArgs = 0; // 0 arguments passed in FPRs.
@@ -933,9 +1054,23 @@
static constexpr size_t kRegistersNeededForLong = 2;
static constexpr size_t kRegistersNeededForDouble = 2;
static constexpr bool kMultiRegistersAligned = true;
- static constexpr bool kMultiRegistersWidened = true;
+ static constexpr bool kMultiFPRegistersWidened = true;
+ static constexpr bool kMultiGPRegistersWidened = false;
static constexpr bool kAlignLongOnStack = true;
static constexpr bool kAlignDoubleOnStack = true;
+#elif defined(__mips__) && defined(__LP64__)
+ // Let the code prepare GPRs only and we will load the FPRs with same data.
+ static constexpr bool kNativeSoftFloatAbi = true;
+ static constexpr size_t kNumNativeGprArgs = 8;
+ static constexpr size_t kNumNativeFprArgs = 0;
+
+ static constexpr size_t kRegistersNeededForLong = 1;
+ static constexpr size_t kRegistersNeededForDouble = 1;
+ static constexpr bool kMultiRegistersAligned = false;
+ static constexpr bool kMultiFPRegistersWidened = false;
+ static constexpr bool kMultiGPRegistersWidened = true;
+ static constexpr bool kAlignLongOnStack = false;
+ static constexpr bool kAlignDoubleOnStack = false;
#elif defined(__i386__)
// TODO: Check these!
static constexpr bool kNativeSoftFloatAbi = false; // Not using int registers for fp
@@ -945,7 +1080,8 @@
static constexpr size_t kRegistersNeededForLong = 2;
static constexpr size_t kRegistersNeededForDouble = 2;
static constexpr bool kMultiRegistersAligned = false; // x86 not using regs, anyways
- static constexpr bool kMultiRegistersWidened = false;
+ static constexpr bool kMultiFPRegistersWidened = false;
+ static constexpr bool kMultiGPRegistersWidened = false;
static constexpr bool kAlignLongOnStack = false;
static constexpr bool kAlignDoubleOnStack = false;
#elif defined(__x86_64__)
@@ -956,7 +1092,8 @@
static constexpr size_t kRegistersNeededForLong = 1;
static constexpr size_t kRegistersNeededForDouble = 1;
static constexpr bool kMultiRegistersAligned = false;
- static constexpr bool kMultiRegistersWidened = false;
+ static constexpr bool kMultiFPRegistersWidened = false;
+ static constexpr bool kMultiGPRegistersWidened = false;
static constexpr bool kAlignLongOnStack = false;
static constexpr bool kAlignDoubleOnStack = false;
#else
@@ -1015,10 +1152,20 @@
void AdvanceInt(uint32_t val) {
if (HaveIntGpr()) {
gpr_index_--;
- PushGpr(val);
+ if (kMultiGPRegistersWidened) {
+ DCHECK_EQ(sizeof(uintptr_t), sizeof(int64_t));
+ PushGpr(static_cast<int64_t>(bit_cast<uint32_t, int32_t>(val)));
+ } else {
+ PushGpr(val);
+ }
} else {
stack_entries_++;
- PushStack(val);
+ if (kMultiGPRegistersWidened) {
+ DCHECK_EQ(sizeof(uintptr_t), sizeof(int64_t));
+ PushStack(static_cast<int64_t>(bit_cast<uint32_t, int32_t>(val)));
+ } else {
+ PushStack(val);
+ }
gpr_index_ = 0;
}
}
@@ -1080,7 +1227,7 @@
if (HaveFloatFpr()) {
fpr_index_--;
if (kRegistersNeededForDouble == 1) {
- if (kMultiRegistersWidened) {
+ if (kMultiFPRegistersWidened) {
PushFpr8(bit_cast<double, uint64_t>(val));
} else {
// No widening, just use the bits.
@@ -1091,7 +1238,7 @@
}
} else {
stack_entries_++;
- if (kRegistersNeededForDouble == 1 && kMultiRegistersWidened) {
+ if (kRegistersNeededForDouble == 1 && kMultiFPRegistersWidened) {
// Need to widen before storing: Note the "double" in the template instantiation.
// Note: We need to jump through those hoops to make the compiler happy.
DCHECK_EQ(sizeof(uintptr_t), sizeof(uint64_t));
diff --git a/runtime/entrypoints/runtime_asm_entrypoints.h b/runtime/entrypoints/runtime_asm_entrypoints.h
index db36a73..420e8db 100644
--- a/runtime/entrypoints/runtime_asm_entrypoints.h
+++ b/runtime/entrypoints/runtime_asm_entrypoints.h
@@ -28,66 +28,30 @@
return reinterpret_cast<const void*>(art_jni_dlsym_lookup_stub);
}
-// Return the address of portable stub code for handling IMT conflicts.
-extern "C" void art_portable_imt_conflict_trampoline(mirror::ArtMethod*);
-static inline const void* GetPortableImtConflictStub() {
- return reinterpret_cast<const void*>(art_portable_imt_conflict_trampoline);
-}
-
// Return the address of quick stub code for handling IMT conflicts.
extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*);
static inline const void* GetQuickImtConflictStub() {
return reinterpret_cast<const void*>(art_quick_imt_conflict_trampoline);
}
-// Return the address of portable stub code for bridging from portable code to the interpreter.
-extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*);
-static inline const void* GetPortableToInterpreterBridge() {
- return reinterpret_cast<const void*>(art_portable_to_interpreter_bridge);
-}
-
// Return the address of quick stub code for bridging from quick code to the interpreter.
extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
static inline const void* GetQuickToInterpreterBridge() {
return reinterpret_cast<const void*>(art_quick_to_interpreter_bridge);
}
-// Return the address of portable stub code for bridging from portable code to quick.
-static inline const void* GetPortableToQuickBridge() {
- // TODO: portable to quick bridge. Bug: 8196384
- return GetPortableToInterpreterBridge();
-}
-
-// Return the address of quick stub code for bridging from quick code to portable.
-static inline const void* GetQuickToPortableBridge() {
- // TODO: quick to portable bridge. Bug: 8196384
- return GetQuickToInterpreterBridge();
-}
-
// Return the address of quick stub code for handling JNI calls.
extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*);
static inline const void* GetQuickGenericJniStub() {
return reinterpret_cast<const void*>(art_quick_generic_jni_trampoline);
}
-// Return the address of portable stub code for handling transitions into the proxy invoke handler.
-extern "C" void art_portable_proxy_invoke_handler();
-static inline const void* GetPortableProxyInvokeHandler() {
- return reinterpret_cast<const void*>(art_portable_proxy_invoke_handler);
-}
-
// Return the address of quick stub code for handling transitions into the proxy invoke handler.
extern "C" void art_quick_proxy_invoke_handler();
static inline const void* GetQuickProxyInvokeHandler() {
return reinterpret_cast<const void*>(art_quick_proxy_invoke_handler);
}
-// Return the address of portable stub code for resolving a method at first call.
-extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*);
-static inline const void* GetPortableResolutionStub() {
- return reinterpret_cast<const void*>(art_portable_resolution_trampoline);
-}
-
// Return the address of quick stub code for resolving a method at first call.
extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*);
static inline const void* GetQuickResolutionStub() {
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index cfd2a3d..daa24c9 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -130,8 +130,11 @@
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_alloc_stack_top, thread_local_alloc_stack_end,
sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_alloc_stack_end, held_mutexes, sizeof(void*));
- EXPECT_OFFSET_DIFF(Thread, tlsPtr_.held_mutexes, Thread, wait_mutex_,
- sizeof(void*) * kLockLevelCount + sizeof(void*), thread_tlsptr_end);
+ EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, held_mutexes, nested_signal_state,
+ sizeof(void*) * kLockLevelCount);
+ EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, nested_signal_state, flip_function, sizeof(void*));
+ EXPECT_OFFSET_DIFF(Thread, tlsPtr_.flip_function, Thread, wait_mutex_, sizeof(void*),
+ thread_tlsptr_end);
}
void CheckInterpreterEntryPoints() {
@@ -150,17 +153,6 @@
+ sizeof(void*) == sizeof(JniEntryPoints), JniEntryPoints_all);
}
- void CheckPortableEntryPoints() {
- CHECKED(OFFSETOF_MEMBER(PortableEntryPoints, pPortableImtConflictTrampoline) == 0,
- PortableEntryPoints_start_with_imt);
- EXPECT_OFFSET_DIFFNP(PortableEntryPoints, pPortableImtConflictTrampoline,
- pPortableResolutionTrampoline, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(PortableEntryPoints, pPortableResolutionTrampoline,
- pPortableToInterpreterBridge, sizeof(void*));
- CHECKED(OFFSETOF_MEMBER(PortableEntryPoints, pPortableToInterpreterBridge)
- + sizeof(void*) == sizeof(PortableEntryPoints), PortableEntryPoints_all);
- }
-
void CheckQuickEntryPoints() {
CHECKED(OFFSETOF_MEMBER(QuickEntryPoints, pAllocArray) == 0,
QuickEntryPoints_start_with_allocarray);
@@ -296,10 +288,6 @@
CheckJniEntryPoints();
}
-TEST_F(EntrypointsOrderTest, PortableEntryPoints) {
- CheckPortableEntryPoints();
-}
-
TEST_F(EntrypointsOrderTest, QuickEntryPoints) {
CheckQuickEntryPoints();
}
diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc
index 580b541..1770658 100644
--- a/runtime/exception_test.cc
+++ b/runtime/exception_test.cc
@@ -19,6 +19,7 @@
#include "class_linker.h"
#include "common_runtime_test.h"
#include "dex_file.h"
+#include "dex_file-inl.h"
#include "gtest/gtest.h"
#include "leb128.h"
#include "mirror/class-inl.h"
@@ -174,61 +175,42 @@
// ASSERT_EQ(sizeof(uintptr_t), sizeof(uint32_t));
- if (!kUsePortableCompiler) {
- // Create three fake stack frames with mapping data created in SetUp. We map offset 3 in the
- // code to dex pc 3.
- const uint32_t dex_pc = 3;
+ // Create three fake stack frames with mapping data created in SetUp. We map offset 3 in the
+ // code to dex pc 3.
+ const uint32_t dex_pc = 3;
- // Create the stack frame for the callee save method, expected by the runtime.
- fake_stack.push_back(reinterpret_cast<uintptr_t>(save_method));
- for (size_t i = 0; i < frame_info.FrameSizeInBytes() - 2 * sizeof(uintptr_t);
- i += sizeof(uintptr_t)) {
- fake_stack.push_back(0);
- }
-
- fake_stack.push_back(method_g_->ToNativeQuickPc(dex_pc)); // return pc
-
- // Create/push fake 16byte stack frame for method g
- fake_stack.push_back(reinterpret_cast<uintptr_t>(method_g_));
+ // Create the stack frame for the callee save method, expected by the runtime.
+ fake_stack.push_back(reinterpret_cast<uintptr_t>(save_method));
+ for (size_t i = 0; i < frame_info.FrameSizeInBytes() - 2 * sizeof(uintptr_t);
+ i += sizeof(uintptr_t)) {
fake_stack.push_back(0);
- fake_stack.push_back(0);
- fake_stack.push_back(method_f_->ToNativeQuickPc(dex_pc)); // return pc
-
- // Create/push fake 16byte stack frame for method f
- fake_stack.push_back(reinterpret_cast<uintptr_t>(method_f_));
- fake_stack.push_back(0);
- fake_stack.push_back(0);
- fake_stack.push_back(0xEBAD6070); // return pc
-
- // Push Method* of NULL to terminate the trace
- fake_stack.push_back(0);
-
- // Push null values which will become null incoming arguments.
- fake_stack.push_back(0);
- fake_stack.push_back(0);
- fake_stack.push_back(0);
-
- // Set up thread to appear as if we called out of method_g_ at pc dex 3
- thread->SetTopOfStack(reinterpret_cast<StackReference<mirror::ArtMethod>*>(&fake_stack[0]));
- } else {
- // Create/push fake 20-byte shadow frame for method g
- fake_stack.push_back(0);
- fake_stack.push_back(0);
- fake_stack.push_back(reinterpret_cast<uintptr_t>(method_g_));
- fake_stack.push_back(3);
- fake_stack.push_back(0);
-
- // Create/push fake 20-byte shadow frame for method f
- fake_stack.push_back(0);
- fake_stack.push_back(0);
- fake_stack.push_back(reinterpret_cast<uintptr_t>(method_f_));
- fake_stack.push_back(3);
- fake_stack.push_back(0);
-
- thread->PushShadowFrame(reinterpret_cast<ShadowFrame*>(&fake_stack[5]));
- thread->PushShadowFrame(reinterpret_cast<ShadowFrame*>(&fake_stack[0]));
}
+ fake_stack.push_back(method_g_->ToNativeQuickPc(dex_pc)); // return pc
+
+ // Create/push fake 16byte stack frame for method g
+ fake_stack.push_back(reinterpret_cast<uintptr_t>(method_g_));
+ fake_stack.push_back(0);
+ fake_stack.push_back(0);
+ fake_stack.push_back(method_f_->ToNativeQuickPc(dex_pc)); // return pc
+
+ // Create/push fake 16byte stack frame for method f
+ fake_stack.push_back(reinterpret_cast<uintptr_t>(method_f_));
+ fake_stack.push_back(0);
+ fake_stack.push_back(0);
+ fake_stack.push_back(0xEBAD6070); // return pc
+
+ // Push Method* of NULL to terminate the trace
+ fake_stack.push_back(0);
+
+ // Push null values which will become null incoming arguments.
+ fake_stack.push_back(0);
+ fake_stack.push_back(0);
+ fake_stack.push_back(0);
+
+ // Set up thread to appear as if we called out of method_g_ at pc dex 3
+ thread->SetTopOfStack(reinterpret_cast<StackReference<mirror::ArtMethod>*>(&fake_stack[0]));
+
jobject internal = thread->CreateInternalStackTrace<false>(soa);
ASSERT_TRUE(internal != nullptr);
jobjectArray ste_array = Thread::InternalStackTraceToStackTraceElementArray(soa, internal);
@@ -253,12 +235,7 @@
EXPECT_STREQ("f", trace_array->Get(1)->GetMethodName()->ToModifiedUtf8().c_str());
EXPECT_EQ(22, trace_array->Get(1)->GetLineNumber());
- if (!kUsePortableCompiler) {
- thread->SetTopOfStack(nullptr); // Disarm the assertion that no code is running when we detach.
- } else {
- thread->PopShadowFrame();
- thread->PopShadowFrame();
- }
+ thread->SetTopOfStack(nullptr); // Disarm the assertion that no code is running when we detach.
}
} // namespace art
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 94753d4..83f3ae1 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -340,7 +340,8 @@
// TODO: check the GC maps to make sure it's an object.
// Check that the class pointer inside the object is not null and is aligned.
// TODO: Method might be not a heap address, and GetClass could fault.
- mirror::Class* cls = method_obj->GetClass<kVerifyNone>();
+ // No read barrier because method_obj may not be a real object.
+ mirror::Class* cls = method_obj->GetClass<kVerifyNone, kWithoutReadBarrier>();
if (cls == nullptr) {
VLOG(signals) << "not a class";
return false;
@@ -440,4 +441,3 @@
}
} // namespace art
-
diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h
index 929a1d2..72734e9 100644
--- a/runtime/gc/accounting/atomic_stack.h
+++ b/runtime/gc/accounting/atomic_stack.h
@@ -25,15 +25,34 @@
#include "base/logging.h"
#include "base/macros.h"
#include "mem_map.h"
+#include "stack.h"
#include "utils.h"
namespace art {
namespace gc {
namespace accounting {
+// Internal representation is StackReference<T>, so this only works with mirror::Object or it's
+// subclasses.
template <typename T>
class AtomicStack {
public:
+ class ObjectComparator {
+ public:
+ // These two comparators are for std::binary_search.
+ bool operator()(const T* a, const StackReference<T>& b) const NO_THREAD_SAFETY_ANALYSIS {
+ return a < b.AsMirrorPtr();
+ }
+ bool operator()(const StackReference<T>& a, const T* b) const NO_THREAD_SAFETY_ANALYSIS {
+ return a.AsMirrorPtr() < b;
+ }
+ // This comparator is for std::sort.
+ bool operator()(const StackReference<T>& a, const StackReference<T>& b) const
+ NO_THREAD_SAFETY_ANALYSIS {
+ return a.AsMirrorPtr() < b.AsMirrorPtr();
+ }
+ };
+
// Capacity is how many elements we can store in the stack.
static AtomicStack* Create(const std::string& name, size_t growth_limit, size_t capacity) {
std::unique_ptr<AtomicStack> mark_stack(new AtomicStack(name, growth_limit, capacity));
@@ -45,7 +64,7 @@
void Reset() {
DCHECK(mem_map_.get() != nullptr);
- DCHECK(begin_ != NULL);
+ DCHECK(begin_ != nullptr);
front_index_.StoreRelaxed(0);
back_index_.StoreRelaxed(0);
debug_is_sorted_ = true;
@@ -55,18 +74,20 @@
// Beware: Mixing atomic pushes and atomic pops will cause ABA problem.
// Returns false if we overflowed the stack.
- bool AtomicPushBackIgnoreGrowthLimit(const T& value) {
+ bool AtomicPushBackIgnoreGrowthLimit(T* value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return AtomicPushBackInternal(value, capacity_);
}
// Returns false if we overflowed the stack.
- bool AtomicPushBack(const T& value) {
+ bool AtomicPushBack(T* value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return AtomicPushBackInternal(value, growth_limit_);
}
// Atomically bump the back index by the given number of
// slots. Returns false if we overflowed the stack.
- bool AtomicBumpBack(size_t num_slots, T** start_address, T** end_address) {
+ bool AtomicBumpBack(size_t num_slots, StackReference<T>** start_address,
+ StackReference<T>** end_address)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (kIsDebugBuild) {
debug_is_sorted_ = false;
}
@@ -80,41 +101,41 @@
return false;
}
} while (!back_index_.CompareExchangeWeakRelaxed(index, new_index));
- *start_address = &begin_[index];
- *end_address = &begin_[new_index];
+ *start_address = begin_ + index;
+ *end_address = begin_ + new_index;
if (kIsDebugBuild) {
// Sanity check that the memory is zero.
for (int32_t i = index; i < new_index; ++i) {
- DCHECK_EQ(begin_[i], static_cast<T>(0))
+ DCHECK_EQ(begin_[i].AsMirrorPtr(), static_cast<T*>(nullptr))
<< "i=" << i << " index=" << index << " new_index=" << new_index;
}
}
return true;
}
- void AssertAllZero() {
+ void AssertAllZero() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (kIsDebugBuild) {
for (size_t i = 0; i < capacity_; ++i) {
- DCHECK_EQ(begin_[i], static_cast<T>(0)) << "i=" << i;
+ DCHECK_EQ(begin_[i].AsMirrorPtr(), static_cast<T*>(nullptr)) << "i=" << i;
}
}
}
- void PushBack(const T& value) {
+ void PushBack(T* value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (kIsDebugBuild) {
debug_is_sorted_ = false;
}
- int32_t index = back_index_.LoadRelaxed();
+ const int32_t index = back_index_.LoadRelaxed();
DCHECK_LT(static_cast<size_t>(index), growth_limit_);
back_index_.StoreRelaxed(index + 1);
- begin_[index] = value;
+ begin_[index].Assign(value);
}
- T PopBack() {
+ T* PopBack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK_GT(back_index_.LoadRelaxed(), front_index_.LoadRelaxed());
// Decrement the back index non atomically.
back_index_.StoreRelaxed(back_index_.LoadRelaxed() - 1);
- return begin_[back_index_.LoadRelaxed()];
+ return begin_[back_index_.LoadRelaxed()].AsMirrorPtr();
}
// Take an item from the front of the stack.
@@ -140,12 +161,11 @@
return back_index_.LoadRelaxed() - front_index_.LoadRelaxed();
}
- T* Begin() const {
- return const_cast<T*>(begin_ + front_index_.LoadRelaxed());
+ StackReference<T>* Begin() const {
+ return begin_ + front_index_.LoadRelaxed();
}
-
- T* End() const {
- return const_cast<T*>(begin_ + back_index_.LoadRelaxed());
+ StackReference<T>* End() const {
+ return begin_ + back_index_.LoadRelaxed();
}
size_t Capacity() const {
@@ -162,7 +182,7 @@
void Sort() {
int32_t start_back_index = back_index_.LoadRelaxed();
int32_t start_front_index = front_index_.LoadRelaxed();
- std::sort(Begin(), End());
+ std::sort(Begin(), End(), ObjectComparator());
CHECK_EQ(start_back_index, back_index_.LoadRelaxed());
CHECK_EQ(start_front_index, front_index_.LoadRelaxed());
if (kIsDebugBuild) {
@@ -170,13 +190,18 @@
}
}
- bool ContainsSorted(const T& value) const {
+ bool ContainsSorted(const T* value) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(debug_is_sorted_);
- return std::binary_search(Begin(), End(), value);
+ return std::binary_search(Begin(), End(), value, ObjectComparator());
}
- bool Contains(const T& value) const {
- return std::find(Begin(), End(), value) != End();
+ bool Contains(const T* value) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ for (auto cur = Begin(), end = End(); cur != end; ++cur) {
+ if (cur->AsMirrorPtr() == value) {
+ return true;
+ }
+ }
+ return false;
}
private:
@@ -191,7 +216,8 @@
}
// Returns false if we overflowed the stack.
- bool AtomicPushBackInternal(const T& value, size_t limit) ALWAYS_INLINE {
+ bool AtomicPushBackInternal(T* value, size_t limit) ALWAYS_INLINE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (kIsDebugBuild) {
debug_is_sorted_ = false;
}
@@ -203,20 +229,20 @@
return false;
}
} while (!back_index_.CompareExchangeWeakRelaxed(index, index + 1));
- begin_[index] = value;
+ begin_[index].Assign(value);
return true;
}
// Size in number of elements.
void Init() {
std::string error_msg;
- mem_map_.reset(MemMap::MapAnonymous(name_.c_str(), NULL, capacity_ * sizeof(T),
+ mem_map_.reset(MemMap::MapAnonymous(name_.c_str(), NULL, capacity_ * sizeof(begin_[0]),
PROT_READ | PROT_WRITE, false, &error_msg));
CHECK(mem_map_.get() != NULL) << "couldn't allocate mark stack.\n" << error_msg;
uint8_t* addr = mem_map_->Begin();
CHECK(addr != NULL);
debug_is_sorted_ = true;
- begin_ = reinterpret_cast<T*>(addr);
+ begin_ = reinterpret_cast<StackReference<T>*>(addr);
Reset();
}
@@ -229,7 +255,7 @@
// Front index, used for implementing PopFront.
AtomicInteger front_index_;
// Base of the atomic stack.
- T* begin_;
+ StackReference<T>* begin_;
// Current maximum which we can push back to, must be <= capacity_.
size_t growth_limit_;
// Maximum number of elements.
@@ -240,7 +266,7 @@
DISALLOW_COPY_AND_ASSIGN(AtomicStack);
};
-typedef AtomicStack<mirror::Object*> ObjectStack;
+typedef AtomicStack<mirror::Object> ObjectStack;
} // namespace accounting
} // namespace gc
diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h
index 15562e5..83ad33e 100644
--- a/runtime/gc/accounting/card_table-inl.h
+++ b/runtime/gc/accounting/card_table-inl.h
@@ -48,7 +48,7 @@
#endif
}
-template <typename Visitor>
+template <bool kClearCard, typename Visitor>
inline size_t CardTable::Scan(ContinuousSpaceBitmap* bitmap, uint8_t* scan_begin, uint8_t* scan_end,
const Visitor& visitor, const uint8_t minimum_age) const {
DCHECK_GE(scan_begin, reinterpret_cast<uint8_t*>(bitmap->HeapBegin()));
@@ -66,6 +66,9 @@
uintptr_t start = reinterpret_cast<uintptr_t>(AddrFromCard(card_cur));
bitmap->VisitMarkedRange(start, start + kCardSize, visitor);
++cards_scanned;
+ if (kClearCard) {
+ *card_cur = 0;
+ }
}
++card_cur;
}
@@ -95,6 +98,9 @@
<< "card " << static_cast<size_t>(*card) << " intptr_t " << (start_word & 0xFF);
bitmap->VisitMarkedRange(start, start + kCardSize, visitor);
++cards_scanned;
+ if (kClearCard) {
+ *card = 0;
+ }
}
start_word >>= 8;
start += kCardSize;
@@ -109,6 +115,9 @@
uintptr_t start = reinterpret_cast<uintptr_t>(AddrFromCard(card_cur));
bitmap->VisitMarkedRange(start, start + kCardSize, visitor);
++cards_scanned;
+ if (kClearCard) {
+ *card_cur = 0;
+ }
}
++card_cur;
}
diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc
index b7b6099..ca1e7c1 100644
--- a/runtime/gc/accounting/card_table.cc
+++ b/runtime/gc/accounting/card_table.cc
@@ -102,6 +102,26 @@
mem_map_->MadviseDontNeedAndZero();
}
+void CardTable::ClearCardRange(uint8_t* start, uint8_t* end) {
+ if (!kMadviseZeroes) {
+ memset(start, 0, end - start);
+ return;
+ }
+ CHECK_ALIGNED(reinterpret_cast<uintptr_t>(start), kCardSize);
+ CHECK_ALIGNED(reinterpret_cast<uintptr_t>(end), kCardSize);
+ static_assert(kCardClean == 0, "kCardClean must be 0");
+ uint8_t* start_card = CardFromAddr(start);
+ uint8_t* end_card = CardFromAddr(end);
+ uint8_t* round_start = AlignUp(start_card, kPageSize);
+ uint8_t* round_end = AlignDown(end_card, kPageSize);
+ if (round_start < round_end) {
+ madvise(round_start, round_end - round_start, MADV_DONTNEED);
+ }
+ // Handle unaligned regions at start / end.
+ memset(start_card, 0, std::min(round_start, end_card) - start_card);
+ memset(std::max(round_end, start_card), 0, end_card - std::max(round_end, start_card));
+}
+
bool CardTable::AddrIsInCardTable(const void* addr) const {
return IsValidCard(biased_begin_ + ((uintptr_t)addr >> kCardShift));
}
diff --git a/runtime/gc/accounting/card_table.h b/runtime/gc/accounting/card_table.h
index 9bd3fba..3ea7651 100644
--- a/runtime/gc/accounting/card_table.h
+++ b/runtime/gc/accounting/card_table.h
@@ -101,7 +101,7 @@
// For every dirty at least minumum age between begin and end invoke the visitor with the
// specified argument. Returns how many cards the visitor was run on.
- template <typename Visitor>
+ template <bool kClearCard, typename Visitor>
size_t Scan(SpaceBitmap<kObjectAlignment>* bitmap, uint8_t* scan_begin, uint8_t* scan_end,
const Visitor& visitor,
const uint8_t minimum_age = kCardDirty) const
@@ -113,6 +113,7 @@
// Resets all of the bytes in the card table to clean.
void ClearCardTable();
+ void ClearCardRange(uint8_t* start, uint8_t* end);
// Resets all of the bytes in the card table which do not map to the image space.
void ClearSpaceCards(space::ContinuousSpace* space);
diff --git a/runtime/gc/accounting/heap_bitmap-inl.h b/runtime/gc/accounting/heap_bitmap-inl.h
index 34c15c7..8fcc87d 100644
--- a/runtime/gc/accounting/heap_bitmap-inl.h
+++ b/runtime/gc/accounting/heap_bitmap-inl.h
@@ -105,6 +105,15 @@
return nullptr;
}
+inline LargeObjectBitmap* HeapBitmap::GetLargeObjectBitmap(const mirror::Object* obj) const {
+ for (const auto& bitmap : large_object_bitmaps_) {
+ if (LIKELY(bitmap->HasAddress(obj))) {
+ return bitmap;
+ }
+ }
+ return nullptr;
+}
+
} // namespace accounting
} // namespace gc
} // namespace art
diff --git a/runtime/gc/accounting/heap_bitmap.h b/runtime/gc/accounting/heap_bitmap.h
index ca6dc46..245e074 100644
--- a/runtime/gc/accounting/heap_bitmap.h
+++ b/runtime/gc/accounting/heap_bitmap.h
@@ -27,6 +27,10 @@
class Heap;
+namespace collector {
+ class ConcurrentCopying;
+} // namespace collector
+
namespace accounting {
class HeapBitmap {
@@ -40,6 +44,7 @@
bool AtomicTestAndSet(const mirror::Object* obj, const LargeObjectSetVisitor& visitor)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) ALWAYS_INLINE;
ContinuousSpaceBitmap* GetContinuousSpaceBitmap(const mirror::Object* obj) const;
+ LargeObjectBitmap* GetLargeObjectBitmap(const mirror::Object* obj) const;
void Walk(ObjectCallback* callback, void* arg)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
@@ -78,6 +83,7 @@
large_object_bitmaps_;
friend class art::gc::Heap;
+ friend class art::gc::collector::ConcurrentCopying;
};
} // namespace accounting
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index 0a15e9e..b1ccc0b 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -26,6 +26,7 @@
#include "gc/collector/mark_sweep-inl.h"
#include "gc/heap.h"
#include "gc/space/space.h"
+#include "gc/space/image_space.h"
#include "mirror/art_field-inl.h"
#include "mirror/object-inl.h"
#include "mirror/class-inl.h"
@@ -76,8 +77,9 @@
public:
ModUnionUpdateObjectReferencesVisitor(MarkHeapReferenceCallback* callback, void* arg,
space::ContinuousSpace* from_space,
+ space::ImageSpace* image_space,
bool* contains_reference_to_other_space)
- : callback_(callback), arg_(arg), from_space_(from_space),
+ : callback_(callback), arg_(arg), from_space_(from_space), image_space_(image_space),
contains_reference_to_other_space_(contains_reference_to_other_space) {
}
@@ -87,7 +89,7 @@
// Only add the reference if it is non null and fits our criteria.
mirror::HeapReference<Object>* obj_ptr = obj->GetFieldObjectReferenceAddr(offset);
mirror::Object* ref = obj_ptr->AsMirrorPtr();
- if (ref != nullptr && !from_space_->HasAddress(ref)) {
+ if (ref != nullptr && !from_space_->HasAddress(ref) && !image_space_->HasAddress(ref)) {
*contains_reference_to_other_space_ = true;
callback_(obj_ptr, arg_);
}
@@ -98,6 +100,7 @@
void* arg_;
// Space which we are scanning
space::ContinuousSpace* const from_space_;
+ space::ImageSpace* const image_space_;
// Set if we have any references to another space.
bool* const contains_reference_to_other_space_;
};
@@ -105,16 +108,16 @@
class ModUnionScanImageRootVisitor {
public:
ModUnionScanImageRootVisitor(MarkHeapReferenceCallback* callback, void* arg,
- space::ContinuousSpace* from_space,
+ space::ContinuousSpace* from_space, space::ImageSpace* image_space,
bool* contains_reference_to_other_space)
- : callback_(callback), arg_(arg), from_space_(from_space),
+ : callback_(callback), arg_(arg), from_space_(from_space), image_space_(image_space),
contains_reference_to_other_space_(contains_reference_to_other_space) {}
void operator()(Object* root) const
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(root != NULL);
- ModUnionUpdateObjectReferencesVisitor ref_visitor(callback_, arg_, from_space_,
+ ModUnionUpdateObjectReferencesVisitor ref_visitor(callback_, arg_, from_space_, image_space_,
contains_reference_to_other_space_);
root->VisitReferences<kMovingClasses>(ref_visitor, VoidFunctor());
}
@@ -124,6 +127,7 @@
void* const arg_;
// Space which we are scanning
space::ContinuousSpace* const from_space_;
+ space::ImageSpace* const image_space_;
// Set if we have any references to another space.
bool* const contains_reference_to_other_space_;
};
@@ -331,9 +335,11 @@
void ModUnionTableCardCache::UpdateAndMarkReferences(MarkHeapReferenceCallback* callback,
void* arg) {
CardTable* card_table = heap_->GetCardTable();
+ space::ImageSpace* image_space = heap_->GetImageSpace();
ContinuousSpaceBitmap* bitmap = space_->GetLiveBitmap();
bool reference_to_other_space = false;
- ModUnionScanImageRootVisitor scan_visitor(callback, arg, space_, &reference_to_other_space);
+ ModUnionScanImageRootVisitor scan_visitor(callback, arg, space_, image_space,
+ &reference_to_other_space);
for (auto it = cleared_cards_.begin(), end = cleared_cards_.end(); it != end; ) {
uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(*it));
DCHECK(space_->HasAddress(reinterpret_cast<Object*>(start)));
diff --git a/runtime/gc/accounting/read_barrier_table.h b/runtime/gc/accounting/read_barrier_table.h
new file mode 100644
index 0000000..84d5da3
--- /dev/null
+++ b/runtime/gc/accounting/read_barrier_table.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_GC_ACCOUNTING_READ_BARRIER_TABLE_H_
+#define ART_RUNTIME_GC_ACCOUNTING_READ_BARRIER_TABLE_H_
+
+#include "base/mutex.h"
+#include "gc/space/space.h"
+#include "globals.h"
+#include "mem_map.h"
+
+namespace art {
+namespace gc {
+namespace accounting {
+
+// Used to decide whether to take the read barrier fast/slow paths for
+// kUseTableLookupReadBarrier. If an entry is set, take the read
+// barrier slow path. There's an entry per region.
+class ReadBarrierTable {
+ public:
+ ReadBarrierTable() {
+ size_t capacity = static_cast<size_t>(kHeapCapacity / kRegionSize);
+ DCHECK_EQ(kHeapCapacity / kRegionSize,
+ static_cast<uint64_t>(static_cast<size_t>(kHeapCapacity / kRegionSize)));
+ std::string error_msg;
+ MemMap* mem_map = MemMap::MapAnonymous("read barrier table", nullptr, capacity,
+ PROT_READ | PROT_WRITE, false, &error_msg);
+ CHECK(mem_map != nullptr && mem_map->Begin() != nullptr)
+ << "couldn't allocate read barrier table: " << error_msg;
+ mem_map_.reset(mem_map);
+ }
+ void ClearForSpace(space::ContinuousSpace* space) {
+ uint8_t* entry_start = EntryFromAddr(space->Begin());
+ uint8_t* entry_end = EntryFromAddr(space->Limit());
+ memset(reinterpret_cast<void*>(entry_start), 0, entry_end - entry_start);
+ }
+ void Clear(uint8_t* start_addr, uint8_t* end_addr) {
+ DCHECK(IsValidHeapAddr(start_addr)) << start_addr;
+ DCHECK(IsValidHeapAddr(end_addr)) << end_addr;
+ DCHECK(IsAligned<kRegionSize>(start_addr));
+ DCHECK(IsAligned<kRegionSize>(end_addr));
+ uint8_t* entry_start = EntryFromAddr(start_addr);
+ uint8_t* entry_end = EntryFromAddr(end_addr);
+ memset(reinterpret_cast<void*>(entry_start), 0, entry_end - entry_start);
+ }
+ bool IsSet(const void* heap_addr) const {
+ DCHECK(IsValidHeapAddr(heap_addr)) << heap_addr;
+ uint8_t entry_value = *EntryFromAddr(heap_addr);
+ DCHECK(entry_value == 0 || entry_value == kSetEntryValue);
+ return entry_value == kSetEntryValue;
+ }
+ void ClearAll() {
+ mem_map_->MadviseDontNeedAndZero();
+ }
+ void SetAll() {
+ memset(mem_map_->Begin(), kSetEntryValue, mem_map_->Size());
+ }
+ bool IsAllCleared() const {
+ for (uint32_t* p = reinterpret_cast<uint32_t*>(mem_map_->Begin());
+ p < reinterpret_cast<uint32_t*>(mem_map_->End()); ++p) {
+ if (*p != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // This should match RegionSpace::kRegionSize. static_assert'ed in concurrent_copying.h.
+ static constexpr size_t kRegionSize = 1 * MB;
+
+ private:
+ static constexpr uint64_t kHeapCapacity = 4ULL * GB; // low 4gb.
+ static constexpr uint8_t kSetEntryValue = 0x01;
+
+ uint8_t* EntryFromAddr(const void* heap_addr) const {
+ DCHECK(IsValidHeapAddr(heap_addr)) << heap_addr;
+ uint8_t* entry_addr = mem_map_->Begin() + reinterpret_cast<uintptr_t>(heap_addr) / kRegionSize;
+ DCHECK(IsValidEntry(entry_addr)) << "heap_addr: " << heap_addr
+ << " entry_addr: " << reinterpret_cast<void*>(entry_addr);
+ return entry_addr;
+ }
+
+ bool IsValidHeapAddr(const void* heap_addr) const {
+#ifdef __LP64__
+ return reinterpret_cast<uint64_t>(heap_addr) < kHeapCapacity;
+#else
+ UNUSED(heap_addr);
+ return true;
+#endif
+ }
+
+ bool IsValidEntry(const uint8_t* entry_addr) const {
+ uint8_t* begin = mem_map_->Begin();
+ uint8_t* end = mem_map_->End();
+ return entry_addr >= begin && entry_addr < end;
+ }
+
+ std::unique_ptr<MemMap> mem_map_;
+};
+
+} // namespace accounting
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_ACCOUNTING_READ_BARRIER_TABLE_H_
diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc
index feb9565..f5d3b47 100644
--- a/runtime/gc/accounting/space_bitmap.cc
+++ b/runtime/gc/accounting/space_bitmap.cc
@@ -17,6 +17,7 @@
#include "space_bitmap-inl.h"
#include "base/stringprintf.h"
+#include "dex_file-inl.h"
#include "mem_map.h"
#include "mirror/object-inl.h"
#include "mirror/class.h"
diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h
index e73166b..7bc83ef 100644
--- a/runtime/gc/accounting/space_bitmap.h
+++ b/runtime/gc/accounting/space_bitmap.h
@@ -160,6 +160,12 @@
return IndexToOffset<uint64_t>(Size() / sizeof(intptr_t));
}
+ void SetHeapSize(size_t bytes) {
+ // TODO: Un-map the end of the mem map.
+ bitmap_size_ = OffsetToIndex(bytes) * sizeof(intptr_t);
+ CHECK_EQ(HeapSize(), bytes);
+ }
+
uintptr_t HeapBegin() const {
return heap_begin_;
}
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index 7c2474f..7996241 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -1119,12 +1119,19 @@
uint8_t* slot_base = reinterpret_cast<uint8_t*>(this) + headerSizes[idx];
size_t num_slots = numOfSlots[idx];
size_t bracket_size = IndexToBracketSize(idx);
- DCHECK_EQ(slot_base + num_slots * bracket_size, reinterpret_cast<uint8_t*>(this) + numOfPages[idx] * kPageSize);
+ DCHECK_EQ(slot_base + num_slots * bracket_size,
+ reinterpret_cast<uint8_t*>(this) + numOfPages[idx] * kPageSize);
size_t num_vec = RoundUp(num_slots, 32) / 32;
size_t slots = 0;
+ const uint32_t* const tl_free_vecp = IsThreadLocal() ? ThreadLocalFreeBitMap() : nullptr;
for (size_t v = 0; v < num_vec; v++, slots += 32) {
DCHECK_GE(num_slots, slots);
uint32_t vec = alloc_bit_map_[v];
+ if (tl_free_vecp != nullptr) {
+ // Clear out the set bits in the thread local free bitmap since these aren't actually
+ // allocated.
+ vec &= ~tl_free_vecp[v];
+ }
size_t end = std::min(num_slots - slots, static_cast<size_t>(32));
for (size_t i = 0; i < end; ++i) {
bool is_allocated = ((vec >> i) & 0x1) != 0;
diff --git a/runtime/gc/allocator_type.h b/runtime/gc/allocator_type.h
index c6ebc73..f9a2ff6 100644
--- a/runtime/gc/allocator_type.h
+++ b/runtime/gc/allocator_type.h
@@ -30,6 +30,8 @@
kAllocatorTypeDlMalloc, // Use dlmalloc allocator, has entrypoints.
kAllocatorTypeNonMoving, // Special allocator for non moving objects, doesn't have entrypoints.
kAllocatorTypeLOS, // Large object space, also doesn't have entrypoints.
+ kAllocatorTypeRegion,
+ kAllocatorTypeRegionTLAB,
};
std::ostream& operator<<(std::ostream& os, const AllocatorType& rhs);
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 079eeba..734c935 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -16,10 +16,1635 @@
#include "concurrent_copying.h"
+#include "gc/accounting/heap_bitmap-inl.h"
+#include "gc/accounting/space_bitmap-inl.h"
+#include "gc/space/image_space.h"
+#include "gc/space/space.h"
+#include "intern_table.h"
+#include "mirror/art_field-inl.h"
+#include "mirror/object-inl.h"
+#include "scoped_thread_state_change.h"
+#include "thread-inl.h"
+#include "thread_list.h"
+#include "well_known_classes.h"
+
namespace art {
namespace gc {
namespace collector {
+ConcurrentCopying::ConcurrentCopying(Heap* heap, const std::string& name_prefix)
+ : GarbageCollector(heap,
+ name_prefix + (name_prefix.empty() ? "" : " ") +
+ "concurrent copying + mark sweep"),
+ region_space_(nullptr), gc_barrier_(new Barrier(0)), mark_queue_(2 * MB),
+ is_marking_(false), is_active_(false), is_asserting_to_space_invariant_(false),
+ heap_mark_bitmap_(nullptr), live_stack_freeze_size_(0),
+ skipped_blocks_lock_("concurrent copying bytes blocks lock", kMarkSweepMarkStackLock),
+ rb_table_(heap_->GetReadBarrierTable()),
+ force_evacuate_all_(false) {
+ static_assert(space::RegionSpace::kRegionSize == accounting::ReadBarrierTable::kRegionSize,
+ "The region space size and the read barrier table region size must match");
+ cc_heap_bitmap_.reset(new accounting::HeapBitmap(heap));
+ {
+ Thread* self = Thread::Current();
+ ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ // Cache this so that we won't have to lock heap_bitmap_lock_ in
+ // Mark() which could cause a nested lock on heap_bitmap_lock_
+ // when GC causes a RB while doing GC or a lock order violation
+ // (class_linker_lock_ and heap_bitmap_lock_).
+ heap_mark_bitmap_ = heap->GetMarkBitmap();
+ }
+}
+
+ConcurrentCopying::~ConcurrentCopying() {
+}
+
+void ConcurrentCopying::RunPhases() {
+ CHECK(kUseBakerReadBarrier || kUseTableLookupReadBarrier);
+ CHECK(!is_active_);
+ is_active_ = true;
+ Thread* self = Thread::Current();
+ Locks::mutator_lock_->AssertNotHeld(self);
+ {
+ ReaderMutexLock mu(self, *Locks::mutator_lock_);
+ InitializePhase();
+ }
+ FlipThreadRoots();
+ {
+ ReaderMutexLock mu(self, *Locks::mutator_lock_);
+ MarkingPhase();
+ }
+ // Verify no from space refs. This causes a pause.
+ if (kEnableNoFromSpaceRefsVerification || kIsDebugBuild) {
+ TimingLogger::ScopedTiming split("(Paused)VerifyNoFromSpaceReferences", GetTimings());
+ ScopedPause pause(this);
+ CheckEmptyMarkQueue();
+ if (kVerboseMode) {
+ LOG(INFO) << "Verifying no from-space refs";
+ }
+ VerifyNoFromSpaceReferences();
+ CheckEmptyMarkQueue();
+ }
+ {
+ ReaderMutexLock mu(self, *Locks::mutator_lock_);
+ ReclaimPhase();
+ }
+ FinishPhase();
+ CHECK(is_active_);
+ is_active_ = false;
+}
+
+void ConcurrentCopying::BindBitmaps() {
+ Thread* self = Thread::Current();
+ WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ // Mark all of the spaces we never collect as immune.
+ for (const auto& space : heap_->GetContinuousSpaces()) {
+ if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyNeverCollect
+ || space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect) {
+ CHECK(space->IsZygoteSpace() || space->IsImageSpace());
+ CHECK(immune_region_.AddContinuousSpace(space)) << "Failed to add space " << *space;
+ const char* bitmap_name = space->IsImageSpace() ? "cc image space bitmap" :
+ "cc zygote space bitmap";
+ // TODO: try avoiding using bitmaps for image/zygote to save space.
+ accounting::ContinuousSpaceBitmap* bitmap =
+ accounting::ContinuousSpaceBitmap::Create(bitmap_name, space->Begin(), space->Capacity());
+ cc_heap_bitmap_->AddContinuousSpaceBitmap(bitmap);
+ cc_bitmaps_.push_back(bitmap);
+ } else if (space == region_space_) {
+ accounting::ContinuousSpaceBitmap* bitmap =
+ accounting::ContinuousSpaceBitmap::Create("cc region space bitmap",
+ space->Begin(), space->Capacity());
+ cc_heap_bitmap_->AddContinuousSpaceBitmap(bitmap);
+ cc_bitmaps_.push_back(bitmap);
+ region_space_bitmap_ = bitmap;
+ }
+ }
+}
+
+void ConcurrentCopying::InitializePhase() {
+ TimingLogger::ScopedTiming split("InitializePhase", GetTimings());
+ if (kVerboseMode) {
+ LOG(INFO) << "GC InitializePhase";
+ LOG(INFO) << "Region-space : " << reinterpret_cast<void*>(region_space_->Begin()) << "-"
+ << reinterpret_cast<void*>(region_space_->Limit());
+ }
+ CHECK(mark_queue_.IsEmpty());
+ immune_region_.Reset();
+ bytes_moved_.StoreRelaxed(0);
+ objects_moved_.StoreRelaxed(0);
+ if (GetCurrentIteration()->GetGcCause() == kGcCauseExplicit ||
+ GetCurrentIteration()->GetGcCause() == kGcCauseForNativeAlloc ||
+ GetCurrentIteration()->GetClearSoftReferences()) {
+ force_evacuate_all_ = true;
+ } else {
+ force_evacuate_all_ = false;
+ }
+ BindBitmaps();
+ if (kVerboseMode) {
+ LOG(INFO) << "force_evacuate_all=" << force_evacuate_all_;
+ LOG(INFO) << "Immune region: " << immune_region_.Begin() << "-" << immune_region_.End();
+ LOG(INFO) << "GC end of InitializePhase";
+ }
+}
+
+// Used to switch the thread roots of a thread from from-space refs to to-space refs.
+class ThreadFlipVisitor : public Closure {
+ public:
+ explicit ThreadFlipVisitor(ConcurrentCopying* concurrent_copying, bool use_tlab)
+ : concurrent_copying_(concurrent_copying), use_tlab_(use_tlab) {
+ }
+
+ virtual void Run(Thread* thread) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Note: self is not necessarily equal to thread since thread may be suspended.
+ Thread* self = Thread::Current();
+ CHECK(thread == self || thread->IsSuspended() || thread->GetState() == kWaitingPerformingGc)
+ << thread->GetState() << " thread " << thread << " self " << self;
+ if (use_tlab_ && thread->HasTlab()) {
+ if (ConcurrentCopying::kEnableFromSpaceAccountingCheck) {
+ // This must come before the revoke.
+ size_t thread_local_objects = thread->GetThreadLocalObjectsAllocated();
+ concurrent_copying_->region_space_->RevokeThreadLocalBuffers(thread);
+ reinterpret_cast<Atomic<size_t>*>(&concurrent_copying_->from_space_num_objects_at_first_pause_)->
+ FetchAndAddSequentiallyConsistent(thread_local_objects);
+ } else {
+ concurrent_copying_->region_space_->RevokeThreadLocalBuffers(thread);
+ }
+ }
+ if (kUseThreadLocalAllocationStack) {
+ thread->RevokeThreadLocalAllocationStack();
+ }
+ ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ thread->VisitRoots(ConcurrentCopying::ProcessRootCallback, concurrent_copying_);
+ concurrent_copying_->GetBarrier().Pass(self);
+ }
+
+ private:
+ ConcurrentCopying* const concurrent_copying_;
+ const bool use_tlab_;
+};
+
+// Called back from Runtime::FlipThreadRoots() during a pause.
+class FlipCallback : public Closure {
+ public:
+ explicit FlipCallback(ConcurrentCopying* concurrent_copying)
+ : concurrent_copying_(concurrent_copying) {
+ }
+
+ virtual void Run(Thread* thread) OVERRIDE EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ConcurrentCopying* cc = concurrent_copying_;
+ TimingLogger::ScopedTiming split("(Paused)FlipCallback", cc->GetTimings());
+ // Note: self is not necessarily equal to thread since thread may be suspended.
+ Thread* self = Thread::Current();
+ CHECK(thread == self);
+ Locks::mutator_lock_->AssertExclusiveHeld(self);
+ cc->region_space_->SetFromSpace(cc->rb_table_, cc->force_evacuate_all_);
+ cc->SwapStacks(self);
+ if (ConcurrentCopying::kEnableFromSpaceAccountingCheck) {
+ cc->RecordLiveStackFreezeSize(self);
+ cc->from_space_num_objects_at_first_pause_ = cc->region_space_->GetObjectsAllocated();
+ cc->from_space_num_bytes_at_first_pause_ = cc->region_space_->GetBytesAllocated();
+ }
+ cc->is_marking_ = true;
+ if (UNLIKELY(Runtime::Current()->IsActiveTransaction())) {
+ CHECK(Runtime::Current()->IsCompiler());
+ TimingLogger::ScopedTiming split2("(Paused)VisitTransactionRoots", cc->GetTimings());
+ Runtime::Current()->VisitTransactionRoots(ConcurrentCopying::ProcessRootCallback, cc);
+ }
+ }
+
+ private:
+ ConcurrentCopying* const concurrent_copying_;
+};
+
+// Switch threads that from from-space to to-space refs. Forward/mark the thread roots.
+void ConcurrentCopying::FlipThreadRoots() {
+ TimingLogger::ScopedTiming split("FlipThreadRoots", GetTimings());
+ if (kVerboseMode) {
+ LOG(INFO) << "time=" << region_space_->Time();
+ region_space_->DumpNonFreeRegions(LOG(INFO));
+ }
+ Thread* self = Thread::Current();
+ Locks::mutator_lock_->AssertNotHeld(self);
+ gc_barrier_->Init(self, 0);
+ ThreadFlipVisitor thread_flip_visitor(this, heap_->use_tlab_);
+ FlipCallback flip_callback(this);
+ size_t barrier_count = Runtime::Current()->FlipThreadRoots(
+ &thread_flip_visitor, &flip_callback, this);
+ {
+ ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
+ gc_barrier_->Increment(self, barrier_count);
+ }
+ is_asserting_to_space_invariant_ = true;
+ QuasiAtomic::ThreadFenceForConstructor();
+ if (kVerboseMode) {
+ LOG(INFO) << "time=" << region_space_->Time();
+ region_space_->DumpNonFreeRegions(LOG(INFO));
+ LOG(INFO) << "GC end of FlipThreadRoots";
+ }
+}
+
+void ConcurrentCopying::SwapStacks(Thread* self) {
+ heap_->SwapStacks(self);
+}
+
+void ConcurrentCopying::RecordLiveStackFreezeSize(Thread* self) {
+ WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ live_stack_freeze_size_ = heap_->GetLiveStack()->Size();
+}
+
+// Used to visit objects in the immune spaces.
+class ConcurrentCopyingImmuneSpaceObjVisitor {
+ public:
+ explicit ConcurrentCopyingImmuneSpaceObjVisitor(ConcurrentCopying* cc)
+ : collector_(cc) {}
+
+ void operator()(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
+ DCHECK(obj != nullptr);
+ DCHECK(collector_->immune_region_.ContainsObject(obj));
+ accounting::ContinuousSpaceBitmap* cc_bitmap =
+ collector_->cc_heap_bitmap_->GetContinuousSpaceBitmap(obj);
+ DCHECK(cc_bitmap != nullptr)
+ << "An immune space object must have a bitmap";
+ if (kIsDebugBuild) {
+ DCHECK(collector_->heap_->GetMarkBitmap()->Test(obj))
+ << "Immune space object must be already marked";
+ }
+ // This may or may not succeed, which is ok.
+ if (kUseBakerReadBarrier) {
+ obj->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
+ }
+ if (cc_bitmap->AtomicTestAndSet(obj)) {
+ // Already marked. Do nothing.
+ } else {
+ // Newly marked. Set the gray bit and push it onto the mark stack.
+ CHECK(!kUseBakerReadBarrier || obj->GetReadBarrierPointer() == ReadBarrier::GrayPtr());
+ collector_->PushOntoMarkStack<true>(obj);
+ }
+ }
+
+ private:
+ ConcurrentCopying* collector_;
+};
+
+class EmptyCheckpoint : public Closure {
+ public:
+ explicit EmptyCheckpoint(ConcurrentCopying* concurrent_copying)
+ : concurrent_copying_(concurrent_copying) {
+ }
+
+ virtual void Run(Thread* thread) OVERRIDE NO_THREAD_SAFETY_ANALYSIS {
+ // Note: self is not necessarily equal to thread since thread may be suspended.
+ Thread* self = Thread::Current();
+ CHECK(thread == self || thread->IsSuspended() || thread->GetState() == kWaitingPerformingGc)
+ << thread->GetState() << " thread " << thread << " self " << self;
+ // If thread is a running mutator, then act on behalf of the garbage collector.
+ // See the code in ThreadList::RunCheckpoint.
+ if (thread->GetState() == kRunnable) {
+ concurrent_copying_->GetBarrier().Pass(self);
+ }
+ }
+
+ private:
+ ConcurrentCopying* const concurrent_copying_;
+};
+
+// Concurrently mark roots that are guarded by read barriers and process the mark stack.
+void ConcurrentCopying::MarkingPhase() {
+ TimingLogger::ScopedTiming split("MarkingPhase", GetTimings());
+ if (kVerboseMode) {
+ LOG(INFO) << "GC MarkingPhase";
+ }
+ {
+ // Mark the image root. The WB-based collectors do not need to
+ // scan the image objects from roots by relying on the card table,
+ // but it's necessary for the RB to-space invariant to hold.
+ TimingLogger::ScopedTiming split1("VisitImageRoots", GetTimings());
+ gc::space::ImageSpace* image = heap_->GetImageSpace();
+ if (image != nullptr) {
+ mirror::ObjectArray<mirror::Object>* image_root = image->GetImageHeader().GetImageRoots();
+ mirror::Object* marked_image_root = Mark(image_root);
+ CHECK_EQ(image_root, marked_image_root) << "An image object does not move";
+ if (ReadBarrier::kEnableToSpaceInvariantChecks) {
+ AssertToSpaceInvariant(nullptr, MemberOffset(0), marked_image_root);
+ }
+ }
+ }
+ {
+ TimingLogger::ScopedTiming split2("VisitConstantRoots", GetTimings());
+ Runtime::Current()->VisitConstantRoots(ProcessRootCallback, this);
+ }
+ {
+ TimingLogger::ScopedTiming split3("VisitInternTableRoots", GetTimings());
+ Runtime::Current()->GetInternTable()->VisitRoots(ProcessRootCallback,
+ this, kVisitRootFlagAllRoots);
+ }
+ {
+ TimingLogger::ScopedTiming split4("VisitClassLinkerRoots", GetTimings());
+ Runtime::Current()->GetClassLinker()->VisitRoots(ProcessRootCallback,
+ this, kVisitRootFlagAllRoots);
+ }
+ {
+ // TODO: don't visit the transaction roots if it's not active.
+ TimingLogger::ScopedTiming split5("VisitNonThreadRoots", GetTimings());
+ Runtime::Current()->VisitNonThreadRoots(ProcessRootCallback, this);
+ }
+
+ // Immune spaces.
+ for (auto& space : heap_->GetContinuousSpaces()) {
+ if (immune_region_.ContainsSpace(space)) {
+ DCHECK(space->IsImageSpace() || space->IsZygoteSpace());
+ accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
+ ConcurrentCopyingImmuneSpaceObjVisitor visitor(this);
+ live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()),
+ reinterpret_cast<uintptr_t>(space->Limit()),
+ visitor);
+ }
+ }
+
+ Thread* self = Thread::Current();
+ {
+ TimingLogger::ScopedTiming split6("ProcessMarkStack", GetTimings());
+ // Process the mark stack and issue an empty check point. If the
+ // mark stack is still empty after the check point, we're
+ // done. Otherwise, repeat.
+ ProcessMarkStack();
+ size_t count = 0;
+ while (!ProcessMarkStack()) {
+ ++count;
+ if (kVerboseMode) {
+ LOG(INFO) << "Issue an empty check point. " << count;
+ }
+ IssueEmptyCheckpoint();
+ }
+ // Need to ensure the mark stack is empty before reference
+ // processing to get rid of non-reference gray objects.
+ CheckEmptyMarkQueue();
+ // Enable the GetReference slow path and disallow access to the system weaks.
+ GetHeap()->GetReferenceProcessor()->EnableSlowPath();
+ Runtime::Current()->DisallowNewSystemWeaks();
+ QuasiAtomic::ThreadFenceForConstructor();
+ // Lock-unlock the system weak locks so that there's no thread in
+ // the middle of accessing system weaks.
+ Runtime::Current()->EnsureNewSystemWeaksDisallowed();
+ // Note: Do not issue a checkpoint from here to the
+ // SweepSystemWeaks call or else a deadlock due to
+ // WaitHoldingLocks() would occur.
+ if (kVerboseMode) {
+ LOG(INFO) << "Enabled the ref proc slow path & disabled access to system weaks.";
+ LOG(INFO) << "ProcessReferences";
+ }
+ ProcessReferences(self, true);
+ CheckEmptyMarkQueue();
+ if (kVerboseMode) {
+ LOG(INFO) << "SweepSystemWeaks";
+ }
+ SweepSystemWeaks(self);
+ if (kVerboseMode) {
+ LOG(INFO) << "SweepSystemWeaks done";
+ }
+ // Because hash_set::Erase() can call the hash function for
+ // arbitrary elements in the weak intern table in
+ // InternTable::Table::SweepWeaks(), the above SweepSystemWeaks()
+ // call may have marked some objects (strings) alive. So process
+ // the mark stack here once again.
+ ProcessMarkStack();
+ CheckEmptyMarkQueue();
+ // Disable marking.
+ if (kUseTableLookupReadBarrier) {
+ heap_->rb_table_->ClearAll();
+ DCHECK(heap_->rb_table_->IsAllCleared());
+ }
+ is_mark_queue_push_disallowed_.StoreSequentiallyConsistent(1);
+ is_marking_ = false;
+ if (kVerboseMode) {
+ LOG(INFO) << "AllowNewSystemWeaks";
+ }
+ Runtime::Current()->AllowNewSystemWeaks();
+ CheckEmptyMarkQueue();
+ }
+
+ if (kVerboseMode) {
+ LOG(INFO) << "GC end of MarkingPhase";
+ }
+}
+
+void ConcurrentCopying::IssueEmptyCheckpoint() {
+ Thread* self = Thread::Current();
+ EmptyCheckpoint check_point(this);
+ ThreadList* thread_list = Runtime::Current()->GetThreadList();
+ gc_barrier_->Init(self, 0);
+ size_t barrier_count = thread_list->RunCheckpoint(&check_point);
+ // If there are no threads to wait which implys that all the checkpoint functions are finished,
+ // then no need to release the mutator lock.
+ if (barrier_count == 0) {
+ return;
+ }
+ // Release locks then wait for all mutator threads to pass the barrier.
+ Locks::mutator_lock_->SharedUnlock(self);
+ {
+ ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
+ gc_barrier_->Increment(self, barrier_count);
+ }
+ Locks::mutator_lock_->SharedLock(self);
+}
+
+mirror::Object* ConcurrentCopying::PopOffMarkStack() {
+ return mark_queue_.Dequeue();
+}
+
+template<bool kThreadSafe>
+void ConcurrentCopying::PushOntoMarkStack(mirror::Object* to_ref) {
+ CHECK_EQ(is_mark_queue_push_disallowed_.LoadRelaxed(), 0)
+ << " " << to_ref << " " << PrettyTypeOf(to_ref);
+ if (kThreadSafe) {
+ CHECK(mark_queue_.Enqueue(to_ref)) << "Mark queue overflow";
+ } else {
+ CHECK(mark_queue_.EnqueueThreadUnsafe(to_ref)) << "Mark queue overflow";
+ }
+}
+
+accounting::ObjectStack* ConcurrentCopying::GetAllocationStack() {
+ return heap_->allocation_stack_.get();
+}
+
+accounting::ObjectStack* ConcurrentCopying::GetLiveStack() {
+ return heap_->live_stack_.get();
+}
+
+inline mirror::Object* ConcurrentCopying::GetFwdPtr(mirror::Object* from_ref) {
+ DCHECK(region_space_->IsInFromSpace(from_ref));
+ LockWord lw = from_ref->GetLockWord(false);
+ if (lw.GetState() == LockWord::kForwardingAddress) {
+ mirror::Object* fwd_ptr = reinterpret_cast<mirror::Object*>(lw.ForwardingAddress());
+ CHECK(fwd_ptr != nullptr);
+ return fwd_ptr;
+ } else {
+ return nullptr;
+ }
+}
+
+inline void ConcurrentCopying::SetFwdPtr(mirror::Object* from_ref, mirror::Object* to_ref) {
+ DCHECK(region_space_->IsInFromSpace(from_ref));
+ DCHECK(region_space_->IsInToSpace(to_ref) || heap_->GetNonMovingSpace()->HasAddress(to_ref));
+ LockWord lw = from_ref->GetLockWord(false);
+ DCHECK_NE(lw.GetState(), LockWord::kForwardingAddress);
+ from_ref->SetLockWord(LockWord::FromForwardingAddress(reinterpret_cast<size_t>(to_ref)), false);
+}
+
+// The following visitors are that used to verify that there's no
+// references to the from-space left after marking.
+class ConcurrentCopyingVerifyNoFromSpaceRefsVisitor {
+ public:
+ explicit ConcurrentCopyingVerifyNoFromSpaceRefsVisitor(ConcurrentCopying* collector)
+ : collector_(collector) {}
+
+ void operator()(mirror::Object* ref) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+ if (ref == nullptr) {
+ // OK.
+ return;
+ }
+ collector_->AssertToSpaceInvariant(nullptr, MemberOffset(0), ref);
+ if (kUseBakerReadBarrier) {
+ if (collector_->RegionSpace()->IsInToSpace(ref)) {
+ CHECK(ref->GetReadBarrierPointer() == nullptr)
+ << "To-space ref " << ref << " " << PrettyTypeOf(ref)
+ << " has non-white rb_ptr " << ref->GetReadBarrierPointer();
+ } else {
+ CHECK(ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr() ||
+ (ref->GetReadBarrierPointer() == ReadBarrier::WhitePtr() &&
+ collector_->IsOnAllocStack(ref)))
+ << "Non-moving/unevac from space ref " << ref << " " << PrettyTypeOf(ref)
+ << " has non-black rb_ptr " << ref->GetReadBarrierPointer()
+ << " but isn't on the alloc stack (and has white rb_ptr)."
+ << " Is it in the non-moving space="
+ << (collector_->GetHeap()->GetNonMovingSpace()->HasAddress(ref));
+ }
+ }
+ }
+
+ static void RootCallback(mirror::Object** root, void *arg, const RootInfo& /*root_info*/)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ConcurrentCopying* collector = reinterpret_cast<ConcurrentCopying*>(arg);
+ ConcurrentCopyingVerifyNoFromSpaceRefsVisitor visitor(collector);
+ DCHECK(root != nullptr);
+ visitor(*root);
+ }
+
+ private:
+ ConcurrentCopying* collector_;
+};
+
+class ConcurrentCopyingVerifyNoFromSpaceRefsFieldVisitor {
+ public:
+ explicit ConcurrentCopyingVerifyNoFromSpaceRefsFieldVisitor(ConcurrentCopying* collector)
+ : collector_(collector) {}
+
+ void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+ mirror::Object* ref =
+ obj->GetFieldObject<mirror::Object, kDefaultVerifyFlags, kWithoutReadBarrier>(offset);
+ ConcurrentCopyingVerifyNoFromSpaceRefsVisitor visitor(collector_);
+ visitor(ref);
+ }
+ void operator()(mirror::Class* klass, mirror::Reference* ref) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+ CHECK(klass->IsTypeOfReferenceClass());
+ this->operator()(ref, mirror::Reference::ReferentOffset(), false);
+ }
+
+ private:
+ ConcurrentCopying* collector_;
+};
+
+class ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor {
+ public:
+ explicit ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor(ConcurrentCopying* collector)
+ : collector_(collector) {}
+ void operator()(mirror::Object* obj) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ObjectCallback(obj, collector_);
+ }
+ static void ObjectCallback(mirror::Object* obj, void *arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ CHECK(obj != nullptr);
+ ConcurrentCopying* collector = reinterpret_cast<ConcurrentCopying*>(arg);
+ space::RegionSpace* region_space = collector->RegionSpace();
+ CHECK(!region_space->IsInFromSpace(obj)) << "Scanning object " << obj << " in from space";
+ ConcurrentCopyingVerifyNoFromSpaceRefsFieldVisitor visitor(collector);
+ obj->VisitReferences<true>(visitor, visitor);
+ if (kUseBakerReadBarrier) {
+ if (collector->RegionSpace()->IsInToSpace(obj)) {
+ CHECK(obj->GetReadBarrierPointer() == nullptr)
+ << "obj=" << obj << " non-white rb_ptr " << obj->GetReadBarrierPointer();
+ } else {
+ CHECK(obj->GetReadBarrierPointer() == ReadBarrier::BlackPtr() ||
+ (obj->GetReadBarrierPointer() == ReadBarrier::WhitePtr() &&
+ collector->IsOnAllocStack(obj)))
+ << "Non-moving space/unevac from space ref " << obj << " " << PrettyTypeOf(obj)
+ << " has non-black rb_ptr " << obj->GetReadBarrierPointer()
+ << " but isn't on the alloc stack (and has white rb_ptr). Is it in the non-moving space="
+ << (collector->GetHeap()->GetNonMovingSpace()->HasAddress(obj));
+ }
+ }
+ }
+
+ private:
+ ConcurrentCopying* const collector_;
+};
+
+// Verify there's no from-space references left after the marking phase.
+void ConcurrentCopying::VerifyNoFromSpaceReferences() {
+ Thread* self = Thread::Current();
+ DCHECK(Locks::mutator_lock_->IsExclusiveHeld(self));
+ ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor visitor(this);
+ // Roots.
+ {
+ ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ Runtime::Current()->VisitRoots(
+ ConcurrentCopyingVerifyNoFromSpaceRefsVisitor::RootCallback, this);
+ }
+ // The to-space.
+ region_space_->WalkToSpace(ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor::ObjectCallback,
+ this);
+ // Non-moving spaces.
+ {
+ WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ heap_->GetMarkBitmap()->Visit(visitor);
+ }
+ // The alloc stack.
+ {
+ ConcurrentCopyingVerifyNoFromSpaceRefsVisitor ref_visitor(this);
+ for (auto* it = heap_->allocation_stack_->Begin(), *end = heap_->allocation_stack_->End();
+ it < end; ++it) {
+ mirror::Object* const obj = it->AsMirrorPtr();
+ if (obj != nullptr && obj->GetClass() != nullptr) {
+ // TODO: need to call this only if obj is alive?
+ ref_visitor(obj);
+ visitor(obj);
+ }
+ }
+ }
+ // TODO: LOS. But only refs in LOS are classes.
+}
+
+// The following visitors are used to assert the to-space invariant.
+class ConcurrentCopyingAssertToSpaceInvariantRefsVisitor {
+ public:
+ explicit ConcurrentCopyingAssertToSpaceInvariantRefsVisitor(ConcurrentCopying* collector)
+ : collector_(collector) {}
+
+ void operator()(mirror::Object* ref) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+ if (ref == nullptr) {
+ // OK.
+ return;
+ }
+ collector_->AssertToSpaceInvariant(nullptr, MemberOffset(0), ref);
+ }
+ static void RootCallback(mirror::Object** root, void *arg, const RootInfo& /*root_info*/)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ConcurrentCopying* collector = reinterpret_cast<ConcurrentCopying*>(arg);
+ ConcurrentCopyingAssertToSpaceInvariantRefsVisitor visitor(collector);
+ DCHECK(root != nullptr);
+ visitor(*root);
+ }
+
+ private:
+ ConcurrentCopying* collector_;
+};
+
+class ConcurrentCopyingAssertToSpaceInvariantFieldVisitor {
+ public:
+ explicit ConcurrentCopyingAssertToSpaceInvariantFieldVisitor(ConcurrentCopying* collector)
+ : collector_(collector) {}
+
+ void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+ mirror::Object* ref =
+ obj->GetFieldObject<mirror::Object, kDefaultVerifyFlags, kWithoutReadBarrier>(offset);
+ ConcurrentCopyingAssertToSpaceInvariantRefsVisitor visitor(collector_);
+ visitor(ref);
+ }
+ void operator()(mirror::Class* klass, mirror::Reference* /* ref */) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+ CHECK(klass->IsTypeOfReferenceClass());
+ }
+
+ private:
+ ConcurrentCopying* collector_;
+};
+
+class ConcurrentCopyingAssertToSpaceInvariantObjectVisitor {
+ public:
+ explicit ConcurrentCopyingAssertToSpaceInvariantObjectVisitor(ConcurrentCopying* collector)
+ : collector_(collector) {}
+ void operator()(mirror::Object* obj) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ObjectCallback(obj, collector_);
+ }
+ static void ObjectCallback(mirror::Object* obj, void *arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ CHECK(obj != nullptr);
+ ConcurrentCopying* collector = reinterpret_cast<ConcurrentCopying*>(arg);
+ space::RegionSpace* region_space = collector->RegionSpace();
+ CHECK(!region_space->IsInFromSpace(obj)) << "Scanning object " << obj << " in from space";
+ collector->AssertToSpaceInvariant(nullptr, MemberOffset(0), obj);
+ ConcurrentCopyingAssertToSpaceInvariantFieldVisitor visitor(collector);
+ obj->VisitReferences<true>(visitor, visitor);
+ }
+
+ private:
+ ConcurrentCopying* collector_;
+};
+
+bool ConcurrentCopying::ProcessMarkStack() {
+ if (kVerboseMode) {
+ LOG(INFO) << "ProcessMarkStack. ";
+ }
+ size_t count = 0;
+ mirror::Object* to_ref;
+ while ((to_ref = PopOffMarkStack()) != nullptr) {
+ ++count;
+ DCHECK(!region_space_->IsInFromSpace(to_ref));
+ if (kUseBakerReadBarrier) {
+ DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr())
+ << " " << to_ref << " " << to_ref->GetReadBarrierPointer()
+ << " is_marked=" << IsMarked(to_ref);
+ }
+ // Scan ref fields.
+ Scan(to_ref);
+ // Mark the gray ref as white or black.
+ if (kUseBakerReadBarrier) {
+ DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr())
+ << " " << to_ref << " " << to_ref->GetReadBarrierPointer()
+ << " is_marked=" << IsMarked(to_ref);
+ }
+ if (to_ref->GetClass<kVerifyNone, kWithoutReadBarrier>()->IsTypeOfReferenceClass() &&
+ to_ref->AsReference()->GetReferent<kWithoutReadBarrier>() != nullptr &&
+ !IsInToSpace(to_ref->AsReference()->GetReferent<kWithoutReadBarrier>())) {
+ // Leave References gray so that GetReferent() will trigger RB.
+ CHECK(to_ref->AsReference()->IsEnqueued()) << "Left unenqueued ref gray " << to_ref;
+ } else {
+ if (kUseBakerReadBarrier) {
+ if (region_space_->IsInToSpace(to_ref)) {
+ // If to-space, change from gray to white.
+ bool success = to_ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(),
+ ReadBarrier::WhitePtr());
+ CHECK(success) << "Must succeed as we won the race.";
+ CHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::WhitePtr());
+ } else {
+ // If non-moving space/unevac from space, change from gray
+ // to black. We can't change gray to white because it's not
+ // safe to use CAS if two threads change values in opposite
+ // directions (A->B and B->A). So, we change it to black to
+ // indicate non-moving objects that have been marked
+ // through. Note we'd need to change from black to white
+ // later (concurrently).
+ bool success = to_ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(),
+ ReadBarrier::BlackPtr());
+ CHECK(success) << "Must succeed as we won the race.";
+ CHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
+ }
+ }
+ }
+ if (ReadBarrier::kEnableToSpaceInvariantChecks || kIsDebugBuild) {
+ ConcurrentCopyingAssertToSpaceInvariantObjectVisitor visitor(this);
+ visitor(to_ref);
+ }
+ }
+ // Return true if the stack was empty.
+ return count == 0;
+}
+
+void ConcurrentCopying::CheckEmptyMarkQueue() {
+ if (!mark_queue_.IsEmpty()) {
+ while (!mark_queue_.IsEmpty()) {
+ mirror::Object* obj = mark_queue_.Dequeue();
+ if (kUseBakerReadBarrier) {
+ mirror::Object* rb_ptr = obj->GetReadBarrierPointer();
+ LOG(INFO) << "On mark queue : " << obj << " " << PrettyTypeOf(obj) << " rb_ptr=" << rb_ptr
+ << " is_marked=" << IsMarked(obj);
+ } else {
+ LOG(INFO) << "On mark queue : " << obj << " " << PrettyTypeOf(obj)
+ << " is_marked=" << IsMarked(obj);
+ }
+ }
+ LOG(FATAL) << "mark queue is not empty";
+ }
+}
+
+void ConcurrentCopying::SweepSystemWeaks(Thread* self) {
+ TimingLogger::ScopedTiming split("SweepSystemWeaks", GetTimings());
+ ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ Runtime::Current()->SweepSystemWeaks(IsMarkedCallback, this);
+}
+
+void ConcurrentCopying::Sweep(bool swap_bitmaps) {
+ {
+ TimingLogger::ScopedTiming t("MarkStackAsLive", GetTimings());
+ accounting::ObjectStack* live_stack = heap_->GetLiveStack();
+ if (kEnableFromSpaceAccountingCheck) {
+ CHECK_GE(live_stack_freeze_size_, live_stack->Size());
+ }
+ heap_->MarkAllocStackAsLive(live_stack);
+ live_stack->Reset();
+ }
+ CHECK(mark_queue_.IsEmpty());
+ TimingLogger::ScopedTiming split("Sweep", GetTimings());
+ for (const auto& space : GetHeap()->GetContinuousSpaces()) {
+ if (space->IsContinuousMemMapAllocSpace()) {
+ space::ContinuousMemMapAllocSpace* alloc_space = space->AsContinuousMemMapAllocSpace();
+ if (space == region_space_ || immune_region_.ContainsSpace(space)) {
+ continue;
+ }
+ TimingLogger::ScopedTiming split2(
+ alloc_space->IsZygoteSpace() ? "SweepZygoteSpace" : "SweepAllocSpace", GetTimings());
+ RecordFree(alloc_space->Sweep(swap_bitmaps));
+ }
+ }
+ SweepLargeObjects(swap_bitmaps);
+}
+
+void ConcurrentCopying::SweepLargeObjects(bool swap_bitmaps) {
+ TimingLogger::ScopedTiming split("SweepLargeObjects", GetTimings());
+ RecordFreeLOS(heap_->GetLargeObjectsSpace()->Sweep(swap_bitmaps));
+}
+
+class ConcurrentCopyingClearBlackPtrsVisitor {
+ public:
+ explicit ConcurrentCopyingClearBlackPtrsVisitor(ConcurrentCopying* cc)
+ : collector_(cc) {}
+ void operator()(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
+ DCHECK(obj != nullptr);
+ DCHECK(collector_->heap_->GetMarkBitmap()->Test(obj)) << obj;
+ DCHECK_EQ(obj->GetReadBarrierPointer(), ReadBarrier::BlackPtr()) << obj;
+ obj->SetReadBarrierPointer(ReadBarrier::WhitePtr());
+ DCHECK_EQ(obj->GetReadBarrierPointer(), ReadBarrier::WhitePtr()) << obj;
+ }
+
+ private:
+ ConcurrentCopying* const collector_;
+};
+
+// Clear the black ptrs in non-moving objects back to white.
+void ConcurrentCopying::ClearBlackPtrs() {
+ CHECK(kUseBakerReadBarrier);
+ TimingLogger::ScopedTiming split("ClearBlackPtrs", GetTimings());
+ ConcurrentCopyingClearBlackPtrsVisitor visitor(this);
+ for (auto& space : heap_->GetContinuousSpaces()) {
+ if (space == region_space_) {
+ continue;
+ }
+ accounting::ContinuousSpaceBitmap* mark_bitmap = space->GetMarkBitmap();
+ if (kVerboseMode) {
+ LOG(INFO) << "ClearBlackPtrs: " << *space << " bitmap: " << *mark_bitmap;
+ }
+ mark_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()),
+ reinterpret_cast<uintptr_t>(space->Limit()),
+ visitor);
+ }
+ space::LargeObjectSpace* large_object_space = heap_->GetLargeObjectsSpace();
+ large_object_space->GetMarkBitmap()->VisitMarkedRange(
+ reinterpret_cast<uintptr_t>(large_object_space->Begin()),
+ reinterpret_cast<uintptr_t>(large_object_space->End()),
+ visitor);
+ // Objects on the allocation stack?
+ if (ReadBarrier::kEnableReadBarrierInvariantChecks || kIsDebugBuild) {
+ size_t count = GetAllocationStack()->Size();
+ auto* it = GetAllocationStack()->Begin();
+ auto* end = GetAllocationStack()->End();
+ for (size_t i = 0; i < count; ++i, ++it) {
+ CHECK_LT(it, end);
+ mirror::Object* obj = it->AsMirrorPtr();
+ if (obj != nullptr) {
+ // Must have been cleared above.
+ CHECK_EQ(obj->GetReadBarrierPointer(), ReadBarrier::WhitePtr()) << obj;
+ }
+ }
+ }
+}
+
+void ConcurrentCopying::ReclaimPhase() {
+ TimingLogger::ScopedTiming split("ReclaimPhase", GetTimings());
+ if (kVerboseMode) {
+ LOG(INFO) << "GC ReclaimPhase";
+ }
+ Thread* self = Thread::Current();
+
+ {
+ // Double-check that the mark stack is empty.
+ // Note: need to set this after VerifyNoFromSpaceRef().
+ is_asserting_to_space_invariant_ = false;
+ QuasiAtomic::ThreadFenceForConstructor();
+ if (kVerboseMode) {
+ LOG(INFO) << "Issue an empty check point. ";
+ }
+ IssueEmptyCheckpoint();
+ // Disable the check.
+ is_mark_queue_push_disallowed_.StoreSequentiallyConsistent(0);
+ CheckEmptyMarkQueue();
+ }
+
+ {
+ // Record freed objects.
+ TimingLogger::ScopedTiming split2("RecordFree", GetTimings());
+ // Don't include thread-locals that are in the to-space.
+ uint64_t from_bytes = region_space_->GetBytesAllocatedInFromSpace();
+ uint64_t from_objects = region_space_->GetObjectsAllocatedInFromSpace();
+ uint64_t unevac_from_bytes = region_space_->GetBytesAllocatedInUnevacFromSpace();
+ uint64_t unevac_from_objects = region_space_->GetObjectsAllocatedInUnevacFromSpace();
+ uint64_t to_bytes = bytes_moved_.LoadSequentiallyConsistent();
+ uint64_t to_objects = objects_moved_.LoadSequentiallyConsistent();
+ if (kEnableFromSpaceAccountingCheck) {
+ CHECK_EQ(from_space_num_objects_at_first_pause_, from_objects + unevac_from_objects);
+ CHECK_EQ(from_space_num_bytes_at_first_pause_, from_bytes + unevac_from_bytes);
+ }
+ CHECK_LE(to_objects, from_objects);
+ CHECK_LE(to_bytes, from_bytes);
+ int64_t freed_bytes = from_bytes - to_bytes;
+ int64_t freed_objects = from_objects - to_objects;
+ if (kVerboseMode) {
+ LOG(INFO) << "RecordFree:"
+ << " from_bytes=" << from_bytes << " from_objects=" << from_objects
+ << " unevac_from_bytes=" << unevac_from_bytes << " unevac_from_objects=" << unevac_from_objects
+ << " to_bytes=" << to_bytes << " to_objects=" << to_objects
+ << " freed_bytes=" << freed_bytes << " freed_objects=" << freed_objects
+ << " from_space size=" << region_space_->FromSpaceSize()
+ << " unevac_from_space size=" << region_space_->UnevacFromSpaceSize()
+ << " to_space size=" << region_space_->ToSpaceSize();
+ LOG(INFO) << "(before) num_bytes_allocated=" << heap_->num_bytes_allocated_.LoadSequentiallyConsistent();
+ }
+ RecordFree(ObjectBytePair(freed_objects, freed_bytes));
+ if (kVerboseMode) {
+ LOG(INFO) << "(after) num_bytes_allocated=" << heap_->num_bytes_allocated_.LoadSequentiallyConsistent();
+ }
+ }
+
+ {
+ TimingLogger::ScopedTiming split3("ComputeUnevacFromSpaceLiveRatio", GetTimings());
+ ComputeUnevacFromSpaceLiveRatio();
+ }
+
+ {
+ TimingLogger::ScopedTiming split4("ClearFromSpace", GetTimings());
+ region_space_->ClearFromSpace();
+ }
+
+ {
+ WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ if (kUseBakerReadBarrier) {
+ ClearBlackPtrs();
+ }
+ Sweep(false);
+ SwapBitmaps();
+ heap_->UnBindBitmaps();
+
+ // Remove bitmaps for the immune spaces.
+ while (!cc_bitmaps_.empty()) {
+ accounting::ContinuousSpaceBitmap* cc_bitmap = cc_bitmaps_.back();
+ cc_heap_bitmap_->RemoveContinuousSpaceBitmap(cc_bitmap);
+ delete cc_bitmap;
+ cc_bitmaps_.pop_back();
+ }
+ region_space_bitmap_ = nullptr;
+ }
+
+ if (kVerboseMode) {
+ LOG(INFO) << "GC end of ReclaimPhase";
+ }
+}
+
+class ConcurrentCopyingComputeUnevacFromSpaceLiveRatioVisitor {
+ public:
+ explicit ConcurrentCopyingComputeUnevacFromSpaceLiveRatioVisitor(ConcurrentCopying* cc)
+ : collector_(cc) {}
+ void operator()(mirror::Object* ref) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
+ DCHECK(ref != nullptr);
+ DCHECK(collector_->region_space_bitmap_->Test(ref)) << ref;
+ DCHECK(collector_->region_space_->IsInUnevacFromSpace(ref)) << ref;
+ if (kUseBakerReadBarrier) {
+ DCHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::BlackPtr()) << ref;
+ // Clear the black ptr.
+ ref->SetReadBarrierPointer(ReadBarrier::WhitePtr());
+ }
+ size_t obj_size = ref->SizeOf();
+ size_t alloc_size = RoundUp(obj_size, space::RegionSpace::kAlignment);
+ collector_->region_space_->AddLiveBytes(ref, alloc_size);
+ }
+
+ private:
+ ConcurrentCopying* collector_;
+};
+
+// Compute how much live objects are left in regions.
+void ConcurrentCopying::ComputeUnevacFromSpaceLiveRatio() {
+ region_space_->AssertAllRegionLiveBytesZeroOrCleared();
+ ConcurrentCopyingComputeUnevacFromSpaceLiveRatioVisitor visitor(this);
+ region_space_bitmap_->VisitMarkedRange(reinterpret_cast<uintptr_t>(region_space_->Begin()),
+ reinterpret_cast<uintptr_t>(region_space_->Limit()),
+ visitor);
+}
+
+// Assert the to-space invariant.
+void ConcurrentCopying::AssertToSpaceInvariant(mirror::Object* obj, MemberOffset offset,
+ mirror::Object* ref) {
+ CHECK(heap_->collector_type_ == kCollectorTypeCC) << static_cast<size_t>(heap_->collector_type_);
+ if (is_asserting_to_space_invariant_) {
+ if (region_space_->IsInToSpace(ref)) {
+ // OK.
+ return;
+ } else if (region_space_->IsInUnevacFromSpace(ref)) {
+ CHECK(region_space_bitmap_->Test(ref)) << ref;
+ } else if (region_space_->IsInFromSpace(ref)) {
+ // Not OK. Do extra logging.
+ if (obj != nullptr) {
+ if (kUseBakerReadBarrier) {
+ LOG(INFO) << "holder=" << obj << " " << PrettyTypeOf(obj)
+ << " holder rb_ptr=" << obj->GetReadBarrierPointer();
+ } else {
+ LOG(INFO) << "holder=" << obj << " " << PrettyTypeOf(obj);
+ }
+ if (region_space_->IsInFromSpace(obj)) {
+ LOG(INFO) << "holder is in the from-space.";
+ } else if (region_space_->IsInToSpace(obj)) {
+ LOG(INFO) << "holder is in the to-space.";
+ } else if (region_space_->IsInUnevacFromSpace(obj)) {
+ LOG(INFO) << "holder is in the unevac from-space.";
+ if (region_space_bitmap_->Test(obj)) {
+ LOG(INFO) << "holder is marked in the region space bitmap.";
+ } else {
+ LOG(INFO) << "holder is not marked in the region space bitmap.";
+ }
+ } else {
+ // In a non-moving space.
+ if (immune_region_.ContainsObject(obj)) {
+ LOG(INFO) << "holder is in the image or the zygote space.";
+ accounting::ContinuousSpaceBitmap* cc_bitmap =
+ cc_heap_bitmap_->GetContinuousSpaceBitmap(obj);
+ CHECK(cc_bitmap != nullptr)
+ << "An immune space object must have a bitmap.";
+ if (cc_bitmap->Test(obj)) {
+ LOG(INFO) << "holder is marked in the bit map.";
+ } else {
+ LOG(INFO) << "holder is NOT marked in the bit map.";
+ }
+ } else {
+ LOG(INFO) << "holder is in a non-moving (or main) space.";
+ accounting::ContinuousSpaceBitmap* mark_bitmap =
+ heap_mark_bitmap_->GetContinuousSpaceBitmap(obj);
+ accounting::LargeObjectBitmap* los_bitmap =
+ heap_mark_bitmap_->GetLargeObjectBitmap(obj);
+ CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
+ bool is_los = mark_bitmap == nullptr;
+ if (!is_los && mark_bitmap->Test(obj)) {
+ LOG(INFO) << "holder is marked in the mark bit map.";
+ } else if (is_los && los_bitmap->Test(obj)) {
+ LOG(INFO) << "holder is marked in the los bit map.";
+ } else {
+ // If ref is on the allocation stack, then it is considered
+ // mark/alive (but not necessarily on the live stack.)
+ if (IsOnAllocStack(obj)) {
+ LOG(INFO) << "holder is on the alloc stack.";
+ } else {
+ LOG(INFO) << "holder is not marked or on the alloc stack.";
+ }
+ }
+ }
+ }
+ LOG(INFO) << "offset=" << offset.SizeValue();
+ }
+ CHECK(false) << "Found from-space ref " << ref << " " << PrettyTypeOf(ref);
+ } else {
+ // In a non-moving spaces. Check that the ref is marked.
+ if (immune_region_.ContainsObject(ref)) {
+ accounting::ContinuousSpaceBitmap* cc_bitmap =
+ cc_heap_bitmap_->GetContinuousSpaceBitmap(ref);
+ CHECK(cc_bitmap != nullptr)
+ << "An immune space ref must have a bitmap. " << ref;
+ if (kUseBakerReadBarrier) {
+ CHECK(cc_bitmap->Test(ref))
+ << "Unmarked immune space ref. obj=" << obj << " rb_ptr="
+ << obj->GetReadBarrierPointer() << " ref=" << ref;
+ } else {
+ CHECK(cc_bitmap->Test(ref))
+ << "Unmarked immune space ref. obj=" << obj << " ref=" << ref;
+ }
+ } else {
+ accounting::ContinuousSpaceBitmap* mark_bitmap =
+ heap_mark_bitmap_->GetContinuousSpaceBitmap(ref);
+ accounting::LargeObjectBitmap* los_bitmap =
+ heap_mark_bitmap_->GetLargeObjectBitmap(ref);
+ CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
+ bool is_los = mark_bitmap == nullptr;
+ if ((!is_los && mark_bitmap->Test(ref)) ||
+ (is_los && los_bitmap->Test(ref))) {
+ // OK.
+ } else {
+ // If ref is on the allocation stack, then it may not be
+ // marked live, but considered marked/alive (but not
+ // necessarily on the live stack).
+ CHECK(IsOnAllocStack(ref)) << "Unmarked ref that's not on the allocation stack. "
+ << "obj=" << obj << " ref=" << ref;
+ }
+ }
+ }
+ }
+}
+
+void ConcurrentCopying::ProcessRootCallback(mirror::Object** root, void* arg,
+ const RootInfo& /*root_info*/) {
+ reinterpret_cast<ConcurrentCopying*>(arg)->Process(root);
+}
+
+// Used to scan ref fields of an object.
+class ConcurrentCopyingRefFieldsVisitor {
+ public:
+ explicit ConcurrentCopyingRefFieldsVisitor(ConcurrentCopying* collector)
+ : collector_(collector) {}
+
+ void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */)
+ const ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
+ collector_->Process(obj, offset);
+ }
+
+ void operator()(mirror::Class* klass, mirror::Reference* ref) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+ CHECK(klass->IsTypeOfReferenceClass());
+ collector_->DelayReferenceReferent(klass, ref);
+ }
+
+ private:
+ ConcurrentCopying* const collector_;
+};
+
+// Scan ref fields of an object.
+void ConcurrentCopying::Scan(mirror::Object* to_ref) {
+ DCHECK(!region_space_->IsInFromSpace(to_ref));
+ ConcurrentCopyingRefFieldsVisitor visitor(this);
+ to_ref->VisitReferences<true>(visitor, visitor);
+}
+
+// Process a field.
+inline void ConcurrentCopying::Process(mirror::Object* obj, MemberOffset offset) {
+ mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier, false>(offset);
+ if (ref == nullptr || region_space_->IsInToSpace(ref)) {
+ return;
+ }
+ mirror::Object* to_ref = Mark(ref);
+ if (to_ref == ref) {
+ return;
+ }
+ // This may fail if the mutator writes to the field at the same time. But it's ok.
+ mirror::Object* expected_ref = ref;
+ mirror::Object* new_ref = to_ref;
+ do {
+ if (expected_ref !=
+ obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier, false>(offset)) {
+ // It was updated by the mutator.
+ break;
+ }
+ } while (!obj->CasFieldWeakSequentiallyConsistentObjectWithoutWriteBarrier<false, false, kVerifyNone>(
+ offset, expected_ref, new_ref));
+}
+
+// Process a root.
+void ConcurrentCopying::Process(mirror::Object** root) {
+ mirror::Object* ref = *root;
+ if (ref == nullptr || region_space_->IsInToSpace(ref)) {
+ return;
+ }
+ mirror::Object* to_ref = Mark(ref);
+ if (to_ref == ref) {
+ return;
+ }
+ Atomic<mirror::Object*>* addr = reinterpret_cast<Atomic<mirror::Object*>*>(root);
+ mirror::Object* expected_ref = ref;
+ mirror::Object* new_ref = to_ref;
+ do {
+ if (expected_ref != addr->LoadRelaxed()) {
+ // It was updated by the mutator.
+ break;
+ }
+ } while (!addr->CompareExchangeWeakSequentiallyConsistent(expected_ref, new_ref));
+}
+
+// Fill the given memory block with a dummy object. Used to fill in a
+// copy of objects that was lost in race.
+void ConcurrentCopying::FillWithDummyObject(mirror::Object* dummy_obj, size_t byte_size) {
+ CHECK(IsAligned<kObjectAlignment>(byte_size));
+ memset(dummy_obj, 0, byte_size);
+ mirror::Class* int_array_class = mirror::IntArray::GetArrayClass();
+ CHECK(int_array_class != nullptr);
+ AssertToSpaceInvariant(nullptr, MemberOffset(0), int_array_class);
+ size_t component_size = int_array_class->GetComponentSize();
+ CHECK_EQ(component_size, sizeof(int32_t));
+ size_t data_offset = mirror::Array::DataOffset(component_size).SizeValue();
+ if (data_offset > byte_size) {
+ // An int array is too big. Use java.lang.Object.
+ mirror::Class* java_lang_Object = WellKnownClasses::ToClass(WellKnownClasses::java_lang_Object);
+ AssertToSpaceInvariant(nullptr, MemberOffset(0), java_lang_Object);
+ CHECK_EQ(byte_size, java_lang_Object->GetObjectSize());
+ dummy_obj->SetClass(java_lang_Object);
+ CHECK_EQ(byte_size, dummy_obj->SizeOf());
+ } else {
+ // Use an int array.
+ dummy_obj->SetClass(int_array_class);
+ CHECK(dummy_obj->IsArrayInstance());
+ int32_t length = (byte_size - data_offset) / component_size;
+ dummy_obj->AsArray()->SetLength(length);
+ CHECK_EQ(dummy_obj->AsArray()->GetLength(), length)
+ << "byte_size=" << byte_size << " length=" << length
+ << " component_size=" << component_size << " data_offset=" << data_offset;
+ CHECK_EQ(byte_size, dummy_obj->SizeOf())
+ << "byte_size=" << byte_size << " length=" << length
+ << " component_size=" << component_size << " data_offset=" << data_offset;
+ }
+}
+
+// Reuse the memory blocks that were copy of objects that were lost in race.
+mirror::Object* ConcurrentCopying::AllocateInSkippedBlock(size_t alloc_size) {
+ // Try to reuse the blocks that were unused due to CAS failures.
+ CHECK(IsAligned<space::RegionSpace::kAlignment>(alloc_size));
+ Thread* self = Thread::Current();
+ size_t min_object_size = RoundUp(sizeof(mirror::Object), space::RegionSpace::kAlignment);
+ MutexLock mu(self, skipped_blocks_lock_);
+ auto it = skipped_blocks_map_.lower_bound(alloc_size);
+ if (it == skipped_blocks_map_.end()) {
+ // Not found.
+ return nullptr;
+ }
+ {
+ size_t byte_size = it->first;
+ CHECK_GE(byte_size, alloc_size);
+ if (byte_size > alloc_size && byte_size - alloc_size < min_object_size) {
+ // If remainder would be too small for a dummy object, retry with a larger request size.
+ it = skipped_blocks_map_.lower_bound(alloc_size + min_object_size);
+ if (it == skipped_blocks_map_.end()) {
+ // Not found.
+ return nullptr;
+ }
+ CHECK(IsAligned<space::RegionSpace::kAlignment>(it->first - alloc_size));
+ CHECK_GE(it->first - alloc_size, min_object_size)
+ << "byte_size=" << byte_size << " it->first=" << it->first << " alloc_size=" << alloc_size;
+ }
+ }
+ // Found a block.
+ CHECK(it != skipped_blocks_map_.end());
+ size_t byte_size = it->first;
+ uint8_t* addr = it->second;
+ CHECK_GE(byte_size, alloc_size);
+ CHECK(region_space_->IsInToSpace(reinterpret_cast<mirror::Object*>(addr)));
+ CHECK(IsAligned<space::RegionSpace::kAlignment>(byte_size));
+ if (kVerboseMode) {
+ LOG(INFO) << "Reusing skipped bytes : " << reinterpret_cast<void*>(addr) << ", " << byte_size;
+ }
+ skipped_blocks_map_.erase(it);
+ memset(addr, 0, byte_size);
+ if (byte_size > alloc_size) {
+ // Return the remainder to the map.
+ CHECK(IsAligned<space::RegionSpace::kAlignment>(byte_size - alloc_size));
+ CHECK_GE(byte_size - alloc_size, min_object_size);
+ FillWithDummyObject(reinterpret_cast<mirror::Object*>(addr + alloc_size),
+ byte_size - alloc_size);
+ CHECK(region_space_->IsInToSpace(reinterpret_cast<mirror::Object*>(addr + alloc_size)));
+ skipped_blocks_map_.insert(std::make_pair(byte_size - alloc_size, addr + alloc_size));
+ }
+ return reinterpret_cast<mirror::Object*>(addr);
+}
+
+mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref) {
+ DCHECK(region_space_->IsInFromSpace(from_ref));
+ // No read barrier to avoid nested RB that might violate the to-space
+ // invariant. Note that from_ref is a from space ref so the SizeOf()
+ // call will access the from-space meta objects, but it's ok and necessary.
+ size_t obj_size = from_ref->SizeOf<kDefaultVerifyFlags, kWithoutReadBarrier>();
+ size_t region_space_alloc_size = RoundUp(obj_size, space::RegionSpace::kAlignment);
+ size_t region_space_bytes_allocated = 0U;
+ size_t non_moving_space_bytes_allocated = 0U;
+ size_t bytes_allocated = 0U;
+ mirror::Object* to_ref = region_space_->AllocNonvirtual<true>(
+ region_space_alloc_size, ®ion_space_bytes_allocated, nullptr);
+ bytes_allocated = region_space_bytes_allocated;
+ if (to_ref != nullptr) {
+ DCHECK_EQ(region_space_alloc_size, region_space_bytes_allocated);
+ }
+ bool fall_back_to_non_moving = false;
+ if (UNLIKELY(to_ref == nullptr)) {
+ // Failed to allocate in the region space. Try the skipped blocks.
+ to_ref = AllocateInSkippedBlock(region_space_alloc_size);
+ if (to_ref != nullptr) {
+ // Succeeded to allocate in a skipped block.
+ if (heap_->use_tlab_) {
+ // This is necessary for the tlab case as it's not accounted in the space.
+ region_space_->RecordAlloc(to_ref);
+ }
+ bytes_allocated = region_space_alloc_size;
+ } else {
+ // Fall back to the non-moving space.
+ fall_back_to_non_moving = true;
+ if (kVerboseMode) {
+ LOG(INFO) << "Out of memory in the to-space. Fall back to non-moving. skipped_bytes="
+ << to_space_bytes_skipped_.LoadSequentiallyConsistent()
+ << " skipped_objects=" << to_space_objects_skipped_.LoadSequentiallyConsistent();
+ }
+ fall_back_to_non_moving = true;
+ to_ref = heap_->non_moving_space_->Alloc(Thread::Current(), obj_size,
+ &non_moving_space_bytes_allocated, nullptr);
+ CHECK(to_ref != nullptr) << "Fall-back non-moving space allocation failed";
+ bytes_allocated = non_moving_space_bytes_allocated;
+ // Mark it in the mark bitmap.
+ accounting::ContinuousSpaceBitmap* mark_bitmap =
+ heap_mark_bitmap_->GetContinuousSpaceBitmap(to_ref);
+ CHECK(mark_bitmap != nullptr);
+ CHECK(!mark_bitmap->AtomicTestAndSet(to_ref));
+ }
+ }
+ DCHECK(to_ref != nullptr);
+
+ // Attempt to install the forward pointer. This is in a loop as the
+ // lock word atomic write can fail.
+ while (true) {
+ // Copy the object. TODO: copy only the lockword in the second iteration and on?
+ memcpy(to_ref, from_ref, obj_size);
+ // Set the gray ptr.
+ if (kUseBakerReadBarrier) {
+ to_ref->SetReadBarrierPointer(ReadBarrier::GrayPtr());
+ }
+
+ LockWord old_lock_word = to_ref->GetLockWord(false);
+
+ if (old_lock_word.GetState() == LockWord::kForwardingAddress) {
+ // Lost the race. Another thread (either GC or mutator) stored
+ // the forwarding pointer first. Make the lost copy (to_ref)
+ // look like a valid but dead (dummy) object and keep it for
+ // future reuse.
+ FillWithDummyObject(to_ref, bytes_allocated);
+ if (!fall_back_to_non_moving) {
+ DCHECK(region_space_->IsInToSpace(to_ref));
+ if (bytes_allocated > space::RegionSpace::kRegionSize) {
+ // Free the large alloc.
+ region_space_->FreeLarge(to_ref, bytes_allocated);
+ } else {
+ // Record the lost copy for later reuse.
+ heap_->num_bytes_allocated_.FetchAndAddSequentiallyConsistent(bytes_allocated);
+ to_space_bytes_skipped_.FetchAndAddSequentiallyConsistent(bytes_allocated);
+ to_space_objects_skipped_.FetchAndAddSequentiallyConsistent(1);
+ MutexLock mu(Thread::Current(), skipped_blocks_lock_);
+ skipped_blocks_map_.insert(std::make_pair(bytes_allocated,
+ reinterpret_cast<uint8_t*>(to_ref)));
+ }
+ } else {
+ DCHECK(heap_->non_moving_space_->HasAddress(to_ref));
+ DCHECK_EQ(bytes_allocated, non_moving_space_bytes_allocated);
+ // Free the non-moving-space chunk.
+ accounting::ContinuousSpaceBitmap* mark_bitmap =
+ heap_mark_bitmap_->GetContinuousSpaceBitmap(to_ref);
+ CHECK(mark_bitmap != nullptr);
+ CHECK(mark_bitmap->Clear(to_ref));
+ heap_->non_moving_space_->Free(Thread::Current(), to_ref);
+ }
+
+ // Get the winner's forward ptr.
+ mirror::Object* lost_fwd_ptr = to_ref;
+ to_ref = reinterpret_cast<mirror::Object*>(old_lock_word.ForwardingAddress());
+ CHECK(to_ref != nullptr);
+ CHECK_NE(to_ref, lost_fwd_ptr);
+ CHECK(region_space_->IsInToSpace(to_ref) || heap_->non_moving_space_->HasAddress(to_ref));
+ CHECK_NE(to_ref->GetLockWord(false).GetState(), LockWord::kForwardingAddress);
+ return to_ref;
+ }
+
+ LockWord new_lock_word = LockWord::FromForwardingAddress(reinterpret_cast<size_t>(to_ref));
+
+ // Try to atomically write the fwd ptr.
+ bool success = from_ref->CasLockWordWeakSequentiallyConsistent(old_lock_word, new_lock_word);
+ if (LIKELY(success)) {
+ // The CAS succeeded.
+ objects_moved_.FetchAndAddSequentiallyConsistent(1);
+ bytes_moved_.FetchAndAddSequentiallyConsistent(region_space_alloc_size);
+ if (LIKELY(!fall_back_to_non_moving)) {
+ DCHECK(region_space_->IsInToSpace(to_ref));
+ } else {
+ DCHECK(heap_->non_moving_space_->HasAddress(to_ref));
+ DCHECK_EQ(bytes_allocated, non_moving_space_bytes_allocated);
+ }
+ if (kUseBakerReadBarrier) {
+ DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr());
+ }
+ DCHECK(GetFwdPtr(from_ref) == to_ref);
+ CHECK_NE(to_ref->GetLockWord(false).GetState(), LockWord::kForwardingAddress);
+ PushOntoMarkStack<true>(to_ref);
+ return to_ref;
+ } else {
+ // The CAS failed. It may have lost the race or may have failed
+ // due to monitor/hashcode ops. Either way, retry.
+ }
+ }
+}
+
+mirror::Object* ConcurrentCopying::IsMarked(mirror::Object* from_ref) {
+ DCHECK(from_ref != nullptr);
+ space::RegionSpace::RegionType rtype = region_space_->GetRegionType(from_ref);
+ if (rtype == space::RegionSpace::RegionType::kRegionTypeToSpace) {
+ // It's already marked.
+ return from_ref;
+ }
+ mirror::Object* to_ref;
+ if (rtype == space::RegionSpace::RegionType::kRegionTypeFromSpace) {
+ to_ref = GetFwdPtr(from_ref);
+ DCHECK(to_ref == nullptr || region_space_->IsInToSpace(to_ref) ||
+ heap_->non_moving_space_->HasAddress(to_ref))
+ << "from_ref=" << from_ref << " to_ref=" << to_ref;
+ } else if (rtype == space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace) {
+ if (region_space_bitmap_->Test(from_ref)) {
+ to_ref = from_ref;
+ } else {
+ to_ref = nullptr;
+ }
+ } else {
+ // from_ref is in a non-moving space.
+ if (immune_region_.ContainsObject(from_ref)) {
+ accounting::ContinuousSpaceBitmap* cc_bitmap =
+ cc_heap_bitmap_->GetContinuousSpaceBitmap(from_ref);
+ DCHECK(cc_bitmap != nullptr)
+ << "An immune space object must have a bitmap";
+ if (kIsDebugBuild) {
+ DCHECK(heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref)->Test(from_ref))
+ << "Immune space object must be already marked";
+ }
+ if (cc_bitmap->Test(from_ref)) {
+ // Already marked.
+ to_ref = from_ref;
+ } else {
+ // Newly marked.
+ to_ref = nullptr;
+ }
+ } else {
+ // Non-immune non-moving space. Use the mark bitmap.
+ accounting::ContinuousSpaceBitmap* mark_bitmap =
+ heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref);
+ accounting::LargeObjectBitmap* los_bitmap =
+ heap_mark_bitmap_->GetLargeObjectBitmap(from_ref);
+ CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
+ bool is_los = mark_bitmap == nullptr;
+ if (!is_los && mark_bitmap->Test(from_ref)) {
+ // Already marked.
+ to_ref = from_ref;
+ } else if (is_los && los_bitmap->Test(from_ref)) {
+ // Already marked in LOS.
+ to_ref = from_ref;
+ } else {
+ // Not marked.
+ if (IsOnAllocStack(from_ref)) {
+ // If on the allocation stack, it's considered marked.
+ to_ref = from_ref;
+ } else {
+ // Not marked.
+ to_ref = nullptr;
+ }
+ }
+ }
+ }
+ return to_ref;
+}
+
+bool ConcurrentCopying::IsOnAllocStack(mirror::Object* ref) {
+ QuasiAtomic::ThreadFenceAcquire();
+ accounting::ObjectStack* alloc_stack = GetAllocationStack();
+ return alloc_stack->Contains(ref);
+}
+
+mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref) {
+ if (from_ref == nullptr) {
+ return nullptr;
+ }
+ DCHECK(from_ref != nullptr);
+ DCHECK(heap_->collector_type_ == kCollectorTypeCC);
+ space::RegionSpace::RegionType rtype = region_space_->GetRegionType(from_ref);
+ if (rtype == space::RegionSpace::RegionType::kRegionTypeToSpace) {
+ // It's already marked.
+ return from_ref;
+ }
+ mirror::Object* to_ref;
+ if (rtype == space::RegionSpace::RegionType::kRegionTypeFromSpace) {
+ to_ref = GetFwdPtr(from_ref);
+ if (kUseBakerReadBarrier) {
+ DCHECK(to_ref != ReadBarrier::GrayPtr()) << "from_ref=" << from_ref << " to_ref=" << to_ref;
+ }
+ if (to_ref == nullptr) {
+ // It isn't marked yet. Mark it by copying it to the to-space.
+ to_ref = Copy(from_ref);
+ }
+ DCHECK(region_space_->IsInToSpace(to_ref) || heap_->non_moving_space_->HasAddress(to_ref))
+ << "from_ref=" << from_ref << " to_ref=" << to_ref;
+ } else if (rtype == space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace) {
+ // This may or may not succeed, which is ok.
+ if (kUseBakerReadBarrier) {
+ from_ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
+ }
+ if (region_space_bitmap_->AtomicTestAndSet(from_ref)) {
+ // Already marked.
+ to_ref = from_ref;
+ } else {
+ // Newly marked.
+ to_ref = from_ref;
+ if (kUseBakerReadBarrier) {
+ DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr());
+ }
+ PushOntoMarkStack<true>(to_ref);
+ }
+ } else {
+ // from_ref is in a non-moving space.
+ DCHECK(!region_space_->HasAddress(from_ref)) << from_ref;
+ if (immune_region_.ContainsObject(from_ref)) {
+ accounting::ContinuousSpaceBitmap* cc_bitmap =
+ cc_heap_bitmap_->GetContinuousSpaceBitmap(from_ref);
+ DCHECK(cc_bitmap != nullptr)
+ << "An immune space object must have a bitmap";
+ if (kIsDebugBuild) {
+ DCHECK(heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref)->Test(from_ref))
+ << "Immune space object must be already marked";
+ }
+ // This may or may not succeed, which is ok.
+ if (kUseBakerReadBarrier) {
+ from_ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
+ }
+ if (cc_bitmap->AtomicTestAndSet(from_ref)) {
+ // Already marked.
+ to_ref = from_ref;
+ } else {
+ // Newly marked.
+ to_ref = from_ref;
+ if (kUseBakerReadBarrier) {
+ DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr());
+ }
+ PushOntoMarkStack<true>(to_ref);
+ }
+ } else {
+ // Use the mark bitmap.
+ accounting::ContinuousSpaceBitmap* mark_bitmap =
+ heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref);
+ accounting::LargeObjectBitmap* los_bitmap =
+ heap_mark_bitmap_->GetLargeObjectBitmap(from_ref);
+ CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
+ bool is_los = mark_bitmap == nullptr;
+ if (!is_los && mark_bitmap->Test(from_ref)) {
+ // Already marked.
+ to_ref = from_ref;
+ if (kUseBakerReadBarrier) {
+ DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr() ||
+ to_ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
+ }
+ } else if (is_los && los_bitmap->Test(from_ref)) {
+ // Already marked in LOS.
+ to_ref = from_ref;
+ if (kUseBakerReadBarrier) {
+ DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr() ||
+ to_ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
+ }
+ } else {
+ // Not marked.
+ if (IsOnAllocStack(from_ref)) {
+ // If it's on the allocation stack, it's considered marked. Keep it white.
+ to_ref = from_ref;
+ // Objects on the allocation stack need not be marked.
+ if (!is_los) {
+ DCHECK(!mark_bitmap->Test(to_ref));
+ } else {
+ DCHECK(!los_bitmap->Test(to_ref));
+ }
+ if (kUseBakerReadBarrier) {
+ DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::WhitePtr());
+ }
+ } else {
+ // Not marked or on the allocation stack. Try to mark it.
+ // This may or may not succeed, which is ok.
+ if (kUseBakerReadBarrier) {
+ from_ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
+ }
+ if (!is_los && mark_bitmap->AtomicTestAndSet(from_ref)) {
+ // Already marked.
+ to_ref = from_ref;
+ } else if (is_los && los_bitmap->AtomicTestAndSet(from_ref)) {
+ // Already marked in LOS.
+ to_ref = from_ref;
+ } else {
+ // Newly marked.
+ to_ref = from_ref;
+ if (kUseBakerReadBarrier) {
+ DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr());
+ }
+ PushOntoMarkStack<true>(to_ref);
+ }
+ }
+ }
+ }
+ }
+ return to_ref;
+}
+
+void ConcurrentCopying::FinishPhase() {
+ region_space_ = nullptr;
+ CHECK(mark_queue_.IsEmpty());
+ mark_queue_.Clear();
+ {
+ MutexLock mu(Thread::Current(), skipped_blocks_lock_);
+ skipped_blocks_map_.clear();
+ }
+ WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
+ heap_->ClearMarkedObjects();
+}
+
+mirror::Object* ConcurrentCopying::IsMarkedCallback(mirror::Object* from_ref, void* arg) {
+ return reinterpret_cast<ConcurrentCopying*>(arg)->IsMarked(from_ref);
+}
+
+bool ConcurrentCopying::IsHeapReferenceMarkedCallback(
+ mirror::HeapReference<mirror::Object>* field, void* arg) {
+ mirror::Object* from_ref = field->AsMirrorPtr();
+ mirror::Object* to_ref = reinterpret_cast<ConcurrentCopying*>(arg)->IsMarked(from_ref);
+ if (to_ref == nullptr) {
+ return false;
+ }
+ if (from_ref != to_ref) {
+ QuasiAtomic::ThreadFenceRelease();
+ field->Assign(to_ref);
+ QuasiAtomic::ThreadFenceSequentiallyConsistent();
+ }
+ return true;
+}
+
+mirror::Object* ConcurrentCopying::MarkCallback(mirror::Object* from_ref, void* arg) {
+ return reinterpret_cast<ConcurrentCopying*>(arg)->Mark(from_ref);
+}
+
+void ConcurrentCopying::ProcessMarkStackCallback(void* arg) {
+ reinterpret_cast<ConcurrentCopying*>(arg)->ProcessMarkStack();
+}
+
+void ConcurrentCopying::DelayReferenceReferent(mirror::Class* klass, mirror::Reference* reference) {
+ heap_->GetReferenceProcessor()->DelayReferenceReferent(
+ klass, reference, &IsHeapReferenceMarkedCallback, this);
+}
+
+void ConcurrentCopying::ProcessReferences(Thread* self, bool concurrent) {
+ TimingLogger::ScopedTiming split("ProcessReferences", GetTimings());
+ WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ GetHeap()->GetReferenceProcessor()->ProcessReferences(
+ concurrent, GetTimings(), GetCurrentIteration()->GetClearSoftReferences(),
+ &IsHeapReferenceMarkedCallback, &MarkCallback, &ProcessMarkStackCallback, this);
+}
+
+void ConcurrentCopying::RevokeAllThreadLocalBuffers() {
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ region_space_->RevokeAllThreadLocalBuffers();
+}
+
} // namespace collector
} // namespace gc
} // namespace art
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index ee5a785..d0e0446 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -17,34 +17,268 @@
#ifndef ART_RUNTIME_GC_COLLECTOR_CONCURRENT_COPYING_H_
#define ART_RUNTIME_GC_COLLECTOR_CONCURRENT_COPYING_H_
+#include "barrier.h"
#include "garbage_collector.h"
+#include "immune_region.h"
+#include "jni.h"
+#include "object_callbacks.h"
+#include "offsets.h"
+#include "gc/accounting/atomic_stack.h"
+#include "gc/accounting/read_barrier_table.h"
+#include "gc/accounting/space_bitmap.h"
+#include "mirror/object.h"
+#include "mirror/object_reference.h"
+#include "safe_map.h"
+
+#include <unordered_map>
+#include <vector>
namespace art {
+class RootInfo;
+
namespace gc {
+
+namespace accounting {
+ typedef SpaceBitmap<kObjectAlignment> ContinuousSpaceBitmap;
+ class HeapBitmap;
+} // namespace accounting
+
+namespace space {
+ class RegionSpace;
+} // namespace space
+
namespace collector {
+// Concurrent queue. Used as the mark stack. TODO: use a concurrent
+// stack for locality.
+class MarkQueue {
+ public:
+ explicit MarkQueue(size_t size) : size_(size) {
+ CHECK(IsPowerOfTwo(size_));
+ buf_.reset(new Atomic<mirror::Object*>[size_]);
+ CHECK(buf_.get() != nullptr);
+ Clear();
+ }
+
+ ALWAYS_INLINE Atomic<mirror::Object*>* GetSlotAddr(size_t index) {
+ return &(buf_.get()[index & (size_ - 1)]);
+ }
+
+ // Multiple-proceducer enqueue.
+ bool Enqueue(mirror::Object* to_ref) {
+ size_t t;
+ do {
+ t = tail_.LoadRelaxed();
+ size_t h = head_.LoadSequentiallyConsistent();
+ if (t + size_ == h) {
+ // It's full.
+ return false;
+ }
+ } while (!tail_.CompareExchangeWeakSequentiallyConsistent(t, t + 1));
+ // We got a slot but its content has not been filled yet at this point.
+ GetSlotAddr(t)->StoreSequentiallyConsistent(to_ref);
+ return true;
+ }
+
+ // Thread-unsafe.
+ bool EnqueueThreadUnsafe(mirror::Object* to_ref) {
+ size_t t = tail_.LoadRelaxed();
+ size_t h = head_.LoadRelaxed();
+ if (t + size_ == h) {
+ // It's full.
+ return false;
+ }
+ GetSlotAddr(t)->StoreRelaxed(to_ref);
+ tail_.StoreRelaxed(t + 1);
+ return true;
+ }
+
+ // Single-consumer dequeue.
+ mirror::Object* Dequeue() {
+ size_t h = head_.LoadRelaxed();
+ size_t t = tail_.LoadSequentiallyConsistent();
+ if (h == t) {
+ // it's empty.
+ return nullptr;
+ }
+ Atomic<mirror::Object*>* slot = GetSlotAddr(h);
+ mirror::Object* ref = slot->LoadSequentiallyConsistent();
+ while (ref == nullptr) {
+ // Wait until the slot content becomes visible.
+ ref = slot->LoadSequentiallyConsistent();
+ }
+ slot->StoreRelaxed(nullptr);
+ head_.StoreSequentiallyConsistent(h + 1);
+ return ref;
+ }
+
+ bool IsEmpty() {
+ size_t h = head_.LoadSequentiallyConsistent();
+ size_t t = tail_.LoadSequentiallyConsistent();
+ return h == t;
+ }
+
+ void Clear() {
+ head_.StoreRelaxed(0);
+ tail_.StoreRelaxed(0);
+ memset(buf_.get(), 0, size_ * sizeof(Atomic<mirror::Object*>));
+ }
+
+ private:
+ Atomic<size_t> head_;
+ Atomic<size_t> tail_;
+
+ size_t size_;
+ std::unique_ptr<Atomic<mirror::Object*>[]> buf_;
+};
+
class ConcurrentCopying : public GarbageCollector {
public:
- explicit ConcurrentCopying(Heap* heap, bool generational = false,
- const std::string& name_prefix = "")
- : GarbageCollector(heap,
- name_prefix + (name_prefix.empty() ? "" : " ") +
- "concurrent copying + mark sweep") {
- UNUSED(generational);
- }
+ // TODO: disable thse flags for production use.
+ // Enable the no-from-space-refs verification at the pause.
+ static constexpr bool kEnableNoFromSpaceRefsVerification = true;
+ // Enable the from-space bytes/objects check.
+ static constexpr bool kEnableFromSpaceAccountingCheck = true;
+ // Enable verbose mode.
+ static constexpr bool kVerboseMode = true;
- ~ConcurrentCopying() {}
+ ConcurrentCopying(Heap* heap, const std::string& name_prefix = "");
+ ~ConcurrentCopying();
- virtual void RunPhases() OVERRIDE {}
+ virtual void RunPhases() OVERRIDE;
+ void InitializePhase() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void MarkingPhase() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void ReclaimPhase() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void FinishPhase();
+
+ void BindBitmaps() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
virtual GcType GetGcType() const OVERRIDE {
return kGcTypePartial;
}
virtual CollectorType GetCollectorType() const OVERRIDE {
return kCollectorTypeCC;
}
- virtual void RevokeAllThreadLocalBuffers() OVERRIDE {}
+ virtual void RevokeAllThreadLocalBuffers() OVERRIDE;
+ void SetRegionSpace(space::RegionSpace* region_space) {
+ DCHECK(region_space != nullptr);
+ region_space_ = region_space;
+ }
+ space::RegionSpace* RegionSpace() {
+ return region_space_;
+ }
+ void AssertToSpaceInvariant(mirror::Object* obj, MemberOffset offset, mirror::Object* ref)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsInToSpace(mirror::Object* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(ref != nullptr);
+ return IsMarked(ref) == ref;
+ }
+ mirror::Object* Mark(mirror::Object* from_ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsMarking() const {
+ return is_marking_;
+ }
+ bool IsActive() const {
+ return is_active_;
+ }
+ Barrier& GetBarrier() {
+ return *gc_barrier_;
+ }
private:
+ mirror::Object* PopOffMarkStack();
+ template<bool kThreadSafe>
+ void PushOntoMarkStack(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::Object* Copy(mirror::Object* from_ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void Scan(mirror::Object* to_ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void Process(mirror::Object* obj, MemberOffset offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void Process(mirror::Object** root) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static void ProcessRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void VerifyNoFromSpaceReferences() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+ accounting::ObjectStack* GetAllocationStack();
+ accounting::ObjectStack* GetLiveStack();
+ bool ProcessMarkStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void DelayReferenceReferent(mirror::Class* klass, mirror::Reference* reference)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void ProcessReferences(Thread* self, bool concurrent)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::Object* IsMarked(mirror::Object* from_ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static mirror::Object* MarkCallback(mirror::Object* from_ref, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static mirror::Object* IsMarkedCallback(mirror::Object* from_ref, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static bool IsHeapReferenceMarkedCallback(
+ mirror::HeapReference<mirror::Object>* field, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static void ProcessMarkStackCallback(void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SweepSystemWeaks(Thread* self)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
+ void Sweep(bool swap_bitmaps)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+ void SweepLargeObjects(bool swap_bitmaps)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+ void ClearBlackPtrs()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+ void FillWithDummyObject(mirror::Object* dummy_obj, size_t byte_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::Object* AllocateInSkippedBlock(size_t alloc_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void CheckEmptyMarkQueue() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void IssueEmptyCheckpoint() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsOnAllocStack(mirror::Object* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::Object* GetFwdPtr(mirror::Object* from_ref)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SetFwdPtr(mirror::Object* from_ref, mirror::Object* to_ref)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void FlipThreadRoots() LOCKS_EXCLUDED(Locks::mutator_lock_);;
+ void SwapStacks(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void RecordLiveStackFreezeSize(Thread* self);
+ void ComputeUnevacFromSpaceLiveRatio();
+
+ space::RegionSpace* region_space_; // The underlying region space.
+ std::unique_ptr<Barrier> gc_barrier_;
+ MarkQueue mark_queue_;
+ bool is_marking_; // True while marking is ongoing.
+ bool is_active_; // True while the collection is ongoing.
+ bool is_asserting_to_space_invariant_; // True while asserting the to-space invariant.
+ ImmuneRegion immune_region_;
+ std::unique_ptr<accounting::HeapBitmap> cc_heap_bitmap_;
+ std::vector<accounting::SpaceBitmap<kObjectAlignment>*> cc_bitmaps_;
+ accounting::SpaceBitmap<kObjectAlignment>* region_space_bitmap_;
+ // A cache of Heap::GetMarkBitmap().
+ accounting::HeapBitmap* heap_mark_bitmap_;
+ size_t live_stack_freeze_size_;
+ size_t from_space_num_objects_at_first_pause_;
+ size_t from_space_num_bytes_at_first_pause_;
+ Atomic<int> is_mark_queue_push_disallowed_;
+
+ // How many objects and bytes we moved. Used for accounting.
+ Atomic<size_t> bytes_moved_;
+ Atomic<size_t> objects_moved_;
+
+ // The skipped blocks are memory blocks/chucks that were copies of
+ // objects that were unused due to lost races (cas failures) at
+ // object copy/forward pointer install. They are reused.
+ Mutex skipped_blocks_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ std::multimap<size_t, uint8_t*> skipped_blocks_map_ GUARDED_BY(skipped_blocks_lock_);
+ Atomic<size_t> to_space_bytes_skipped_;
+ Atomic<size_t> to_space_objects_skipped_;
+
+ accounting::ReadBarrierTable* rb_table_;
+ bool force_evacuate_all_; // True if all regions are evacuated.
+
+ friend class ConcurrentCopyingRefFieldsVisitor;
+ friend class ConcurrentCopyingImmuneSpaceObjVisitor;
+ friend class ConcurrentCopyingVerifyNoFromSpaceRefsVisitor;
+ friend class ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor;
+ friend class ConcurrentCopyingClearBlackPtrsVisitor;
+ friend class ConcurrentCopyingLostCopyVisitor;
+ friend class ThreadFlipVisitor;
+ friend class FlipCallback;
+ friend class ConcurrentCopyingComputeUnevacFromSpaceLiveRatioVisitor;
+
DISALLOW_COPY_AND_ASSIGN(ConcurrentCopying);
};
diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc
index 9e6a800..8be18be 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -102,7 +102,7 @@
total_time_ns_ += current_iteration->GetDurationNs();
for (uint64_t pause_time : current_iteration->GetPauseTimes()) {
MutexLock mu(self, pause_histogram_lock_);
- pause_histogram_.AddValue(pause_time / 1000);
+ pause_histogram_.AdjustAndAddValue(pause_time);
}
ATRACE_END();
}
diff --git a/runtime/gc/collector/immune_region.h b/runtime/gc/collector/immune_region.h
index 277525e..30144f0 100644
--- a/runtime/gc/collector/immune_region.h
+++ b/runtime/gc/collector/immune_region.h
@@ -57,6 +57,13 @@
UpdateSize();
}
+ mirror::Object* Begin() {
+ return begin_;
+ }
+ mirror::Object* End() {
+ return end_;
+ }
+
private:
bool IsEmpty() const {
return size_ == 0;
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index b2482ac..234bce5 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -197,7 +197,7 @@
BindBitmaps();
t.NewTiming("ProcessCards");
// Process dirty cards and add dirty cards to mod-union tables.
- heap_->ProcessCards(GetTimings(), false);
+ heap_->ProcessCards(GetTimings(), false, false, true);
// Clear the whole card table since we can not Get any additional dirty cards during the
// paused GC. This saves memory but only works for pause the world collectors.
t.NewTiming("ClearCardTable");
@@ -274,11 +274,11 @@
}
void MarkCompact::ResizeMarkStack(size_t new_size) {
- std::vector<Object*> temp(mark_stack_->Begin(), mark_stack_->End());
+ std::vector<StackReference<Object>> temp(mark_stack_->Begin(), mark_stack_->End());
CHECK_LE(mark_stack_->Size(), new_size);
mark_stack_->Resize(new_size);
- for (const auto& obj : temp) {
- mark_stack_->PushBack(obj);
+ for (auto& obj : temp) {
+ mark_stack_->PushBack(obj.AsMirrorPtr());
}
}
@@ -300,22 +300,20 @@
}
void MarkCompact::MarkHeapReferenceCallback(mirror::HeapReference<mirror::Object>* obj_ptr,
- void* arg) {
+ void* arg) {
reinterpret_cast<MarkCompact*>(arg)->MarkObject(obj_ptr->AsMirrorPtr());
}
void MarkCompact::DelayReferenceReferentCallback(mirror::Class* klass, mirror::Reference* ref,
- void* arg) {
+ void* arg) {
reinterpret_cast<MarkCompact*>(arg)->DelayReferenceReferent(klass, ref);
}
-void MarkCompact::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
- RootType /*root_type*/) {
+void MarkCompact::MarkRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
reinterpret_cast<MarkCompact*>(arg)->MarkObject(*root);
}
-void MarkCompact::UpdateRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
- RootType /*root_type*/) {
+void MarkCompact::UpdateRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
mirror::Object* obj = *root;
mirror::Object* new_obj = reinterpret_cast<MarkCompact*>(arg)->GetMarkedForwardAddress(obj);
if (obj != new_obj) {
diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h
index f40e870..06304bf 100644
--- a/runtime/gc/collector/mark_compact.h
+++ b/runtime/gc/collector/mark_compact.h
@@ -24,6 +24,7 @@
#include "base/macros.h"
#include "base/mutex.h"
#include "garbage_collector.h"
+#include "gc_root.h"
#include "gc/accounting/heap_bitmap.h"
#include "immune_region.h"
#include "lock_word.h"
@@ -45,7 +46,7 @@
namespace accounting {
template <typename T> class AtomicStack;
- typedef AtomicStack<mirror::Object*> ObjectStack;
+ typedef AtomicStack<mirror::Object> ObjectStack;
} // namespace accounting
namespace space {
@@ -113,8 +114,7 @@
void SweepSystemWeaks()
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
- static void MarkRootCallback(mirror::Object** root, void* arg, uint32_t /*tid*/,
- RootType /*root_type*/)
+ static void MarkRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
static mirror::Object* MarkObjectCallback(mirror::Object* root, void* arg)
@@ -156,13 +156,13 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Expand mark stack to 2x its current size.
- void ResizeMarkStack(size_t new_size);
+ void ResizeMarkStack(size_t new_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Returns true if we should sweep the space.
bool ShouldSweepSpace(space::ContinuousSpace* space) const;
// Push an object onto the mark stack.
- void MarkStackPush(mirror::Object* obj);
+ void MarkStackPush(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void UpdateAndMarkModUnion()
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
@@ -180,8 +180,7 @@
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
// Update the references of objects by using the forwarding addresses.
void UpdateReferences() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
- static void UpdateRootCallback(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
- RootType /*root_type*/)
+ static void UpdateRootCallback(mirror::Object** root, void* arg, const RootInfo& /*root_info*/)
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
// Move objects and restore lock words.
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 6ad44e6..cd63d26 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -217,7 +217,7 @@
Thread* self = Thread::Current();
CHECK(!Locks::mutator_lock_->IsExclusiveHeld(self));
// Process dirty cards and add dirty cards to mod union tables, also ages cards.
- heap_->ProcessCards(GetTimings(), false);
+ heap_->ProcessCards(GetTimings(), false, true, false);
// The checkpoint root marking is required to avoid a race condition which occurs if the
// following happens during a reference write:
// 1. mutator dirties the card (write barrier)
@@ -255,7 +255,8 @@
BindBitmaps();
FindDefaultSpaceBitmap();
// Process dirty cards and add dirty cards to mod union tables.
- heap_->ProcessCards(GetTimings(), false);
+ // If the GC type is non sticky, then we just clear the cards instead of ageing them.
+ heap_->ProcessCards(GetTimings(), false, true, GetGcType() != kGcTypeSticky);
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
MarkRoots(self);
MarkReachableObjects();
@@ -330,11 +331,11 @@
// Someone else acquired the lock and expanded the mark stack before us.
return;
}
- std::vector<Object*> temp(mark_stack_->Begin(), mark_stack_->End());
+ std::vector<StackReference<Object>> temp(mark_stack_->Begin(), mark_stack_->End());
CHECK_LE(mark_stack_->Size(), new_size);
mark_stack_->Resize(new_size);
- for (const auto& obj : temp) {
- mark_stack_->PushBack(obj);
+ for (auto& obj : temp) {
+ mark_stack_->PushBack(obj.AsMirrorPtr());
}
}
@@ -460,42 +461,35 @@
}
}
-void MarkSweep::MarkRootParallelCallback(Object** root, void* arg, uint32_t /*thread_id*/,
- RootType /*root_type*/) {
+void MarkSweep::MarkRootParallelCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNullParallel(*root);
}
-void MarkSweep::VerifyRootMarked(Object** root, void* arg, uint32_t /*thread_id*/,
- RootType /*root_type*/) {
+void MarkSweep::VerifyRootMarked(Object** root, void* arg, const RootInfo& /*root_info*/) {
CHECK(reinterpret_cast<MarkSweep*>(arg)->IsMarked(*root));
}
-void MarkSweep::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
- RootType /*root_type*/) {
+void MarkSweep::MarkRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNull(*root);
}
-void MarkSweep::VerifyRootCallback(const Object* root, void* arg, size_t vreg,
- const StackVisitor* visitor, RootType root_type) {
- reinterpret_cast<MarkSweep*>(arg)->VerifyRoot(root, vreg, visitor, root_type);
+void MarkSweep::VerifyRootCallback(Object** root, void* arg, const RootInfo& root_info) {
+ reinterpret_cast<MarkSweep*>(arg)->VerifyRoot(*root, root_info);
}
-void MarkSweep::VerifyRoot(const Object* root, size_t vreg, const StackVisitor* visitor,
- RootType root_type) {
+void MarkSweep::VerifyRoot(const Object* root, const RootInfo& root_info) {
// See if the root is on any space bitmap.
if (heap_->GetLiveBitmap()->GetContinuousSpaceBitmap(root) == nullptr) {
space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace();
if (large_object_space != nullptr && !large_object_space->Contains(root)) {
- LOG(ERROR) << "Found invalid root: " << root << " with type " << root_type;
- if (visitor != NULL) {
- LOG(ERROR) << visitor->DescribeLocation() << " in VReg: " << vreg;
- }
+ LOG(ERROR) << "Found invalid root: " << root << " ";
+ root_info.Describe(LOG(ERROR));
}
}
}
void MarkSweep::VerifyRoots() {
- Runtime::Current()->GetThreadList()->VerifyRoots(VerifyRootCallback, this);
+ Runtime::Current()->GetThreadList()->VisitRoots(VerifyRootCallback, this);
}
void MarkSweep::MarkRoots(Thread* self) {
@@ -561,7 +555,7 @@
class MarkStackTask : public Task {
public:
MarkStackTask(ThreadPool* thread_pool, MarkSweep* mark_sweep, size_t mark_stack_size,
- Object** mark_stack)
+ StackReference<Object>* mark_stack)
: mark_sweep_(mark_sweep),
thread_pool_(thread_pool),
mark_stack_pos_(mark_stack_size) {
@@ -634,11 +628,11 @@
MarkSweep* const mark_sweep_;
ThreadPool* const thread_pool_;
// Thread local mark stack for this task.
- Object* mark_stack_[kMaxSize];
+ StackReference<Object> mark_stack_[kMaxSize];
// Mark stack position.
size_t mark_stack_pos_;
- void MarkStackPush(Object* obj) ALWAYS_INLINE {
+ ALWAYS_INLINE void MarkStackPush(Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (UNLIKELY(mark_stack_pos_ == kMaxSize)) {
// Mark stack overflow, give 1/2 the stack to the thread pool as a new work task.
mark_stack_pos_ /= 2;
@@ -648,7 +642,7 @@
}
DCHECK(obj != nullptr);
DCHECK_LT(mark_stack_pos_, kMaxSize);
- mark_stack_[mark_stack_pos_++] = obj;
+ mark_stack_[mark_stack_pos_++].Assign(obj);
}
virtual void Finalize() {
@@ -667,7 +661,7 @@
Object* obj = nullptr;
if (kUseMarkStackPrefetch) {
while (mark_stack_pos_ != 0 && prefetch_fifo.size() < kFifoSize) {
- Object* mark_stack_obj = mark_stack_[--mark_stack_pos_];
+ Object* const mark_stack_obj = mark_stack_[--mark_stack_pos_].AsMirrorPtr();
DCHECK(mark_stack_obj != nullptr);
__builtin_prefetch(mark_stack_obj);
prefetch_fifo.push_back(mark_stack_obj);
@@ -681,7 +675,7 @@
if (UNLIKELY(mark_stack_pos_ == 0)) {
break;
}
- obj = mark_stack_[--mark_stack_pos_];
+ obj = mark_stack_[--mark_stack_pos_].AsMirrorPtr();
}
DCHECK(obj != nullptr);
visitor(obj);
@@ -694,12 +688,12 @@
CardScanTask(ThreadPool* thread_pool, MarkSweep* mark_sweep,
accounting::ContinuousSpaceBitmap* bitmap,
uint8_t* begin, uint8_t* end, uint8_t minimum_age, size_t mark_stack_size,
- Object** mark_stack_obj)
+ StackReference<Object>* mark_stack_obj, bool clear_card)
: MarkStackTask<false>(thread_pool, mark_sweep, mark_stack_size, mark_stack_obj),
bitmap_(bitmap),
begin_(begin),
end_(end),
- minimum_age_(minimum_age) {
+ minimum_age_(minimum_age), clear_card_(clear_card) {
}
protected:
@@ -707,6 +701,7 @@
uint8_t* const begin_;
uint8_t* const end_;
const uint8_t minimum_age_;
+ const bool clear_card_;
virtual void Finalize() {
delete this;
@@ -715,7 +710,9 @@
virtual void Run(Thread* self) NO_THREAD_SAFETY_ANALYSIS {
ScanObjectParallelVisitor visitor(this);
accounting::CardTable* card_table = mark_sweep_->GetHeap()->GetCardTable();
- size_t cards_scanned = card_table->Scan(bitmap_, begin_, end_, visitor, minimum_age_);
+ size_t cards_scanned = clear_card_ ?
+ card_table->Scan<true>(bitmap_, begin_, end_, visitor, minimum_age_) :
+ card_table->Scan<false>(bitmap_, begin_, end_, visitor, minimum_age_);
VLOG(heap) << "Parallel scanning cards " << reinterpret_cast<void*>(begin_) << " - "
<< reinterpret_cast<void*>(end_) << " = " << cards_scanned;
// Finish by emptying our local mark stack.
@@ -746,8 +743,8 @@
TimingLogger::ScopedTiming t(paused ? "(Paused)ScanGrayObjects" : __FUNCTION__,
GetTimings());
// Try to take some of the mark stack since we can pass this off to the worker tasks.
- Object** mark_stack_begin = mark_stack_->Begin();
- Object** mark_stack_end = mark_stack_->End();
+ StackReference<Object>* mark_stack_begin = mark_stack_->Begin();
+ StackReference<Object>* mark_stack_end = mark_stack_->End();
const size_t mark_stack_size = mark_stack_end - mark_stack_begin;
// Estimated number of work tasks we will create.
const size_t mark_stack_tasks = GetHeap()->GetContinuousSpaces().size() * thread_count;
@@ -770,6 +767,11 @@
// Calculate how much address range each task gets.
const size_t card_delta = RoundUp(address_range / thread_count + 1,
accounting::CardTable::kCardSize);
+ // If paused and the space is neither zygote nor image space, we could clear the dirty
+ // cards to avoid accumulating them to increase card scanning load in the following GC
+ // cycles. We need to keep dirty cards of image space and zygote space in order to track
+ // references to the other spaces.
+ bool clear_card = paused && !space->IsZygoteSpace() && !space->IsImageSpace();
// Create the worker tasks for this space.
while (card_begin != card_end) {
// Add a range of cards.
@@ -784,7 +786,7 @@
// Add the new task to the thread pool.
auto* task = new CardScanTask(thread_pool, this, space->GetMarkBitmap(), card_begin,
card_begin + card_increment, minimum_age,
- mark_stack_increment, mark_stack_end);
+ mark_stack_increment, mark_stack_end, clear_card);
thread_pool->AddTask(self, task);
card_begin += card_increment;
}
@@ -818,8 +820,14 @@
}
TimingLogger::ScopedTiming t(name, GetTimings());
ScanObjectVisitor visitor(this);
- card_table->Scan(space->GetMarkBitmap(), space->Begin(), space->End(), visitor,
- minimum_age);
+ bool clear_card = paused && !space->IsZygoteSpace() && !space->IsImageSpace();
+ if (clear_card) {
+ card_table->Scan<true>(space->GetMarkBitmap(), space->Begin(), space->End(), visitor,
+ minimum_age);
+ } else {
+ card_table->Scan<false>(space->GetMarkBitmap(), space->Begin(), space->End(), visitor,
+ minimum_age);
+ }
}
}
}
@@ -947,9 +955,9 @@
void MarkSweep::VerifyIsLive(const Object* obj) {
if (!heap_->GetLiveBitmap()->Test(obj)) {
- accounting::ObjectStack* allocation_stack = heap_->allocation_stack_.get();
- CHECK(std::find(allocation_stack->Begin(), allocation_stack->End(), obj) !=
- allocation_stack->End()) << "Found dead object " << obj << "\n" << heap_->DumpSpaces();
+ // TODO: Consider live stack? Has this code bitrotted?
+ CHECK(!heap_->allocation_stack_->Contains(obj))
+ << "Found dead object " << obj << "\n" << heap_->DumpSpaces();
}
}
@@ -981,7 +989,11 @@
mark_sweep_->GetHeap()->RevokeRosAllocThreadLocalBuffers(thread);
ATRACE_END();
}
- mark_sweep_->GetBarrier().Pass(self);
+ // If thread is a running mutator, then act on behalf of the garbage collector.
+ // See the code in ThreadList::RunCheckpoint.
+ if (thread->GetState() == kRunnable) {
+ mark_sweep_->GetBarrier().Pass(self);
+ }
}
private:
@@ -998,7 +1010,11 @@
// run through the barrier including self.
size_t barrier_count = thread_list->RunCheckpoint(&check_point);
// Release locks then wait for all mutator threads to pass the barrier.
- // TODO: optimize to not release locks when there are no threads to wait for.
+ // If there are no threads to wait which implys that all the checkpoint functions are finished,
+ // then no need to release locks.
+ if (barrier_count == 0) {
+ return;
+ }
Locks::heap_bitmap_lock_->ExclusiveUnlock(self);
Locks::mutator_lock_->SharedUnlock(self);
{
@@ -1018,7 +1034,7 @@
ObjectBytePair freed;
ObjectBytePair freed_los;
// How many objects are left in the array, modified after each space is swept.
- Object** objects = allocations->Begin();
+ StackReference<Object>* objects = allocations->Begin();
size_t count = allocations->Size();
// Change the order to ensure that the non-moving space last swept as an optimization.
std::vector<space::ContinuousSpace*> sweep_spaces;
@@ -1046,9 +1062,9 @@
if (swap_bitmaps) {
std::swap(live_bitmap, mark_bitmap);
}
- Object** out = objects;
+ StackReference<Object>* out = objects;
for (size_t i = 0; i < count; ++i) {
- Object* obj = objects[i];
+ Object* const obj = objects[i].AsMirrorPtr();
if (kUseThreadLocalAllocationStack && obj == nullptr) {
continue;
}
@@ -1065,7 +1081,7 @@
chunk_free_buffer[chunk_free_pos++] = obj;
}
} else {
- *(out++) = obj;
+ (out++)->Assign(obj);
}
}
if (chunk_free_pos > 0) {
@@ -1087,7 +1103,7 @@
std::swap(large_live_objects, large_mark_objects);
}
for (size_t i = 0; i < count; ++i) {
- Object* obj = objects[i];
+ Object* const obj = objects[i].AsMirrorPtr();
// Handle large objects.
if (kUseThreadLocalAllocationStack && obj == nullptr) {
continue;
@@ -1188,7 +1204,7 @@
static_cast<size_t>(MarkStackTask<false>::kMaxSize));
CHECK_GT(chunk_size, 0U);
// Split the current mark stack up into work tasks.
- for (mirror::Object **it = mark_stack_->Begin(), **end = mark_stack_->End(); it < end; ) {
+ for (auto* it = mark_stack_->Begin(), *end = mark_stack_->End(); it < end; ) {
const size_t delta = std::min(static_cast<size_t>(end - it), chunk_size);
thread_pool->AddTask(self, new MarkStackTask<false>(thread_pool, this, delta, it));
it += delta;
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index 9ac110d..3f99e21 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -24,6 +24,7 @@
#include "base/macros.h"
#include "base/mutex.h"
#include "garbage_collector.h"
+#include "gc_root.h"
#include "gc/accounting/heap_bitmap.h"
#include "immune_region.h"
#include "object_callbacks.h"
@@ -46,7 +47,7 @@
namespace accounting {
template<typename T> class AtomicStack;
- typedef AtomicStack<mirror::Object*> ObjectStack;
+ typedef AtomicStack<mirror::Object> ObjectStack;
} // namespace accounting
namespace collector {
@@ -135,7 +136,8 @@
// Sweeps unmarked objects to complete the garbage collection. Virtual as by default it sweeps
// all allocation spaces. Partial and sticky GCs want to just sweep a subset of the heap.
- virtual void Sweep(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+ virtual void Sweep(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Sweeps unmarked objects to complete the garbage collection.
void SweepLargeObjects(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
@@ -161,13 +163,14 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
static mirror::Object* VerifySystemWeakIsLiveCallback(mirror::Object* obj, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
void VerifySystemWeaks()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
// Verify that an object is live, either in a live bitmap or in the allocation stack.
void VerifyIsLive(const mirror::Object* obj)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
static mirror::Object* MarkObjectCallback(mirror::Object* obj, void* arg)
@@ -182,13 +185,11 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
- static void MarkRootCallback(mirror::Object** root, void* arg, uint32_t thread_id,
- RootType root_type)
+ static void MarkRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
- static void VerifyRootMarked(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
- RootType /*root_type*/)
+ static void VerifyRootMarked(mirror::Object** root, void* arg, const RootInfo& root_info)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
@@ -196,8 +197,7 @@
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void MarkRootParallelCallback(mirror::Object** root, void* arg, uint32_t thread_id,
- RootType root_type)
+ static void MarkRootParallelCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Marks an object.
@@ -225,11 +225,12 @@
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
void MarkObjectNonNull(mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
// Marks an object atomically, safe to use from multiple threads.
- void MarkObjectNonNullParallel(mirror::Object* obj);
+ void MarkObjectNonNullParallel(mirror::Object* obj)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Returns true if we need to add obj to a mark stack.
bool MarkObjectParallel(const mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS;
@@ -240,21 +241,21 @@
NO_THREAD_SAFETY_ANALYSIS;
// Expand mark stack to 2x its current size.
- void ExpandMarkStack() EXCLUSIVE_LOCKS_REQUIRED(mark_stack_lock_);
- void ResizeMarkStack(size_t new_size) EXCLUSIVE_LOCKS_REQUIRED(mark_stack_lock_);
+ void ExpandMarkStack() EXCLUSIVE_LOCKS_REQUIRED(mark_stack_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void ResizeMarkStack(size_t new_size) EXCLUSIVE_LOCKS_REQUIRED(mark_stack_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Returns how many threads we should use for the current GC phase based on if we are paused,
// whether or not we care about pauses.
size_t GetThreadCount(bool paused) const;
- static void VerifyRootCallback(const mirror::Object* root, void* arg, size_t vreg,
- const StackVisitor *visitor, RootType root_type);
+ static void VerifyRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info);
- void VerifyRoot(const mirror::Object* root, size_t vreg, const StackVisitor* visitor,
- RootType root_type) NO_THREAD_SAFETY_ANALYSIS;
+ void VerifyRoot(const mirror::Object* root, const RootInfo& root_info) NO_THREAD_SAFETY_ANALYSIS;
// Push a single reference on a mark stack.
- void PushOnMarkStack(mirror::Object* obj);
+ void PushOnMarkStack(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Blackens objects grayed during a garbage collection.
void ScanGrayObjects(bool paused, uint8_t minimum_age)
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index cb9f111..c1ba5e3 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -216,7 +216,7 @@
// Assume the cleared space is already empty.
BindBitmaps();
// Process dirty cards and add dirty cards to mod-union tables.
- heap_->ProcessCards(GetTimings(), kUseRememberedSet && generational_);
+ heap_->ProcessCards(GetTimings(), kUseRememberedSet && generational_, false, true);
// Clear the whole card table since we can not Get any additional dirty cards during the
// paused GC. This saves memory but only works for pause the world collectors.
t.NewTiming("ClearCardTable");
@@ -253,8 +253,17 @@
RecordFree(ObjectBytePair(from_objects - to_objects, from_bytes - to_bytes));
// Clear and protect the from space.
from_space_->Clear();
- VLOG(heap) << "Protecting from_space_: " << *from_space_;
- from_space_->GetMemMap()->Protect(kProtectFromSpace ? PROT_NONE : PROT_READ);
+ if (kProtectFromSpace && !from_space_->IsRosAllocSpace()) {
+ // Protect with PROT_NONE.
+ VLOG(heap) << "Protecting from_space_ : " << *from_space_;
+ from_space_->GetMemMap()->Protect(PROT_NONE);
+ } else {
+ // If RosAllocSpace, we'll leave it as PROT_READ here so the
+ // rosaloc verification can read the metadata magic number and
+ // protect it with PROT_NONE later in FinishPhase().
+ VLOG(heap) << "Protecting from_space_ with PROT_READ : " << *from_space_;
+ from_space_->GetMemMap()->Protect(PROT_READ);
+ }
heap_->PreSweepingGcVerification(this);
if (swap_semi_spaces_) {
heap_->SwapSemiSpaces();
@@ -412,11 +421,11 @@
}
void SemiSpace::ResizeMarkStack(size_t new_size) {
- std::vector<Object*> temp(mark_stack_->Begin(), mark_stack_->End());
+ std::vector<StackReference<Object>> temp(mark_stack_->Begin(), mark_stack_->End());
CHECK_LE(mark_stack_->Size(), new_size);
mark_stack_->Resize(new_size);
- for (const auto& obj : temp) {
- mark_stack_->PushBack(obj);
+ for (auto& obj : temp) {
+ mark_stack_->PushBack(obj.AsMirrorPtr());
}
}
@@ -591,8 +600,7 @@
reinterpret_cast<SemiSpace*>(arg)->DelayReferenceReferent(klass, ref);
}
-void SemiSpace::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
- RootType /*root_type*/) {
+void SemiSpace::MarkRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
auto ref = StackReference<mirror::Object>::FromMirrorPtr(*root);
reinterpret_cast<SemiSpace*>(arg)->MarkObject(&ref);
if (*root != ref.AsMirrorPtr()) {
@@ -749,6 +757,10 @@
void SemiSpace::FinishPhase() {
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ if (kProtectFromSpace && from_space_->IsRosAllocSpace()) {
+ VLOG(heap) << "Protecting from_space_ with PROT_NONE : " << *from_space_;
+ from_space_->GetMemMap()->Protect(PROT_NONE);
+ }
// Null the "to" and "from" spaces since compacting from one to the other isn't valid until
// further action is done by the heap.
to_space_ = nullptr;
diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h
index 1c4f1e4..192fb14 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -23,6 +23,7 @@
#include "base/macros.h"
#include "base/mutex.h"
#include "garbage_collector.h"
+#include "gc_root.h"
#include "gc/accounting/heap_bitmap.h"
#include "immune_region.h"
#include "mirror/object_reference.h"
@@ -44,7 +45,7 @@
namespace accounting {
template <typename T> class AtomicStack;
- typedef AtomicStack<mirror::Object*> ObjectStack;
+ typedef AtomicStack<mirror::Object> ObjectStack;
} // namespace accounting
namespace space {
@@ -132,8 +133,7 @@
void SweepSystemWeaks()
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
- static void MarkRootCallback(mirror::Object** root, void* arg, uint32_t /*tid*/,
- RootType /*root_type*/)
+ static void MarkRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
static mirror::Object* MarkObjectCallback(mirror::Object* root, void* arg)
@@ -178,13 +178,13 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Expand mark stack to 2x its current size.
- void ResizeMarkStack(size_t new_size);
+ void ResizeMarkStack(size_t new_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Returns true if we should sweep the space.
virtual bool ShouldSweepSpace(space::ContinuousSpace* space) const;
// Push an object onto the mark stack.
- void MarkStackPush(mirror::Object* obj);
+ void MarkStackPush(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void UpdateAndMarkModUnion()
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h
index ef5d56e..9275e6d 100644
--- a/runtime/gc/collector_type.h
+++ b/runtime/gc/collector_type.h
@@ -46,6 +46,19 @@
};
std::ostream& operator<<(std::ostream& os, const CollectorType& collector_type);
+static constexpr CollectorType kCollectorTypeDefault =
+#if ART_DEFAULT_GC_TYPE_IS_CMS
+ kCollectorTypeCMS
+#elif ART_DEFAULT_GC_TYPE_IS_SS
+ kCollectorTypeSS
+#elif ART_DEFAULT_GC_TYPE_IS_GSS
+ kCollectorTypeGSS
+#else
+ kCollectorTypeCMS
+#error "ART default GC type must be set"
+#endif
+ ; // NOLINT [whitespace/semicolon] [5]
+
} // namespace gc
} // namespace art
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 9d2f6d1..b8c2452 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -25,6 +25,7 @@
#include "gc/space/bump_pointer_space-inl.h"
#include "gc/space/dlmalloc_space-inl.h"
#include "gc/space/large_object_space.h"
+#include "gc/space/region_space-inl.h"
#include "gc/space/rosalloc_space-inl.h"
#include "runtime.h"
#include "handle_scope-inl.h"
@@ -66,11 +67,12 @@
size_t bytes_allocated;
size_t usable_size;
size_t new_num_bytes_allocated = 0;
- if (allocator == kAllocatorTypeTLAB) {
+ if (allocator == kAllocatorTypeTLAB || allocator == kAllocatorTypeRegionTLAB) {
byte_count = RoundUp(byte_count, space::BumpPointerSpace::kAlignment);
}
// If we have a thread local allocation we don't need to update bytes allocated.
- if (allocator == kAllocatorTypeTLAB && byte_count <= self->TlabSize()) {
+ if ((allocator == kAllocatorTypeTLAB || allocator == kAllocatorTypeRegionTLAB) &&
+ byte_count <= self->TlabSize()) {
obj = self->AllocTlab(byte_count);
DCHECK(obj != nullptr) << "AllocTlab can't fail";
obj->SetClass(klass);
@@ -195,7 +197,7 @@
inline mirror::Object* Heap::TryToAllocate(Thread* self, AllocatorType allocator_type,
size_t alloc_size, size_t* bytes_allocated,
size_t* usable_size) {
- if (allocator_type != kAllocatorTypeTLAB &&
+ if (allocator_type != kAllocatorTypeTLAB && allocator_type != kAllocatorTypeRegionTLAB &&
UNLIKELY(IsOutOfMemoryOnAllocation<kGrow>(allocator_type, alloc_size))) {
return nullptr;
}
@@ -265,6 +267,55 @@
*usable_size = alloc_size;
break;
}
+ case kAllocatorTypeRegion: {
+ DCHECK(region_space_ != nullptr);
+ alloc_size = RoundUp(alloc_size, space::RegionSpace::kAlignment);
+ ret = region_space_->AllocNonvirtual<false>(alloc_size, bytes_allocated, usable_size);
+ break;
+ }
+ case kAllocatorTypeRegionTLAB: {
+ DCHECK(region_space_ != nullptr);
+ DCHECK_ALIGNED(alloc_size, space::RegionSpace::kAlignment);
+ if (UNLIKELY(self->TlabSize() < alloc_size)) {
+ if (space::RegionSpace::kRegionSize >= alloc_size) {
+ // Non-large. Check OOME for a tlab.
+ if (LIKELY(!IsOutOfMemoryOnAllocation<kGrow>(allocator_type, space::RegionSpace::kRegionSize))) {
+ // Try to allocate a tlab.
+ if (!region_space_->AllocNewTlab(self)) {
+ // Failed to allocate a tlab. Try non-tlab.
+ ret = region_space_->AllocNonvirtual<false>(alloc_size, bytes_allocated, usable_size);
+ return ret;
+ }
+ *bytes_allocated = space::RegionSpace::kRegionSize;
+ // Fall-through.
+ } else {
+ // Check OOME for a non-tlab allocation.
+ if (!IsOutOfMemoryOnAllocation<kGrow>(allocator_type, alloc_size)) {
+ ret = region_space_->AllocNonvirtual<false>(alloc_size, bytes_allocated, usable_size);
+ return ret;
+ } else {
+ // Neither tlab or non-tlab works. Give up.
+ return nullptr;
+ }
+ }
+ } else {
+ // Large. Check OOME.
+ if (LIKELY(!IsOutOfMemoryOnAllocation<kGrow>(allocator_type, alloc_size))) {
+ ret = region_space_->AllocNonvirtual<false>(alloc_size, bytes_allocated, usable_size);
+ return ret;
+ } else {
+ return nullptr;
+ }
+ }
+ } else {
+ *bytes_allocated = 0;
+ }
+ // The allocation can't fail.
+ ret = self->AllocTlab(alloc_size);
+ DCHECK(ret != nullptr);
+ *usable_size = alloc_size;
+ break;
+ }
default: {
LOG(FATAL) << "Invalid allocator type";
ret = nullptr;
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 0fd0a9f..419d555 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -30,6 +30,7 @@
#include "common_throws.h"
#include "cutils/sched_policy.h"
#include "debugger.h"
+#include "dex_file-inl.h"
#include "gc/accounting/atomic_stack.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap-inl.h"
@@ -48,9 +49,11 @@
#include "gc/space/dlmalloc_space-inl.h"
#include "gc/space/image_space.h"
#include "gc/space/large_object_space.h"
+#include "gc/space/region_space.h"
#include "gc/space/rosalloc_space-inl.h"
#include "gc/space/space-inl.h"
#include "gc/space/zygote_space.h"
+#include "gc/task_processor.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "heap-inl.h"
#include "image.h"
@@ -76,8 +79,6 @@
static constexpr size_t kCollectorTransitionStressIterations = 0;
static constexpr size_t kCollectorTransitionStressWait = 10 * 1000; // Microseconds
-static constexpr bool kGCALotMode = false;
-static constexpr size_t kGcAlotInterval = KB;
// Minimum amount of remaining bytes before a concurrent GC is triggered.
static constexpr size_t kMinConcurrentRemainingBytes = 128 * KB;
static constexpr size_t kMaxConcurrentRemainingBytes = 512 * KB;
@@ -99,6 +100,15 @@
static const char* kNonMovingSpaceName = "non moving space";
static const char* kZygoteSpaceName = "zygote space";
static constexpr size_t kGSSBumpPointerSpaceCapacity = 32 * MB;
+static constexpr bool kGCALotMode = false;
+// GC alot mode uses a small allocation stack to stress test a lot of GC.
+static constexpr size_t kGcAlotAllocationStackSize = 4 * KB /
+ sizeof(mirror::HeapReference<mirror::Object>);
+// Verify objet has a small allocation stack size since searching the allocation stack is slow.
+static constexpr size_t kVerifyObjectAllocationStackSize = 16 * KB /
+ sizeof(mirror::HeapReference<mirror::Object>);
+static constexpr size_t kDefaultAllocationStackSize = 8 * MB /
+ sizeof(mirror::HeapReference<mirror::Object>);
Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free,
double target_utilization, double foreground_heap_growth_multiplier,
@@ -121,10 +131,7 @@
foreground_collector_type_(foreground_collector_type),
background_collector_type_(background_collector_type),
desired_collector_type_(foreground_collector_type_),
- heap_trim_request_lock_(nullptr),
- last_trim_time_(0),
- heap_transition_or_trim_target_time_(0),
- heap_trim_request_pending_(false),
+ pending_task_lock_(nullptr),
parallel_gc_threads_(parallel_gc_threads),
conc_gc_threads_(conc_gc_threads),
low_memory_mode_(low_memory_mode),
@@ -158,19 +165,19 @@
verify_pre_gc_rosalloc_(verify_pre_gc_rosalloc),
verify_pre_sweeping_rosalloc_(verify_pre_sweeping_rosalloc),
verify_post_gc_rosalloc_(verify_post_gc_rosalloc),
- last_gc_time_ns_(NanoTime()),
- allocation_rate_(0),
/* For GC a lot mode, we limit the allocations stacks to be kGcAlotInterval allocations. This
* causes a lot of GC since we do a GC for alloc whenever the stack is full. When heap
* verification is enabled, we limit the size of allocation stacks to speed up their
* searching.
*/
- max_allocation_stack_size_(kGCALotMode ? kGcAlotInterval
- : (kVerifyObjectSupport > kVerifyObjectModeFast) ? KB : MB),
+ max_allocation_stack_size_(kGCALotMode ? kGcAlotAllocationStackSize
+ : (kVerifyObjectSupport > kVerifyObjectModeFast) ? kVerifyObjectAllocationStackSize :
+ kDefaultAllocationStackSize),
current_allocator_(kAllocatorTypeDlMalloc),
current_non_moving_allocator_(kAllocatorTypeNonMoving),
bump_pointer_space_(nullptr),
temp_space_(nullptr),
+ region_space_(nullptr),
min_free_(min_free),
max_free_(max_free),
target_utilization_(target_utilization),
@@ -185,6 +192,8 @@
min_interval_homogeneous_space_compaction_by_oom_(
min_interval_homogeneous_space_compaction_by_oom),
last_time_homogeneous_space_compaction_by_oom_(NanoTime()),
+ pending_collector_transition_(nullptr),
+ pending_heap_trim_(nullptr),
use_homogeneous_space_compaction_for_oom_(use_homogeneous_space_compaction_for_oom) {
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
LOG(INFO) << "Heap() entering";
@@ -204,6 +213,12 @@
mark_bitmap_.reset(new accounting::HeapBitmap(this));
// Requested begin for the alloc space, to follow the mapped image and oat files
uint8_t* requested_alloc_space_begin = nullptr;
+ 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).
+ CHECK_GE(300 * MB, non_moving_space_capacity);
+ requested_alloc_space_begin = reinterpret_cast<uint8_t*>(300 * MB) - non_moving_space_capacity;
+ }
if (!image_file_name.empty()) {
std::string error_msg;
space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str(),
@@ -234,8 +249,9 @@
+-main alloc space2 / bump space 2 (capacity_)+-
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
*/
- // We don't have hspace compaction enabled with GSS.
- if (foreground_collector_type_ == kCollectorTypeGSS) {
+ // We don't have hspace compaction enabled with GSS or CC.
+ if (foreground_collector_type_ == kCollectorTypeGSS ||
+ foreground_collector_type_ == kCollectorTypeCC) {
use_homogeneous_space_compaction_for_oom_ = false;
}
bool support_homogeneous_space_compaction =
@@ -273,10 +289,12 @@
// Try to reserve virtual memory at a lower address if we have a separate non moving space.
request_begin = reinterpret_cast<uint8_t*>(300 * MB);
}
- // Attempt to create 2 mem maps at or after the requested begin.
- main_mem_map_1.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[0], request_begin, capacity_,
- &error_str));
- CHECK(main_mem_map_1.get() != nullptr) << error_str;
+ if (foreground_collector_type_ != kCollectorTypeCC) {
+ // Attempt to create 2 mem maps at or after the requested begin.
+ main_mem_map_1.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[0], request_begin, capacity_,
+ &error_str));
+ CHECK(main_mem_map_1.get() != nullptr) << error_str;
+ }
if (support_homogeneous_space_compaction ||
background_collector_type_ == kCollectorTypeSS ||
foreground_collector_type_ == kCollectorTypeSS) {
@@ -298,7 +316,10 @@
AddSpace(non_moving_space_);
}
// Create other spaces based on whether or not we have a moving GC.
- if (IsMovingGc(foreground_collector_type_) && foreground_collector_type_ != kCollectorTypeGSS) {
+ if (foreground_collector_type_ == kCollectorTypeCC) {
+ region_space_ = space::RegionSpace::Create("Region space", capacity_ * 2, request_begin);
+ AddSpace(region_space_);
+ } else if (IsMovingGc(foreground_collector_type_) && foreground_collector_type_ != kCollectorTypeGSS) {
// Create bump pointer spaces.
// We only to create the bump pointer if the foreground collector is a compacting GC.
// TODO: Place bump-pointer spaces somewhere to minimize size of card table.
@@ -343,11 +364,11 @@
CHECK(non_moving_space_ != nullptr);
CHECK(!non_moving_space_->CanMoveObjects());
// Allocate the large object space.
- if (large_object_space_type == space::kLargeObjectSpaceTypeFreeList) {
+ if (large_object_space_type == space::LargeObjectSpaceType::kFreeList) {
large_object_space_ = space::FreeListSpace::Create("free list large object space", nullptr,
capacity_);
CHECK(large_object_space_ != nullptr) << "Failed to create large object space";
- } else if (large_object_space_type == space::kLargeObjectSpaceTypeMap) {
+ } else if (large_object_space_type == space::LargeObjectSpaceType::kMap) {
large_object_space_ = space::LargeObjectMapSpace::Create("mem map large object space");
CHECK(large_object_space_ != nullptr) << "Failed to create large object space";
} else {
@@ -372,6 +393,12 @@
// Allocate the card table.
card_table_.reset(accounting::CardTable::Create(heap_begin, heap_capacity));
CHECK(card_table_.get() != NULL) << "Failed to create card table";
+
+ if (foreground_collector_type_ == kCollectorTypeCC && kUseTableLookupReadBarrier) {
+ rb_table_.reset(new accounting::ReadBarrierTable());
+ DCHECK(rb_table_->IsAllCleared());
+ }
+
// Card cache for now since it makes it easier for us to update the references to the copying
// spaces.
accounting::ModUnionTable* mod_union_table =
@@ -400,8 +427,8 @@
gc_complete_lock_ = new Mutex("GC complete lock");
gc_complete_cond_.reset(new ConditionVariable("GC complete condition variable",
*gc_complete_lock_));
- heap_trim_request_lock_ = new Mutex("Heap trim request lock");
- last_gc_size_ = GetBytesAllocated();
+ task_processor_.reset(new TaskProcessor());
+ pending_task_lock_ = new Mutex("Pending task lock");
if (ignore_max_footprint_) {
SetIdealFootprint(std::numeric_limits<size_t>::max());
concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
@@ -696,25 +723,83 @@
}
}
+// Visit objects when threads aren't suspended. If concurrent moving
+// GC, disable moving GC and suspend threads and then visit objects.
void Heap::VisitObjects(ObjectCallback callback, void* arg) {
- // GCs can move objects, so don't allow this.
- ScopedAssertNoThreadSuspension ants(Thread::Current(), "Visiting objects");
+ Thread* self = Thread::Current();
+ Locks::mutator_lock_->AssertSharedHeld(self);
+ DCHECK(!Locks::mutator_lock_->IsExclusiveHeld(self)) << "Call VisitObjectsPaused() instead";
+ if (IsGcConcurrentAndMoving()) {
+ // Concurrent moving GC. Just suspending threads isn't sufficient
+ // because a collection isn't one big pause and we could suspend
+ // threads in the middle (between phases) of a concurrent moving
+ // collection where it's not easily known which objects are alive
+ // (both the region space and the non-moving space) or which
+ // copies of objects to visit, and the to-space invariant could be
+ // easily broken. Visit objects while GC isn't running by using
+ // IncrementDisableMovingGC() and threads are suspended.
+ IncrementDisableMovingGC(self);
+ self->TransitionFromRunnableToSuspended(kWaitingForVisitObjects);
+ ThreadList* tl = Runtime::Current()->GetThreadList();
+ tl->SuspendAll();
+ VisitObjectsInternalRegionSpace(callback, arg);
+ VisitObjectsInternal(callback, arg);
+ tl->ResumeAll();
+ self->TransitionFromSuspendedToRunnable();
+ DecrementDisableMovingGC(self);
+ } else {
+ // GCs can move objects, so don't allow this.
+ ScopedAssertNoThreadSuspension ants(self, "Visiting objects");
+ DCHECK(region_space_ == nullptr);
+ VisitObjectsInternal(callback, arg);
+ }
+}
+
+// Visit objects when threads are already suspended.
+void Heap::VisitObjectsPaused(ObjectCallback callback, void* arg) {
+ Thread* self = Thread::Current();
+ Locks::mutator_lock_->AssertExclusiveHeld(self);
+ VisitObjectsInternalRegionSpace(callback, arg);
+ VisitObjectsInternal(callback, arg);
+}
+
+// Visit objects in the region spaces.
+void Heap::VisitObjectsInternalRegionSpace(ObjectCallback callback, void* arg) {
+ Thread* self = Thread::Current();
+ Locks::mutator_lock_->AssertExclusiveHeld(self);
+ if (region_space_ != nullptr) {
+ DCHECK(IsGcConcurrentAndMoving());
+ if (!zygote_creation_lock_.IsExclusiveHeld(self)) {
+ // Exclude the pre-zygote fork time where the semi-space collector
+ // calls VerifyHeapReferences() as part of the zygote compaction
+ // which then would call here without the moving GC disabled,
+ // which is fine.
+ DCHECK(IsMovingGCDisabled(self));
+ }
+ region_space_->Walk(callback, arg);
+ }
+}
+
+// Visit objects in the other spaces.
+void Heap::VisitObjectsInternal(ObjectCallback callback, void* arg) {
if (bump_pointer_space_ != nullptr) {
// Visit objects in bump pointer space.
bump_pointer_space_->Walk(callback, arg);
}
// TODO: Switch to standard begin and end to use ranged a based loop.
- for (mirror::Object** it = allocation_stack_->Begin(), **end = allocation_stack_->End();
- it < end; ++it) {
- mirror::Object* obj = *it;
+ for (auto* it = allocation_stack_->Begin(), *end = allocation_stack_->End(); it < end; ++it) {
+ mirror::Object* const obj = it->AsMirrorPtr();
if (obj != nullptr && obj->GetClass() != nullptr) {
// Avoid the race condition caused by the object not yet being written into the allocation
- // stack or the class not yet being written in the object. Or, if kUseThreadLocalAllocationStack,
- // there can be nulls on the allocation stack.
+ // stack or the class not yet being written in the object. Or, if
+ // kUseThreadLocalAllocationStack, there can be nulls on the allocation stack.
callback(obj, arg);
}
}
- GetLiveBitmap()->Walk(callback, arg);
+ {
+ ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
+ GetLiveBitmap()->Walk(callback, arg);
+ }
}
void Heap::MarkAllocStackAsLive(accounting::ObjectStack* stack) {
@@ -847,7 +932,7 @@
os << "Zygote space size " << PrettySize(zygote_space_->Size()) << "\n";
}
os << "Total mutator paused time: " << PrettyDuration(total_paused_time) << "\n";
- os << "Total time waiting for GC to complete: " << PrettyDuration(total_wait_time_) << "\n";
+ os << "Total time waiting for GC to complete: " << PrettyDuration(total_wait_time_);
BaseMutex::DumpAll(os);
}
@@ -862,7 +947,7 @@
STLDeleteElements(&continuous_spaces_);
STLDeleteElements(&discontinuous_spaces_);
delete gc_complete_lock_;
- delete heap_trim_request_lock_;
+ delete pending_task_lock_;
VLOG(heap) << "Finished ~Heap()";
}
@@ -925,6 +1010,9 @@
} else if (allocator_type == kAllocatorTypeBumpPointer ||
allocator_type == kAllocatorTypeTLAB) {
space = bump_pointer_space_;
+ } else if (allocator_type == kAllocatorTypeRegion ||
+ allocator_type == kAllocatorTypeRegionTLAB) {
+ space = region_space_;
}
if (space != nullptr) {
space->LogFragmentationAllocFailure(oss, byte_count);
@@ -933,37 +1021,23 @@
self->ThrowOutOfMemoryError(oss.str().c_str());
}
-void Heap::DoPendingTransitionOrTrim() {
- Thread* self = Thread::Current();
- CollectorType desired_collector_type;
- // Wait until we reach the desired transition time.
- while (true) {
- uint64_t wait_time;
- {
- MutexLock mu(self, *heap_trim_request_lock_);
- desired_collector_type = desired_collector_type_;
- uint64_t current_time = NanoTime();
- if (current_time >= heap_transition_or_trim_target_time_) {
- break;
- }
- wait_time = heap_transition_or_trim_target_time_ - current_time;
- }
- ScopedThreadStateChange tsc(self, kSleeping);
- usleep(wait_time / 1000); // Usleep takes microseconds.
- }
+void Heap::DoPendingCollectorTransition() {
+ CollectorType desired_collector_type = desired_collector_type_;
// Launch homogeneous space compaction if it is desired.
if (desired_collector_type == kCollectorTypeHomogeneousSpaceCompact) {
if (!CareAboutPauseTimes()) {
PerformHomogeneousSpaceCompact();
+ } else {
+ VLOG(gc) << "Homogeneous compaction ignored due to jank perceptible process state";
}
- // No need to Trim(). Homogeneous space compaction may free more virtual and physical memory.
- desired_collector_type = collector_type_;
- return;
+ } else {
+ TransitionCollector(desired_collector_type);
}
- // Transition the collector if the desired collector type is not the same as the current
- // collector type.
- TransitionCollector(desired_collector_type);
+}
+
+void Heap::Trim(Thread* self) {
if (!CareAboutPauseTimes()) {
+ ATRACE_BEGIN("Deflating monitors");
// Deflate the monitors, this can cause a pause but shouldn't matter since we don't care
// about pauses.
Runtime* runtime = Runtime::Current();
@@ -973,9 +1047,10 @@
VLOG(heap) << "Deflating " << count << " monitors took "
<< PrettyDuration(NanoTime() - start_time);
runtime->GetThreadList()->ResumeAll();
+ ATRACE_END();
}
- // Do a heap trim if it is needed.
- Trim();
+ TrimIndirectReferenceTables(self);
+ TrimSpaces(self);
}
class TrimIndirectReferenceTableClosure : public Closure {
@@ -986,24 +1061,35 @@
ATRACE_BEGIN("Trimming reference table");
thread->GetJniEnv()->locals.Trim();
ATRACE_END();
- barrier_->Pass(Thread::Current());
+ // If thread is a running mutator, then act on behalf of the trim thread.
+ // See the code in ThreadList::RunCheckpoint.
+ if (thread->GetState() == kRunnable) {
+ barrier_->Pass(Thread::Current());
+ }
}
private:
Barrier* const barrier_;
};
-
-void Heap::Trim() {
- Thread* self = Thread::Current();
- {
- MutexLock mu(self, *heap_trim_request_lock_);
- if (!heap_trim_request_pending_ || last_trim_time_ + kHeapTrimWait >= NanoTime()) {
- return;
- }
- last_trim_time_ = NanoTime();
- heap_trim_request_pending_ = false;
+void Heap::TrimIndirectReferenceTables(Thread* self) {
+ ScopedObjectAccess soa(self);
+ ATRACE_BEGIN(__FUNCTION__);
+ JavaVMExt* vm = soa.Vm();
+ // Trim globals indirect reference table.
+ vm->TrimGlobals();
+ // Trim locals indirect reference tables.
+ Barrier barrier(0);
+ TrimIndirectReferenceTableClosure closure(&barrier);
+ ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
+ size_t barrier_count = Runtime::Current()->GetThreadList()->RunCheckpoint(&closure);
+ if (barrier_count != 0) {
+ barrier.Increment(self, barrier_count);
}
+ ATRACE_END();
+}
+
+void Heap::TrimSpaces(Thread* self) {
{
// Need to do this before acquiring the locks since we don't want to get suspended while
// holding any locks.
@@ -1015,20 +1101,8 @@
WaitForGcToCompleteLocked(kGcCauseTrim, self);
collector_type_running_ = kCollectorTypeHeapTrim;
}
- // Trim reference tables.
- {
- ScopedObjectAccess soa(self);
- JavaVMExt* vm = soa.Vm();
- // Trim globals indirect reference table.
- vm->TrimGlobals();
- // Trim locals indirect reference tables.
- Barrier barrier(0);
- TrimIndirectReferenceTableClosure closure(&barrier);
- ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
- size_t barrier_count = Runtime::Current()->GetThreadList()->RunCheckpoint(&closure);
- barrier.Increment(self, barrier_count);
- }
- uint64_t start_ns = NanoTime();
+ ATRACE_BEGIN(__FUNCTION__);
+ const uint64_t start_ns = NanoTime();
// Trim the managed spaces.
uint64_t total_alloc_space_allocated = 0;
uint64_t total_alloc_space_size = 0;
@@ -1051,6 +1125,9 @@
if (bump_pointer_space_ != nullptr) {
total_alloc_space_allocated -= bump_pointer_space_->Size();
}
+ if (region_space_ != nullptr) {
+ total_alloc_space_allocated -= region_space_->GetBytesAllocated();
+ }
const float managed_utilization = static_cast<float>(total_alloc_space_allocated) /
static_cast<float>(total_alloc_space_size);
uint64_t gc_heap_end_ns = NanoTime();
@@ -1078,6 +1155,7 @@
<< PrettyDuration(end_ns - gc_heap_end_ns) << ", advised=" << PrettySize(native_reclaimed)
<< ") heaps. Managed heap utilization of " << static_cast<int>(100 * managed_utilization)
<< "%.";
+ ATRACE_END();
}
bool Heap::IsValidObjectAddress(const mirror::Object* obj) const {
@@ -1122,6 +1200,9 @@
// a GC). When a GC isn't running End() - Begin() is 0 which means no objects are contained.
return temp_space_->Contains(obj);
}
+ if (region_space_ != nullptr && region_space_->HasAddress(obj)) {
+ return true;
+ }
space::ContinuousSpace* c_space = FindContinuousSpaceFromObject(obj, true);
space::DiscontinuousSpace* d_space = nullptr;
if (c_space != nullptr) {
@@ -1471,10 +1552,7 @@
void Heap::CountInstances(const std::vector<mirror::Class*>& classes, bool use_is_assignable_from,
uint64_t* counts) {
- // Can't do any GC in this function since this may move classes.
- ScopedAssertNoThreadSuspension ants(Thread::Current(), "CountInstances");
InstanceCounter counter(classes, use_is_assignable_from, counts);
- ReaderMutexLock mu(ants.Self(), *Locks::heap_bitmap_lock_);
VisitObjects(InstanceCounter::Callback, &counter);
}
@@ -1505,10 +1583,7 @@
void Heap::GetInstances(mirror::Class* c, int32_t max_count,
std::vector<mirror::Object*>& instances) {
- // Can't do any GC in this function since this may move classes.
- ScopedAssertNoThreadSuspension ants(Thread::Current(), "GetInstances");
InstanceCollector collector(c, max_count, instances);
- ReaderMutexLock mu(ants.Self(), *Locks::heap_bitmap_lock_);
VisitObjects(&InstanceCollector::Callback, &collector);
}
@@ -1550,10 +1625,7 @@
void Heap::GetReferringObjects(mirror::Object* o, int32_t max_count,
std::vector<mirror::Object*>& referring_objects) {
- // Can't do any GC in this function since this may move the object o.
- ScopedAssertNoThreadSuspension ants(Thread::Current(), "GetReferringObjects");
ReferringObjectsFinder finder(o, max_count, referring_objects);
- ReaderMutexLock mu(ants.Self(), *Locks::heap_bitmap_lock_);
VisitObjects(&ReferringObjectsFinder::Callback, &finder);
}
@@ -1604,8 +1676,6 @@
// Make sure that we will have enough room to copy.
CHECK_GE(to_space->GetFootprintLimit(), from_space->GetFootprintLimit());
Compact(to_space, from_space, kGcCauseHomogeneousSpaceCompact);
- // Leave as prot read so that we can still run ROSAlloc verification on this space.
- from_space->GetMemMap()->Protect(PROT_READ);
const uint64_t space_size_after_compaction = to_space->Size();
main_space_ = to_space;
main_space_backup_.reset(from_space);
@@ -1628,7 +1698,6 @@
return HomogeneousSpaceCompactResult::kSuccess;
}
-
void Heap::TransitionCollector(CollectorType collector_type) {
if (collector_type == collector_type_) {
return;
@@ -1780,7 +1849,15 @@
collector_type_ = collector_type;
gc_plan_.clear();
switch (collector_type_) {
- case kCollectorTypeCC: // Fall-through.
+ case kCollectorTypeCC: {
+ gc_plan_.push_back(collector::kGcTypeFull);
+ if (use_tlab_) {
+ ChangeAllocator(kAllocatorTypeRegionTLAB);
+ } else {
+ ChangeAllocator(kAllocatorTypeRegion);
+ }
+ break;
+ }
case kCollectorTypeMC: // Fall-through.
case kCollectorTypeSS: // Fall-through.
case kCollectorTypeGSS: {
@@ -1963,7 +2040,11 @@
// Compact the bump pointer space to a new zygote bump pointer space.
bool reset_main_space = false;
if (IsMovingGc(collector_type_)) {
- zygote_collector.SetFromSpace(bump_pointer_space_);
+ if (collector_type_ == kCollectorTypeCC) {
+ zygote_collector.SetFromSpace(region_space_);
+ } else {
+ zygote_collector.SetFromSpace(bump_pointer_space_);
+ }
} else {
CHECK(main_space_ != nullptr);
// Copy from the main space.
@@ -1984,7 +2065,11 @@
delete old_main_space;
AddSpace(main_space_);
} else {
- bump_pointer_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
+ if (collector_type_ == kCollectorTypeCC) {
+ region_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
+ } else {
+ bump_pointer_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
+ }
}
if (temp_space_ != nullptr) {
CHECK(temp_space_->IsEmpty());
@@ -2059,9 +2144,9 @@
accounting::ObjectStack* stack) {
DCHECK(bitmap1 != nullptr);
DCHECK(bitmap2 != nullptr);
- mirror::Object** limit = stack->End();
- for (mirror::Object** it = stack->Begin(); it != limit; ++it) {
- const mirror::Object* obj = *it;
+ const auto* limit = stack->End();
+ for (auto* it = stack->Begin(); it != limit; ++it) {
+ const mirror::Object* obj = it->AsMirrorPtr();
if (!kUseThreadLocalAllocationStack || obj != nullptr) {
if (bitmap1->HasAddress(obj)) {
bitmap1->Set(obj);
@@ -2119,7 +2204,9 @@
ScopedThreadStateChange tsc(self, kWaitingPerformingGc);
Locks::mutator_lock_->AssertNotHeld(self);
if (self->IsHandlingStackOverflow()) {
- LOG(WARNING) << "Performing GC on a thread that is handling a stack overflow.";
+ // If we are throwing a stack overflow error we probably don't have enough remaining stack
+ // space to run the GC.
+ return collector::kGcTypeNone;
}
bool compacting_gc;
{
@@ -2141,16 +2228,9 @@
++runtime->GetStats()->gc_for_alloc_count;
++self->GetStats()->gc_for_alloc_count;
}
- uint64_t gc_start_time_ns = NanoTime();
- uint64_t gc_start_size = GetBytesAllocated();
- // Approximate allocation rate in bytes / second.
- uint64_t ms_delta = NsToMs(gc_start_time_ns - last_gc_time_ns_);
- // Back to back GCs can cause 0 ms of wait time in between GC invocations.
- if (LIKELY(ms_delta != 0)) {
- allocation_rate_ = ((gc_start_size - last_gc_size_) * 1000) / ms_delta;
- ATRACE_INT("Allocation rate KB/s", allocation_rate_ / KB);
- VLOG(heap) << "Allocation rate: " << PrettySize(allocation_rate_) << "/s";
- }
+ const uint64_t bytes_allocated_before_gc = GetBytesAllocated();
+ // Approximate heap size.
+ ATRACE_INT("Heap size (KB)", bytes_allocated_before_gc / KB);
DCHECK_LT(gc_type, collector::kGcTypeMax);
DCHECK_NE(gc_type, collector::kGcTypeNone);
@@ -2159,7 +2239,9 @@
// TODO: Clean this up.
if (compacting_gc) {
DCHECK(current_allocator_ == kAllocatorTypeBumpPointer ||
- current_allocator_ == kAllocatorTypeTLAB);
+ current_allocator_ == kAllocatorTypeTLAB ||
+ current_allocator_ == kAllocatorTypeRegion ||
+ current_allocator_ == kAllocatorTypeRegionTLAB);
switch (collector_type_) {
case kCollectorTypeSS:
// Fall-through.
@@ -2170,6 +2252,7 @@
collector = semi_space_collector_;
break;
case kCollectorTypeCC:
+ concurrent_copying_collector_->SetRegionSpace(region_space_);
collector = concurrent_copying_collector_;
break;
case kCollectorTypeMC:
@@ -2179,7 +2262,7 @@
default:
LOG(FATAL) << "Invalid collector type " << static_cast<size_t>(collector_type_);
}
- if (collector != mark_compact_collector_) {
+ if (collector != mark_compact_collector_ && collector != concurrent_copying_collector_) {
temp_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
CHECK(temp_space_->IsEmpty());
}
@@ -2203,11 +2286,11 @@
collector->Run(gc_cause, clear_soft_references || runtime->IsZygote());
total_objects_freed_ever_ += GetCurrentGcIteration()->GetFreedObjects();
total_bytes_freed_ever_ += GetCurrentGcIteration()->GetFreedBytes();
- RequestHeapTrim();
+ RequestTrim(self);
// Enqueue cleared references.
reference_processor_.EnqueueClearedReferences(self);
// Grow the heap so that we know when to perform the next GC.
- GrowForUtilization(collector);
+ GrowForUtilization(collector, bytes_allocated_before_gc);
const size_t duration = GetCurrentGcIteration()->GetDurationNs();
const std::vector<uint64_t>& pause_times = GetCurrentGcIteration()->GetPauseTimes();
// Print the GC if it is an explicit GC (e.g. Runtime.gc()) or a slow GC
@@ -2256,8 +2339,8 @@
gc_complete_cond_->Broadcast(self);
}
-static void RootMatchesObjectVisitor(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
- RootType /*root_type*/) {
+static void RootMatchesObjectVisitor(mirror::Object** root, void* arg,
+ const RootInfo& /*root_info*/) {
mirror::Object* obj = reinterpret_cast<mirror::Object*>(arg);
if (*root == obj) {
LOG(INFO) << "Object " << obj << " is a root";
@@ -2299,12 +2382,12 @@
return heap_->IsLiveObjectLocked(obj, true, false, true);
}
- static void VerifyRootCallback(mirror::Object** root, void* arg, uint32_t thread_id,
- RootType root_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ static void VerifyRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
VerifyReferenceVisitor* visitor = reinterpret_cast<VerifyReferenceVisitor*>(arg);
if (!visitor->VerifyReference(nullptr, *root, MemberOffset(0))) {
LOG(ERROR) << "Root " << *root << " is dead with type " << PrettyTypeOf(*root)
- << " thread_id= " << thread_id << " root_type= " << root_type;
+ << " thread_id= " << root_info.GetThreadId() << " root_type= " << root_info.GetType();
}
}
@@ -2390,8 +2473,8 @@
// Attempt to see if the card table missed the reference.
ScanVisitor scan_visitor;
uint8_t* byte_cover_begin = reinterpret_cast<uint8_t*>(card_table->AddrFromCard(card_addr));
- card_table->Scan(bitmap, byte_cover_begin,
- byte_cover_begin + accounting::CardTable::kCardSize, scan_visitor);
+ card_table->Scan<false>(bitmap, byte_cover_begin,
+ byte_cover_begin + accounting::CardTable::kCardSize, scan_visitor);
}
// Search to see if any of the roots reference our object.
@@ -2460,8 +2543,8 @@
void Heap::PushOnThreadLocalAllocationStackWithInternalGC(Thread* self, mirror::Object** obj) {
// Slow path, the allocation stack push back must have already failed.
DCHECK(!self->PushOnThreadLocalAllocationStack(*obj));
- mirror::Object** start_address;
- mirror::Object** end_address;
+ StackReference<mirror::Object>* start_address;
+ StackReference<mirror::Object>* end_address;
while (!allocation_stack_->AtomicBumpBack(kThreadLocalAllocationStackSize, &start_address,
&end_address)) {
// TODO: Add handle VerifyObject.
@@ -2496,7 +2579,7 @@
// 2. Allocated during the GC (pre sweep GC verification).
// We don't want to verify the objects in the live stack since they themselves may be
// pointing to dead objects if they are not reachable.
- VisitObjects(VerifyObjectVisitor::VisitCallback, &visitor);
+ VisitObjectsPaused(VerifyObjectVisitor::VisitCallback, &visitor);
// Verify the roots:
Runtime::Current()->VisitRoots(VerifyReferenceVisitor::VerifyRootCallback, &visitor);
if (visitor.GetFailureCount() > 0) {
@@ -2620,9 +2703,9 @@
VerifyLiveStackReferences visitor(this);
GetLiveBitmap()->Visit(visitor);
// We can verify objects in the live stack since none of these should reference dead objects.
- for (mirror::Object** it = live_stack_->Begin(); it != live_stack_->End(); ++it) {
- if (!kUseThreadLocalAllocationStack || *it != nullptr) {
- visitor(*it);
+ for (auto* it = live_stack_->Begin(); it != live_stack_->End(); ++it) {
+ if (!kUseThreadLocalAllocationStack || it->AsMirrorPtr() != nullptr) {
+ visitor(it->AsMirrorPtr());
}
}
return !visitor.Failed();
@@ -2638,7 +2721,7 @@
void Heap::RevokeAllThreadLocalAllocationStacks(Thread* self) {
// This must be called only during the pause.
- CHECK(Locks::mutator_lock_->IsExclusiveHeld(self));
+ DCHECK(Locks::mutator_lock_->IsExclusiveHeld(self));
MutexLock mu(self, *Locks::runtime_shutdown_lock_);
MutexLock mu2(self, *Locks::thread_list_lock_);
std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
@@ -2682,7 +2765,8 @@
return it->second;
}
-void Heap::ProcessCards(TimingLogger* timings, bool use_rem_sets) {
+void Heap::ProcessCards(TimingLogger* timings, bool use_rem_sets, bool process_alloc_space_cards,
+ bool clear_alloc_space_cards) {
TimingLogger::ScopedTiming t(__FUNCTION__, timings);
// Clear cards and keep track of cards cleared in the mod-union table.
for (const auto& space : continuous_spaces_) {
@@ -2698,17 +2782,21 @@
<< static_cast<int>(collector_type_);
TimingLogger::ScopedTiming t2("AllocSpaceRemSetClearCards", timings);
rem_set->ClearCards();
- } else if (space->GetType() != space::kSpaceTypeBumpPointerSpace) {
+ } else if (process_alloc_space_cards) {
TimingLogger::ScopedTiming t2("AllocSpaceClearCards", timings);
- // No mod union table for the AllocSpace. Age the cards so that the GC knows that these cards
- // were dirty before the GC started.
- // TODO: Need to use atomic for the case where aged(cleaning thread) -> dirty(other thread)
- // -> clean(cleaning thread).
- // The races are we either end up with: Aged card, unaged card. Since we have the checkpoint
- // roots and then we scan / update mod union tables after. We will always scan either card.
- // If we end up with the non aged card, we scan it it in the pause.
- card_table_->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(),
- VoidFunctor());
+ if (clear_alloc_space_cards) {
+ card_table_->ClearCardRange(space->Begin(), space->End());
+ } else {
+ // No mod union table for the AllocSpace. Age the cards so that the GC knows that these
+ // cards were dirty before the GC started.
+ // TODO: Need to use atomic for the case where aged(cleaning thread) -> dirty(other thread)
+ // -> clean(cleaning thread).
+ // The races are we either end up with: Aged card, unaged card. Since we have the
+ // checkpoint roots and then we scan / update mod union tables after. We will always
+ // scan either card. If we end up with the non aged card, we scan it it in the pause.
+ card_table_->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(),
+ VoidFunctor());
+ }
}
}
}
@@ -2722,7 +2810,6 @@
TimingLogger::ScopedTiming t(__FUNCTION__, timings);
if (verify_pre_gc_heap_) {
TimingLogger::ScopedTiming t2("(Paused)PreGcVerifyHeapReferences", timings);
- ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
size_t failures = VerifyHeapReferences();
if (failures > 0) {
LOG(FATAL) << "Pre " << gc->GetName() << " heap verification failed with " << failures
@@ -2774,9 +2861,11 @@
if (verify_pre_sweeping_heap_) {
TimingLogger::ScopedTiming t2("(Paused)PostSweepingVerifyHeapReferences", timings);
CHECK_NE(self->GetState(), kRunnable);
- WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
- // Swapping bound bitmaps does nothing.
- gc->SwapBitmaps();
+ {
+ WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ // Swapping bound bitmaps does nothing.
+ gc->SwapBitmaps();
+ }
// Pass in false since concurrent reference processing can mean that the reference referents
// may point to dead objects at the point which PreSweepingGcVerification is called.
size_t failures = VerifyHeapReferences(false);
@@ -2784,7 +2873,10 @@
LOG(FATAL) << "Pre sweeping " << gc->GetName() << " GC verification failed with " << failures
<< " failures";
}
- gc->SwapBitmaps();
+ {
+ WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ gc->SwapBitmaps();
+ }
}
if (verify_pre_sweeping_rosalloc_) {
RosAllocVerification(timings, "PreSweepingRosAllocVerification");
@@ -2806,7 +2898,6 @@
}
if (verify_post_gc_heap_) {
TimingLogger::ScopedTiming t2("(Paused)PostGcVerifyHeapReferences", timings);
- ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
size_t failures = VerifyHeapReferences();
if (failures > 0) {
LOG(FATAL) << "Pre " << gc->GetName() << " heap verification failed with " << failures
@@ -2917,25 +3008,24 @@
return foreground_heap_growth_multiplier_;
}
-void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran) {
+void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran,
+ uint64_t bytes_allocated_before_gc) {
// We know what our utilization is at this moment.
// This doesn't actually resize any memory. It just lets the heap grow more when necessary.
const uint64_t bytes_allocated = GetBytesAllocated();
- last_gc_size_ = bytes_allocated;
- last_gc_time_ns_ = NanoTime();
uint64_t target_size;
collector::GcType gc_type = collector_ran->GetGcType();
+ const double multiplier = HeapGrowthMultiplier(); // Use the multiplier to grow more for
+ // foreground.
+ const uint64_t adjusted_min_free = static_cast<uint64_t>(min_free_ * multiplier);
+ const uint64_t adjusted_max_free = static_cast<uint64_t>(max_free_ * multiplier);
if (gc_type != collector::kGcTypeSticky) {
// Grow the heap for non sticky GC.
- const float multiplier = HeapGrowthMultiplier(); // Use the multiplier to grow more for
- // foreground.
- intptr_t delta = bytes_allocated / GetTargetHeapUtilization() - bytes_allocated;
+ ssize_t delta = bytes_allocated / GetTargetHeapUtilization() - bytes_allocated;
CHECK_GE(delta, 0);
target_size = bytes_allocated + delta * multiplier;
- target_size = std::min(target_size,
- bytes_allocated + static_cast<uint64_t>(max_free_ * multiplier));
- target_size = std::max(target_size,
- bytes_allocated + static_cast<uint64_t>(min_free_ * multiplier));
+ target_size = std::min(target_size, bytes_allocated + adjusted_max_free);
+ target_size = std::max(target_size, bytes_allocated + adjusted_min_free);
native_need_to_run_finalization_ = true;
next_gc_type_ = collector::kGcTypeSticky;
} else {
@@ -2957,8 +3047,8 @@
next_gc_type_ = non_sticky_gc_type;
}
// If we have freed enough memory, shrink the heap back down.
- if (bytes_allocated + max_free_ < max_allowed_footprint_) {
- target_size = bytes_allocated + max_free_;
+ if (bytes_allocated + adjusted_max_free < max_allowed_footprint_) {
+ target_size = bytes_allocated + adjusted_max_free;
} else {
target_size = std::max(bytes_allocated, static_cast<uint64_t>(max_allowed_footprint_));
}
@@ -2966,11 +3056,18 @@
if (!ignore_max_footprint_) {
SetIdealFootprint(target_size);
if (IsGcConcurrent()) {
+ const uint64_t freed_bytes = current_gc_iteration_.GetFreedBytes() +
+ current_gc_iteration_.GetFreedLargeObjectBytes();
+ // Bytes allocated will shrink by freed_bytes after the GC runs, so if we want to figure out
+ // how many bytes were allocated during the GC we need to add freed_bytes back on.
+ CHECK_GE(bytes_allocated + freed_bytes, bytes_allocated_before_gc);
+ const uint64_t bytes_allocated_during_gc = bytes_allocated + freed_bytes -
+ bytes_allocated_before_gc;
// Calculate when to perform the next ConcurrentGC.
// Calculate the estimated GC duration.
const double gc_duration_seconds = NsToMs(current_gc_iteration_.GetDurationNs()) / 1000.0;
// Estimate how many remaining bytes we will have when we need to start the next GC.
- size_t remaining_bytes = allocation_rate_ * gc_duration_seconds;
+ size_t remaining_bytes = bytes_allocated_during_gc * gc_duration_seconds;
remaining_bytes = std::min(remaining_bytes, kMaxConcurrentRemainingBytes);
remaining_bytes = std::max(remaining_bytes, kMinConcurrentRemainingBytes);
if (UNLIKELY(remaining_bytes > max_allowed_footprint_)) {
@@ -2990,6 +3087,20 @@
}
}
+void Heap::ClampGrowthLimit() {
+ capacity_ = growth_limit_;
+ for (const auto& space : continuous_spaces_) {
+ if (space->IsMallocSpace()) {
+ gc::space::MallocSpace* malloc_space = space->AsMallocSpace();
+ malloc_space->ClampGrowthLimit();
+ }
+ }
+ // This space isn't added for performance reasons.
+ if (main_space_backup_.get() != nullptr) {
+ main_space_backup_->ClampGrowthLimit();
+ }
+}
+
void Heap::ClearGrowthLimit() {
growth_limit_ = capacity_;
for (const auto& space : continuous_spaces_) {
@@ -3022,57 +3133,109 @@
RequestConcurrentGC(self);
}
-void Heap::RequestConcurrentGC(Thread* self) {
- // Make sure that we can do a concurrent GC.
- Runtime* runtime = Runtime::Current();
- if (runtime == nullptr || !runtime->IsFinishedStarting() || runtime->IsShuttingDown(self) ||
- self->IsHandlingStackOverflow()) {
- return;
+class Heap::ConcurrentGCTask : public HeapTask {
+ public:
+ explicit ConcurrentGCTask(uint64_t target_time) : HeapTask(target_time) { }
+ virtual void Run(Thread* self) OVERRIDE {
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ heap->ConcurrentGC(self);
+ heap->ClearConcurrentGCRequest();
}
- JNIEnv* env = self->GetJniEnv();
- DCHECK(WellKnownClasses::java_lang_Daemons != nullptr);
- DCHECK(WellKnownClasses::java_lang_Daemons_requestGC != nullptr);
- env->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons,
- WellKnownClasses::java_lang_Daemons_requestGC);
- CHECK(!env->ExceptionCheck());
+};
+
+static bool CanAddHeapTask(Thread* self) LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_) {
+ Runtime* runtime = Runtime::Current();
+ return runtime != nullptr && runtime->IsFinishedStarting() && !runtime->IsShuttingDown(self) &&
+ !self->IsHandlingStackOverflow();
+}
+
+void Heap::ClearConcurrentGCRequest() {
+ concurrent_gc_pending_.StoreRelaxed(false);
+}
+
+void Heap::RequestConcurrentGC(Thread* self) {
+ if (CanAddHeapTask(self) &&
+ concurrent_gc_pending_.CompareExchangeStrongSequentiallyConsistent(false, true)) {
+ task_processor_->AddTask(self, new ConcurrentGCTask(NanoTime())); // Start straight away.
+ }
}
void Heap::ConcurrentGC(Thread* self) {
- if (Runtime::Current()->IsShuttingDown(self)) {
- return;
- }
- // Wait for any GCs currently running to finish.
- if (WaitForGcToComplete(kGcCauseBackground, self) == collector::kGcTypeNone) {
- // If the we can't run the GC type we wanted to run, find the next appropriate one and try that
- // instead. E.g. can't do partial, so do full instead.
- if (CollectGarbageInternal(next_gc_type_, kGcCauseBackground, false) ==
- collector::kGcTypeNone) {
- for (collector::GcType gc_type : gc_plan_) {
- // Attempt to run the collector, if we succeed, we are done.
- if (gc_type > next_gc_type_ &&
- CollectGarbageInternal(gc_type, kGcCauseBackground, false) != collector::kGcTypeNone) {
- break;
+ if (!Runtime::Current()->IsShuttingDown(self)) {
+ // Wait for any GCs currently running to finish.
+ if (WaitForGcToComplete(kGcCauseBackground, self) == collector::kGcTypeNone) {
+ // If the we can't run the GC type we wanted to run, find the next appropriate one and try that
+ // instead. E.g. can't do partial, so do full instead.
+ if (CollectGarbageInternal(next_gc_type_, kGcCauseBackground, false) ==
+ collector::kGcTypeNone) {
+ for (collector::GcType gc_type : gc_plan_) {
+ // Attempt to run the collector, if we succeed, we are done.
+ if (gc_type > next_gc_type_ &&
+ CollectGarbageInternal(gc_type, kGcCauseBackground, false) !=
+ collector::kGcTypeNone) {
+ break;
+ }
}
}
}
}
}
-void Heap::RequestCollectorTransition(CollectorType desired_collector_type, uint64_t delta_time) {
- Thread* self = Thread::Current();
- {
- MutexLock mu(self, *heap_trim_request_lock_);
- if (desired_collector_type_ == desired_collector_type) {
- return;
- }
- heap_transition_or_trim_target_time_ =
- std::max(heap_transition_or_trim_target_time_, NanoTime() + delta_time);
- desired_collector_type_ = desired_collector_type;
+class Heap::CollectorTransitionTask : public HeapTask {
+ public:
+ explicit CollectorTransitionTask(uint64_t target_time) : HeapTask(target_time) { }
+ virtual void Run(Thread* self) OVERRIDE {
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ heap->DoPendingCollectorTransition();
+ heap->ClearPendingCollectorTransition(self);
}
- SignalHeapTrimDaemon(self);
+};
+
+void Heap::ClearPendingCollectorTransition(Thread* self) {
+ MutexLock mu(self, *pending_task_lock_);
+ pending_collector_transition_ = nullptr;
}
-void Heap::RequestHeapTrim() {
+void Heap::RequestCollectorTransition(CollectorType desired_collector_type, uint64_t delta_time) {
+ Thread* self = Thread::Current();
+ desired_collector_type_ = desired_collector_type;
+ if (desired_collector_type_ == collector_type_ || !CanAddHeapTask(self)) {
+ return;
+ }
+ CollectorTransitionTask* added_task = nullptr;
+ const uint64_t target_time = NanoTime() + delta_time;
+ {
+ MutexLock mu(self, *pending_task_lock_);
+ // If we have an existing collector transition, update the targe time to be the new target.
+ if (pending_collector_transition_ != nullptr) {
+ task_processor_->UpdateTargetRunTime(self, pending_collector_transition_, target_time);
+ return;
+ }
+ added_task = new CollectorTransitionTask(target_time);
+ pending_collector_transition_ = added_task;
+ }
+ task_processor_->AddTask(self, added_task);
+}
+
+class Heap::HeapTrimTask : public HeapTask {
+ public:
+ explicit HeapTrimTask(uint64_t delta_time) : HeapTask(NanoTime() + delta_time) { }
+ virtual void Run(Thread* self) OVERRIDE {
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ heap->Trim(self);
+ heap->ClearPendingTrim(self);
+ }
+};
+
+void Heap::ClearPendingTrim(Thread* self) {
+ MutexLock mu(self, *pending_task_lock_);
+ pending_heap_trim_ = nullptr;
+}
+
+void Heap::RequestTrim(Thread* self) {
+ if (!CanAddHeapTask(self)) {
+ return;
+ }
// GC completed and now we must decide whether to request a heap trim (advising pages back to the
// kernel) or not. Issuing a request will also cause trimming of the libc heap. As a trim scans
// a space it will hold its lock and can become a cause of jank.
@@ -3085,42 +3248,17 @@
// to utilization (which is probably inversely proportional to how much benefit we can expect).
// We could try mincore(2) but that's only a measure of how many pages we haven't given away,
// not how much use we're making of those pages.
-
- Thread* self = Thread::Current();
- Runtime* runtime = Runtime::Current();
- if (runtime == nullptr || !runtime->IsFinishedStarting() || runtime->IsShuttingDown(self) ||
- runtime->IsZygote()) {
- // Ignore the request if we are the zygote to prevent app launching lag due to sleep in heap
- // trimmer daemon. b/17310019
- // Heap trimming isn't supported without a Java runtime or Daemons (such as at dex2oat time)
- // Also: we do not wish to start a heap trim if the runtime is shutting down (a racy check
- // as we don't hold the lock while requesting the trim).
- return;
- }
+ HeapTrimTask* added_task = nullptr;
{
- MutexLock mu(self, *heap_trim_request_lock_);
- if (last_trim_time_ + kHeapTrimWait >= NanoTime()) {
- // We have done a heap trim in the last kHeapTrimWait nanosecs, don't request another one
- // just yet.
+ MutexLock mu(self, *pending_task_lock_);
+ if (pending_heap_trim_ != nullptr) {
+ // Already have a heap trim request in task processor, ignore this request.
return;
}
- heap_trim_request_pending_ = true;
- uint64_t current_time = NanoTime();
- if (heap_transition_or_trim_target_time_ < current_time) {
- heap_transition_or_trim_target_time_ = current_time + kHeapTrimWait;
- }
+ added_task = new HeapTrimTask(kHeapTrimWait);
+ pending_heap_trim_ = added_task;
}
- // Notify the daemon thread which will actually do the heap trim.
- SignalHeapTrimDaemon(self);
-}
-
-void Heap::SignalHeapTrimDaemon(Thread* self) {
- JNIEnv* env = self->GetJniEnv();
- DCHECK(WellKnownClasses::java_lang_Daemons != nullptr);
- DCHECK(WellKnownClasses::java_lang_Daemons_requestHeapTrim != nullptr);
- env->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons,
- WellKnownClasses::java_lang_Daemons_requestHeapTrim);
- CHECK(!env->ExceptionCheck());
+ task_processor_->AddTask(self, added_task);
}
void Heap::RevokeThreadLocalBuffers(Thread* thread) {
@@ -3130,6 +3268,9 @@
if (bump_pointer_space_ != nullptr) {
bump_pointer_space_->RevokeThreadLocalBuffers(thread);
}
+ if (region_space_ != nullptr) {
+ region_space_->RevokeThreadLocalBuffers(thread);
+ }
}
void Heap::RevokeRosAllocThreadLocalBuffers(Thread* thread) {
@@ -3145,10 +3286,13 @@
if (bump_pointer_space_ != nullptr) {
bump_pointer_space_->RevokeAllThreadLocalBuffers();
}
+ if (region_space_ != nullptr) {
+ region_space_->RevokeAllThreadLocalBuffers();
+ }
}
bool Heap::IsGCRequestPending() const {
- return concurrent_start_bytes_ != std::numeric_limits<size_t>::max();
+ return concurrent_gc_pending_.LoadRelaxed();
}
void Heap::RunFinalization(JNIEnv* env) {
@@ -3230,7 +3374,7 @@
}
void Heap::CheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count) {
- CHECK(c == NULL || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) ||
+ CHECK(c == nullptr || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) ||
(c->IsVariableSize() || c->GetObjectSize() == byte_count));
CHECK_GE(byte_count, sizeof(mirror::Object));
}
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 4e1a0ff..57c1460 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -27,6 +27,7 @@
#include "base/timing_logger.h"
#include "gc/accounting/atomic_stack.h"
#include "gc/accounting/card_table.h"
+#include "gc/accounting/read_barrier_table.h"
#include "gc/gc_cause.h"
#include "gc/collector/garbage_collector.h"
#include "gc/collector/gc_type.h"
@@ -57,6 +58,7 @@
namespace gc {
class ReferenceProcessor;
+class TaskProcessor;
namespace accounting {
class HeapBitmap;
@@ -85,6 +87,7 @@
class ImageSpace;
class LargeObjectSpace;
class MallocSpace;
+ class RegionSpace;
class RosAllocSpace;
class Space;
class SpaceTest;
@@ -142,15 +145,14 @@
static constexpr double kDefaultHeapGrowthMultiplier = 2.0;
// Primitive arrays larger than this size are put in the large object space.
static constexpr size_t kDefaultLargeObjectThreshold = 3 * kPageSize;
+
// Whether or not we use the free list large object space. Only use it if USE_ART_LOW_4G_ALLOCATOR
// since this means that we have to use the slow msync loop in MemMap::MapAnonymous.
-#if USE_ART_LOW_4G_ALLOCATOR
static constexpr space::LargeObjectSpaceType kDefaultLargeObjectSpaceType =
- space::kLargeObjectSpaceTypeFreeList;
-#else
- static constexpr space::LargeObjectSpaceType kDefaultLargeObjectSpaceType =
- space::kLargeObjectSpaceTypeMap;
-#endif
+ USE_ART_LOW_4G_ALLOCATOR ?
+ space::LargeObjectSpaceType::kFreeList
+ : space::LargeObjectSpaceType::kMap;
+
// Used so that we don't overflow the allocation time atomic integer.
static constexpr size_t kTimeAdjust = 1024;
@@ -215,7 +217,11 @@
// Visit all of the live objects in the heap.
void VisitObjects(ObjectCallback callback, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
+ void VisitObjectsPaused(ObjectCallback callback, void* arg)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
+ LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
void CheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -244,7 +250,7 @@
void VerifyHeap() LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
// Returns how many failures occured.
size_t VerifyHeapReferences(bool verify_referents = true)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
bool VerifyMissingCardMarks()
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
@@ -301,6 +307,10 @@
// implement dalvik.system.VMRuntime.clearGrowthLimit.
void ClearGrowthLimit();
+ // Make the current growth limit the new maximum capacity, unmaps pages at the end of spaces
+ // which will never be used. Used to implement dalvik.system.VMRuntime.clampGrowthLimit.
+ void ClampGrowthLimit();
+
// Target ideal heap utilization ratio, implements
// dalvik.system.VMRuntime.getTargetHeapUtilization.
double GetTargetHeapUtilization() const {
@@ -401,6 +411,10 @@
return card_table_.get();
}
+ accounting::ReadBarrierTable* GetReadBarrierTable() const {
+ return rb_table_.get();
+ }
+
void AddFinalizerReference(Thread* self, mirror::Object** object);
// Returns the number of bytes currently allocated.
@@ -470,11 +484,11 @@
void DumpForSigQuit(std::ostream& os);
- // Do a pending heap transition or trim.
- void DoPendingTransitionOrTrim() LOCKS_EXCLUDED(heap_trim_request_lock_);
+ // Do a pending collector transition.
+ void DoPendingCollectorTransition();
- // Trim the managed and native heaps by releasing unused memory back to the OS.
- void Trim() LOCKS_EXCLUDED(heap_trim_request_lock_);
+ // Deflate monitors, ... and trim the spaces.
+ void Trim(Thread* self) LOCKS_EXCLUDED(gc_complete_lock_);
void RevokeThreadLocalBuffers(Thread* thread);
void RevokeRosAllocThreadLocalBuffers(Thread* thread);
@@ -500,6 +514,7 @@
// Mark and empty stack.
void FlushAllocStack()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
// Revoke all the thread-local allocation stacks.
@@ -513,10 +528,12 @@
accounting::SpaceBitmap<kObjectAlignment>* bitmap2,
accounting::SpaceBitmap<kLargeObjectAlignment>* large_objects,
accounting::ObjectStack* stack)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
// Mark the specified allocation stack as live.
void MarkAllocStackAsLive(accounting::ObjectStack* stack)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
// Unbind any bound bitmaps.
@@ -606,12 +623,49 @@
ReferenceProcessor* GetReferenceProcessor() {
return &reference_processor_;
}
+ TaskProcessor* GetTaskProcessor() {
+ return task_processor_.get();
+ }
bool HasZygoteSpace() const {
return zygote_space_ != nullptr;
}
+ collector::ConcurrentCopying* ConcurrentCopyingCollector() {
+ return concurrent_copying_collector_;
+ }
+
+ CollectorType CurrentCollectorType() {
+ return collector_type_;
+ }
+
+ bool IsGcConcurrentAndMoving() const {
+ if (IsGcConcurrent() && IsMovingGc(collector_type_)) {
+ // Assume no transition when a concurrent moving collector is used.
+ DCHECK_EQ(collector_type_, foreground_collector_type_);
+ DCHECK_EQ(foreground_collector_type_, background_collector_type_)
+ << "Assume no transition such that collector_type_ won't change";
+ return true;
+ }
+ return false;
+ }
+
+ bool IsMovingGCDisabled(Thread* self) {
+ MutexLock mu(self, *gc_complete_lock_);
+ return disable_moving_gc_count_ > 0;
+ }
+
+ // Request an asynchronous trim.
+ void RequestTrim(Thread* self) LOCKS_EXCLUDED(pending_task_lock_);
+
+ // Request asynchronous GC.
+ void RequestConcurrentGC(Thread* self) LOCKS_EXCLUDED(pending_task_lock_);
+
private:
+ class ConcurrentGCTask;
+ class CollectorTransitionTask;
+ class HeapTrimTask;
+
// Compact source space to target space.
void Compact(space::ContinuousMemMapAllocSpace* target_space,
space::ContinuousMemMapAllocSpace* source_space,
@@ -632,10 +686,14 @@
static ALWAYS_INLINE bool AllocatorHasAllocationStack(AllocatorType allocator_type) {
return
allocator_type != kAllocatorTypeBumpPointer &&
- allocator_type != kAllocatorTypeTLAB;
+ allocator_type != kAllocatorTypeTLAB &&
+ allocator_type != kAllocatorTypeRegion &&
+ allocator_type != kAllocatorTypeRegionTLAB;
}
static ALWAYS_INLINE bool AllocatorMayHaveConcurrentGC(AllocatorType allocator_type) {
- return AllocatorHasAllocationStack(allocator_type);
+ return
+ allocator_type != kAllocatorTypeBumpPointer &&
+ allocator_type != kAllocatorTypeTLAB;
}
static bool IsMovingGc(CollectorType collector_type) {
return collector_type == kCollectorTypeSS || collector_type == kCollectorTypeGSS ||
@@ -702,12 +760,10 @@
EXCLUSIVE_LOCKS_REQUIRED(gc_complete_lock_);
void RequestCollectorTransition(CollectorType desired_collector_type, uint64_t delta_time)
- LOCKS_EXCLUDED(heap_trim_request_lock_);
- void RequestHeapTrim() LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_);
+ LOCKS_EXCLUDED(pending_task_lock_);
+
void RequestConcurrentGCAndSaveObject(Thread* self, mirror::Object** obj)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void RequestConcurrentGC(Thread* self)
- LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_);
bool IsGCRequestPending() const;
// Sometimes CollectGarbageInternal decides to run a different Gc than you requested. Returns
@@ -725,7 +781,8 @@
void PrePauseRosAllocVerification(collector::GarbageCollector* gc)
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
void PreSweepingGcVerification(collector::GarbageCollector* gc)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
+ LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
void PostGcVerification(collector::GarbageCollector* gc)
LOCKS_EXCLUDED(Locks::mutator_lock_);
void PostGcVerificationPaused(collector::GarbageCollector* gc)
@@ -752,8 +809,10 @@
// Given the current contents of the alloc space, increase the allowed heap footprint to match
// the target utilization ratio. This should only be called immediately after a full garbage
- // collection.
- void GrowForUtilization(collector::GarbageCollector* collector_ran);
+ // collection. bytes_allocated_before_gc is used to measure bytes / second for the period which
+ // the GC was run.
+ void GrowForUtilization(collector::GarbageCollector* collector_ran,
+ uint64_t bytes_allocated_before_gc = 0);
size_t GetPercentFree();
@@ -761,14 +820,13 @@
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
// Swap the allocation stack with the live stack.
- void SwapStacks(Thread* self);
+ void SwapStacks(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Clear cards and update the mod union table.
- void ProcessCards(TimingLogger* timings, bool use_rem_sets);
-
- // Signal the heap trim daemon that there is something to do, either a heap transition or heap
- // trim.
- void SignalHeapTrimDaemon(Thread* self);
+ // Clear cards and update the mod union table. When process_alloc_space_cards is true,
+ // if clear_alloc_space_cards is true, then we clear cards instead of ageing them. We do
+ // not process the alloc space if process_alloc_space_cards is false.
+ void ProcessCards(TimingLogger* timings, bool use_rem_sets, bool process_alloc_space_cards,
+ bool clear_alloc_space_cards);
// Push an object onto the allocation stack.
void PushOnAllocationStack(Thread* self, mirror::Object** obj)
@@ -778,12 +836,29 @@
void PushOnThreadLocalAllocationStackWithInternalGC(Thread* thread, mirror::Object** obj)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void ClearConcurrentGCRequest();
+ void ClearPendingTrim(Thread* self) LOCKS_EXCLUDED(pending_task_lock_);
+ void ClearPendingCollectorTransition(Thread* self) LOCKS_EXCLUDED(pending_task_lock_);
+
// What kind of concurrency behavior is the runtime after? Currently true for concurrent mark
// sweep GC, false for other GC types.
bool IsGcConcurrent() const ALWAYS_INLINE {
return collector_type_ == kCollectorTypeCMS || collector_type_ == kCollectorTypeCC;
}
+ // Trim the managed and native spaces by releasing unused memory back to the OS.
+ void TrimSpaces(Thread* self) LOCKS_EXCLUDED(gc_complete_lock_);
+
+ // Trim 0 pages at the end of reference tables.
+ void TrimIndirectReferenceTables(Thread* self);
+
+ void VisitObjectsInternal(ObjectCallback callback, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
+ void VisitObjectsInternalRegionSpace(ObjectCallback callback, void* arg)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
+ LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
+
// All-known continuous spaces, where objects lie within fixed bounds.
std::vector<space::ContinuousSpace*> continuous_spaces_;
@@ -813,6 +888,8 @@
// The card table, dirtied by the write barrier.
std::unique_ptr<accounting::CardTable> card_table_;
+ std::unique_ptr<accounting::ReadBarrierTable> rb_table_;
+
// A mod-union table remembers all of the references from the it's space to other spaces.
AllocationTrackingSafeMap<space::Space*, accounting::ModUnionTable*, kAllocatorTagHeap>
mod_union_tables_;
@@ -830,14 +907,8 @@
// Desired collector type, heap trimming daemon transitions the heap if it is != collector_type_.
CollectorType desired_collector_type_;
- // Lock which guards heap trim requests.
- Mutex* heap_trim_request_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- // When we want to perform the next heap trim (nano seconds).
- uint64_t last_trim_time_ GUARDED_BY(heap_trim_request_lock_);
- // When we want to perform the next heap transition (nano seconds) or heap trim.
- uint64_t heap_transition_or_trim_target_time_ GUARDED_BY(heap_trim_request_lock_);
- // If we have a heap trim request pending.
- bool heap_trim_request_pending_ GUARDED_BY(heap_trim_request_lock_);
+ // Lock which guards pending tasks.
+ Mutex* pending_task_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
// How many GC threads we may use for paused parts of garbage collection.
const size_t parallel_gc_threads_;
@@ -877,6 +948,9 @@
// Reference processor;
ReferenceProcessor reference_processor_;
+ // Task processor, proxies heap trim requests to the daemon threads.
+ std::unique_ptr<TaskProcessor> task_processor_;
+
// True while the garbage collector is running.
volatile CollectorType collector_type_running_ GUARDED_BY(gc_complete_lock_);
@@ -885,7 +959,7 @@
collector::GcType next_gc_type_;
// Maximum size that the heap can reach.
- const size_t capacity_;
+ size_t capacity_;
// The size the heap is limited to. This is initially smaller than capacity, but for largeHeap
// programs it is "cleared" making it the same as capacity.
@@ -963,12 +1037,6 @@
// Parallel GC data structures.
std::unique_ptr<ThreadPool> thread_pool_;
- // The nanosecond time at which the last GC ended.
- uint64_t last_gc_time_ns_;
-
- // How many bytes were allocated at the end of the last GC.
- uint64_t last_gc_size_;
-
// Estimated allocation rate (bytes / second). Computed between the time of the last GC cycle
// and the start of the current one.
uint64_t allocation_rate_;
@@ -1000,6 +1068,8 @@
// Temp space is the space which the semispace collector copies to.
space::BumpPointerSpace* temp_space_;
+ space::RegionSpace* region_space_;
+
// Minimum free guarantees that you always have at least min_free_ free bytes after growing for
// utilization, regardless of target utilization ratio.
size_t min_free_;
@@ -1055,11 +1125,20 @@
// Count for performed homogeneous space compaction.
Atomic<size_t> count_performed_homogeneous_space_compaction_;
+ // Whether or not a concurrent GC is pending.
+ Atomic<bool> concurrent_gc_pending_;
+
+ // Active tasks which we can modify (change target time, desired collector type, etc..).
+ CollectorTransitionTask* pending_collector_transition_ GUARDED_BY(pending_task_lock_);
+ HeapTrimTask* pending_heap_trim_ GUARDED_BY(pending_task_lock_);
+
// Whether or not we use homogeneous space compaction to avoid OOM errors.
bool use_homogeneous_space_compaction_for_oom_;
+ friend class CollectorTransitionTask;
friend class collector::GarbageCollector;
friend class collector::MarkCompact;
+ friend class collector::ConcurrentCopying;
friend class collector::MarkSweep;
friend class collector::SemiSpace;
friend class ReferenceQueue;
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index 012f9f9..01e8795 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -23,11 +23,14 @@
#include "reflection.h"
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change.h"
+#include "task_processor.h"
#include "well_known_classes.h"
namespace art {
namespace gc {
+static constexpr bool kAsyncReferenceQueueAdd = false;
+
ReferenceProcessor::ReferenceProcessor()
: process_references_args_(nullptr, nullptr, nullptr),
preserving_references_(false),
@@ -213,17 +216,43 @@
cleared_references_.UpdateRoots(callback, arg);
}
+class ClearedReferenceTask : public HeapTask {
+ public:
+ explicit ClearedReferenceTask(jobject cleared_references)
+ : HeapTask(NanoTime()), cleared_references_(cleared_references) {
+ }
+ virtual void Run(Thread* thread) {
+ ScopedObjectAccess soa(thread);
+ jvalue args[1];
+ args[0].l = cleared_references_;
+ InvokeWithJValues(soa, nullptr, WellKnownClasses::java_lang_ref_ReferenceQueue_add, args);
+ soa.Env()->DeleteGlobalRef(cleared_references_);
+ }
+
+ private:
+ const jobject cleared_references_;
+};
+
void ReferenceProcessor::EnqueueClearedReferences(Thread* self) {
Locks::mutator_lock_->AssertNotHeld(self);
+ // When a runtime isn't started there are no reference queues to care about so ignore.
if (!cleared_references_.IsEmpty()) {
- // When a runtime isn't started there are no reference queues to care about so ignore.
if (LIKELY(Runtime::Current()->IsStarted())) {
- ScopedObjectAccess soa(self);
- ScopedLocalRef<jobject> arg(self->GetJniEnv(),
- soa.AddLocalReference<jobject>(cleared_references_.GetList()));
- jvalue args[1];
- args[0].l = arg.get();
- InvokeWithJValues(soa, nullptr, WellKnownClasses::java_lang_ref_ReferenceQueue_add, args);
+ jobject cleared_references;
+ {
+ ReaderMutexLock mu(self, *Locks::mutator_lock_);
+ cleared_references = self->GetJniEnv()->vm->AddGlobalRef(
+ self, cleared_references_.GetList());
+ }
+ if (kAsyncReferenceQueueAdd) {
+ // TODO: This can cause RunFinalization to terminate before newly freed objects are
+ // finalized since they may not be enqueued by the time RunFinalization starts.
+ Runtime::Current()->GetHeap()->GetTaskProcessor()->AddTask(
+ self, new ClearedReferenceTask(cleared_references));
+ } else {
+ ClearedReferenceTask task(cleared_references);
+ task.Run(self);
+ }
}
cleared_references_.Clear();
}
@@ -234,7 +263,7 @@
MutexLock mu(self, *Locks::reference_processor_lock_);
// Wait untul we are done processing reference.
while (SlowPathEnabled()) {
- condition_.Wait(self);
+ condition_.WaitHoldingLocks(self);
}
// At this point, since the sentinel of the reference is live, it is guaranteed to not be
// enqueued if we just finished processing references. Otherwise, we may be doing the main GC
diff --git a/runtime/gc/reference_processor.h b/runtime/gc/reference_processor.h
index 5eb095b..c67fd98 100644
--- a/runtime/gc/reference_processor.h
+++ b/runtime/gc/reference_processor.h
@@ -53,7 +53,7 @@
// The slow path bool is contained in the reference class object, can only be set once
// Only allow setting this with mutators suspended so that we can avoid using a lock in the
// GetReferent fast path as an optimization.
- void EnableSlowPath() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void EnableSlowPath() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Decode the referent, may block if references are being processed.
mirror::Object* GetReferent(Thread* self, mirror::Reference* reference)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::reference_processor_lock_);
diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc
index 4003524..7be0704 100644
--- a/runtime/gc/reference_queue.cc
+++ b/runtime/gc/reference_queue.cc
@@ -17,6 +17,7 @@
#include "reference_queue.h"
#include "accounting/card_table-inl.h"
+#include "collector/concurrent_copying.h"
#include "heap.h"
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
@@ -85,21 +86,54 @@
} else {
ref->SetPendingNext<false>(nullptr);
}
+ Heap* heap = Runtime::Current()->GetHeap();
+ if (kUseBakerOrBrooksReadBarrier && heap->CurrentCollectorType() == kCollectorTypeCC &&
+ heap->ConcurrentCopyingCollector()->IsActive()) {
+ // Clear the gray ptr we left in ConcurrentCopying::ProcessMarkStack().
+ // We don't want to do this when the zygote compaction collector (SemiSpace) is running.
+ CHECK(ref != nullptr);
+ CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::GrayPtr())
+ << "ref=" << ref << " rb_ptr=" << ref->GetReadBarrierPointer();
+ if (heap->ConcurrentCopyingCollector()->RegionSpace()->IsInToSpace(ref)) {
+ // Moving objects.
+ ref->SetReadBarrierPointer(ReadBarrier::WhitePtr());
+ CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::WhitePtr());
+ } else {
+ // Non-moving objects.
+ ref->SetReadBarrierPointer(ReadBarrier::BlackPtr());
+ CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::BlackPtr());
+ }
+ }
return ref;
}
void ReferenceQueue::Dump(std::ostream& os) const {
mirror::Reference* cur = list_;
os << "Reference starting at list_=" << list_ << "\n";
- while (cur != nullptr) {
+ if (cur == nullptr) {
+ return;
+ }
+ do {
mirror::Reference* pending_next = cur->GetPendingNext();
- os << "PendingNext=" << pending_next;
+ os << "Reference= " << cur << " PendingNext=" << pending_next;
if (cur->IsFinalizerReferenceInstance()) {
os << " Zombie=" << cur->AsFinalizerReference()->GetZombie();
}
os << "\n";
cur = pending_next;
+ } while (cur != list_);
+}
+
+size_t ReferenceQueue::GetLength() const {
+ size_t count = 0;
+ mirror::Reference* cur = list_;
+ if (cur != nullptr) {
+ do {
+ ++count;
+ cur = cur->GetPendingNext();
+ } while (cur != list_);
}
+ return count;
}
void ReferenceQueue::ClearWhiteReferences(ReferenceQueue* cleared_references,
diff --git a/runtime/gc/reference_queue.h b/runtime/gc/reference_queue.h
index 4ef8478..f7d89d0 100644
--- a/runtime/gc/reference_queue.h
+++ b/runtime/gc/reference_queue.h
@@ -56,12 +56,14 @@
// overhead.
void EnqueueReference(mirror::Reference* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Enqueue a reference without checking that it is enqueable.
void EnqueuePendingReference(mirror::Reference* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Dequeue the first reference (returns list_).
mirror::Reference* DequeuePendingReference() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Enqueues finalizer references with white referents. White referents are blackened, moved to the
- // zombie field, and the referent field is cleared.
+ // Enqueues finalizer references with white referents. White referents are blackened, moved to
+ // the zombie field, and the referent field is cleared.
void EnqueueFinalizerReferences(ReferenceQueue* cleared_references,
IsHeapReferenceMarkedCallback* is_marked_callback,
MarkObjectCallback* mark_object_callback, void* arg)
@@ -73,24 +75,22 @@
void ForwardSoftReferences(IsHeapReferenceMarkedCallback* preserve_callback, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Unlink the reference list clearing references objects with white referents. Cleared references
+ // Unlink the reference list clearing references objects with white referents. Cleared references
// registered to a reference queue are scheduled for appending by the heap worker thread.
void ClearWhiteReferences(ReferenceQueue* cleared_references,
IsHeapReferenceMarkedCallback* is_marked_callback, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void Dump(std::ostream& os) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void Dump(std::ostream& os) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ size_t GetLength() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsEmpty() const {
return list_ == nullptr;
}
-
void Clear() {
list_ = nullptr;
}
-
- mirror::Reference* GetList() {
+ mirror::Reference* GetList() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return list_;
}
@@ -102,7 +102,6 @@
// Lock, used for parallel GC reference enqueuing. It allows for multiple threads simultaneously
// calling AtomicEnqueueIfNotEnqueued.
Mutex* const lock_;
-
// The actual reference list. Only a root for the mark compact GC since it will be null for other
// GC types.
mirror::Reference* list_;
diff --git a/runtime/gc/reference_queue_test.cc b/runtime/gc/reference_queue_test.cc
new file mode 100644
index 0000000..888c0d2
--- /dev/null
+++ b/runtime/gc/reference_queue_test.cc
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common_runtime_test.h"
+#include "reference_queue.h"
+#include "handle_scope-inl.h"
+#include "mirror/class-inl.h"
+#include "scoped_thread_state_change.h"
+
+namespace art {
+namespace gc {
+
+class ReferenceQueueTest : public CommonRuntimeTest {};
+
+TEST_F(ReferenceQueueTest, EnqueueDequeue) {
+ Thread* self = Thread::Current();
+ StackHandleScope<20> hs(self);
+ Mutex lock("Reference queue lock");
+ ReferenceQueue queue(&lock);
+ ASSERT_TRUE(queue.IsEmpty());
+ ScopedObjectAccess soa(self);
+ ASSERT_EQ(queue.GetLength(), 0U);
+ auto ref_class = hs.NewHandle(
+ Runtime::Current()->GetClassLinker()->FindClass(self, "Ljava/lang/ref/WeakReference;",
+ NullHandle<mirror::ClassLoader>()));
+ ASSERT_TRUE(ref_class.Get() != nullptr);
+ auto ref1(hs.NewHandle(ref_class->AllocObject(self)->AsReference()));
+ ASSERT_TRUE(ref1.Get() != nullptr);
+ auto ref2(hs.NewHandle(ref_class->AllocObject(self)->AsReference()));
+ ASSERT_TRUE(ref2.Get() != nullptr);
+ // FIFO ordering.
+ queue.EnqueuePendingReference(ref1.Get());
+ ASSERT_TRUE(!queue.IsEmpty());
+ ASSERT_EQ(queue.GetLength(), 1U);
+ queue.EnqueuePendingReference(ref2.Get());
+ ASSERT_TRUE(!queue.IsEmpty());
+ ASSERT_EQ(queue.GetLength(), 2U);
+ ASSERT_EQ(queue.DequeuePendingReference(), ref2.Get());
+ ASSERT_TRUE(!queue.IsEmpty());
+ ASSERT_EQ(queue.GetLength(), 1U);
+ ASSERT_EQ(queue.DequeuePendingReference(), ref1.Get());
+ ASSERT_EQ(queue.GetLength(), 0U);
+ ASSERT_TRUE(queue.IsEmpty());
+}
+
+TEST_F(ReferenceQueueTest, Dump) {
+ Thread* self = Thread::Current();
+ StackHandleScope<20> hs(self);
+ Mutex lock("Reference queue lock");
+ ReferenceQueue queue(&lock);
+ ScopedObjectAccess soa(self);
+ queue.Dump(LOG(INFO));
+ auto weak_ref_class = hs.NewHandle(
+ Runtime::Current()->GetClassLinker()->FindClass(self, "Ljava/lang/ref/WeakReference;",
+ NullHandle<mirror::ClassLoader>()));
+ ASSERT_TRUE(weak_ref_class.Get() != nullptr);
+ auto finalizer_ref_class = hs.NewHandle(
+ Runtime::Current()->GetClassLinker()->FindClass(self, "Ljava/lang/ref/FinalizerReference;",
+ NullHandle<mirror::ClassLoader>()));
+ ASSERT_TRUE(finalizer_ref_class.Get() != nullptr);
+ auto ref1(hs.NewHandle(weak_ref_class->AllocObject(self)->AsReference()));
+ ASSERT_TRUE(ref1.Get() != nullptr);
+ auto ref2(hs.NewHandle(finalizer_ref_class->AllocObject(self)->AsReference()));
+ ASSERT_TRUE(ref2.Get() != nullptr);
+ queue.EnqueuePendingReference(ref1.Get());
+ queue.Dump(LOG(INFO));
+ queue.EnqueuePendingReference(ref2.Get());
+ queue.Dump(LOG(INFO));
+}
+
+} // namespace gc
+} // namespace art
diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc
index 04b09e9..9675ba6 100644
--- a/runtime/gc/space/bump_pointer_space.cc
+++ b/runtime/gc/space/bump_pointer_space.cc
@@ -57,7 +57,7 @@
kGcRetentionPolicyAlwaysCollect),
growth_end_(mem_map->End()),
objects_allocated_(0), bytes_allocated_(0),
- block_lock_("Block lock"),
+ block_lock_("Block lock", kBumpPointerSpaceBlockLock),
main_block_size_(0),
num_blocks_(0) {
}
@@ -172,7 +172,8 @@
// Walk all of the objects in the main block first.
while (pos < main_end) {
mirror::Object* obj = reinterpret_cast<mirror::Object*>(pos);
- if (obj->GetClass() == nullptr) {
+ // No read barrier because obj may not be a valid object.
+ if (obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() == nullptr) {
// There is a race condition where a thread has just allocated an object but not set the
// class. We can't know the size of this object, so we don't visit it and exit the function
// since there is guaranteed to be not other blocks.
@@ -192,7 +193,8 @@
CHECK_LE(reinterpret_cast<const uint8_t*>(end_obj), End());
// We don't know how many objects are allocated in the current block. When we hit a null class
// assume its the end. TODO: Have a thread update the header when it flushes the block?
- while (obj < end_obj && obj->GetClass() != nullptr) {
+ // No read barrier because obj may not be a valid object.
+ while (obj < end_obj && obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) {
callback(obj, arg);
obj = GetNextObject(obj);
}
diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h
index 850a006..847f575 100644
--- a/runtime/gc/space/large_object_space.h
+++ b/runtime/gc/space/large_object_space.h
@@ -31,10 +31,10 @@
class AllocationInfo;
-enum LargeObjectSpaceType {
- kLargeObjectSpaceTypeDisabled,
- kLargeObjectSpaceTypeMap,
- kLargeObjectSpaceTypeFreeList,
+enum class LargeObjectSpaceType {
+ kDisabled,
+ kMap,
+ kFreeList,
};
// Abstraction implemented by all large object spaces.
diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc
index 43a2c59..9bbbb3c 100644
--- a/runtime/gc/space/malloc_space.cc
+++ b/runtime/gc/space/malloc_space.cc
@@ -173,7 +173,8 @@
// stored in between objects.
// Remaining size is for the new alloc space.
const size_t growth_limit = growth_limit_ - size;
- const size_t capacity = Capacity() - size;
+ // Use mem map limit in case error for clear growth limit.
+ const size_t capacity = NonGrowthLimitCapacity() - size;
VLOG(heap) << "Begin " << reinterpret_cast<const void*>(begin_) << "\n"
<< "End " << reinterpret_cast<const void*>(End()) << "\n"
<< "Size " << size << "\n"
@@ -247,6 +248,16 @@
context->freed.bytes += space->FreeList(self, num_ptrs, ptrs);
}
+void MallocSpace::ClampGrowthLimit() {
+ size_t new_capacity = Capacity();
+ CHECK_LE(new_capacity, NonGrowthLimitCapacity());
+ GetLiveBitmap()->SetHeapSize(new_capacity);
+ GetMarkBitmap()->SetHeapSize(new_capacity);
+ GetMemMap()->SetSize(new_capacity);
+ limit_ = Begin() + new_capacity;
+ CHECK(temp_bitmap_.get() == nullptr);
+}
+
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h
index 2fbd5f0..06239e5 100644
--- a/runtime/gc/space/malloc_space.h
+++ b/runtime/gc/space/malloc_space.h
@@ -110,6 +110,10 @@
return GetMemMap()->Size();
}
+ // Change the non growth limit capacity by shrinking or expanding the map. Currently, only
+ // shrinking is supported.
+ void ClampGrowthLimit();
+
void Dump(std::ostream& os) const;
void SetGrowthLimit(size_t growth_limit);
diff --git a/runtime/gc/space/region_space-inl.h b/runtime/gc/space/region_space-inl.h
new file mode 100644
index 0000000..a4ed718
--- /dev/null
+++ b/runtime/gc/space/region_space-inl.h
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_GC_SPACE_REGION_SPACE_INL_H_
+#define ART_RUNTIME_GC_SPACE_REGION_SPACE_INL_H_
+
+#include "region_space.h"
+
+namespace art {
+namespace gc {
+namespace space {
+
+inline mirror::Object* RegionSpace::Alloc(Thread*, size_t num_bytes, size_t* bytes_allocated,
+ size_t* usable_size) {
+ num_bytes = RoundUp(num_bytes, kAlignment);
+ return AllocNonvirtual<false>(num_bytes, bytes_allocated, usable_size);
+}
+
+inline mirror::Object* RegionSpace::AllocThreadUnsafe(Thread* self, size_t num_bytes,
+ size_t* bytes_allocated,
+ size_t* usable_size) {
+ Locks::mutator_lock_->AssertExclusiveHeld(self);
+ return Alloc(self, num_bytes, bytes_allocated, usable_size);
+}
+
+template<bool kForEvac>
+inline mirror::Object* RegionSpace::AllocNonvirtual(size_t num_bytes, size_t* bytes_allocated,
+ size_t* usable_size) {
+ DCHECK(IsAligned<kAlignment>(num_bytes));
+ mirror::Object* obj;
+ if (LIKELY(num_bytes <= kRegionSize)) {
+ // Non-large object.
+ if (!kForEvac) {
+ obj = current_region_->Alloc(num_bytes, bytes_allocated, usable_size);
+ } else {
+ DCHECK(evac_region_ != nullptr);
+ obj = evac_region_->Alloc(num_bytes, bytes_allocated, usable_size);
+ }
+ if (LIKELY(obj != nullptr)) {
+ return obj;
+ }
+ MutexLock mu(Thread::Current(), region_lock_);
+ // Retry with current region since another thread may have updated it.
+ if (!kForEvac) {
+ obj = current_region_->Alloc(num_bytes, bytes_allocated, usable_size);
+ } else {
+ obj = evac_region_->Alloc(num_bytes, bytes_allocated, usable_size);
+ }
+ if (LIKELY(obj != nullptr)) {
+ return obj;
+ }
+ if (!kForEvac) {
+ // Retain sufficient free regions for full evacuation.
+ if ((num_non_free_regions_ + 1) * 2 > num_regions_) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* r = ®ions_[i];
+ if (r->IsFree()) {
+ r->Unfree(time_);
+ r->SetNewlyAllocated();
+ ++num_non_free_regions_;
+ obj = r->Alloc(num_bytes, bytes_allocated, usable_size);
+ CHECK(obj != nullptr);
+ current_region_ = r;
+ return obj;
+ }
+ }
+ } else {
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* r = ®ions_[i];
+ if (r->IsFree()) {
+ r->Unfree(time_);
+ ++num_non_free_regions_;
+ obj = r->Alloc(num_bytes, bytes_allocated, usable_size);
+ CHECK(obj != nullptr);
+ evac_region_ = r;
+ return obj;
+ }
+ }
+ }
+ } else {
+ // Large object.
+ obj = AllocLarge<kForEvac>(num_bytes, bytes_allocated, usable_size);
+ if (LIKELY(obj != nullptr)) {
+ return obj;
+ }
+ }
+ return nullptr;
+}
+
+inline mirror::Object* RegionSpace::Region::Alloc(size_t num_bytes, size_t* bytes_allocated,
+ size_t* usable_size) {
+ DCHECK(IsAllocated() && IsInToSpace());
+ DCHECK(IsAligned<kAlignment>(num_bytes));
+ Atomic<uint8_t*>* atomic_top = reinterpret_cast<Atomic<uint8_t*>*>(&top_);
+ uint8_t* old_top;
+ uint8_t* new_top;
+ do {
+ old_top = atomic_top->LoadRelaxed();
+ new_top = old_top + num_bytes;
+ if (UNLIKELY(new_top > end_)) {
+ return nullptr;
+ }
+ } while (!atomic_top->CompareExchangeWeakSequentiallyConsistent(old_top, new_top));
+ reinterpret_cast<Atomic<uint64_t>*>(&objects_allocated_)->FetchAndAddSequentiallyConsistent(1);
+ DCHECK_LE(atomic_top->LoadRelaxed(), end_);
+ DCHECK_LT(old_top, end_);
+ DCHECK_LE(new_top, end_);
+ *bytes_allocated = num_bytes;
+ if (usable_size != nullptr) {
+ *usable_size = num_bytes;
+ }
+ return reinterpret_cast<mirror::Object*>(old_top);
+}
+
+inline size_t RegionSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_t* usable_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ size_t num_bytes = obj->SizeOf();
+ if (usable_size != nullptr) {
+ if (LIKELY(num_bytes <= kRegionSize)) {
+ DCHECK(RefToRegion(obj)->IsAllocated());
+ *usable_size = RoundUp(num_bytes, kAlignment);
+ } else {
+ DCHECK(RefToRegion(obj)->IsLarge());
+ *usable_size = RoundUp(num_bytes, kRegionSize);
+ }
+ }
+ return num_bytes;
+}
+
+template<RegionSpace::RegionType kRegionType>
+uint64_t RegionSpace::GetBytesAllocatedInternal() {
+ uint64_t bytes = 0;
+ MutexLock mu(Thread::Current(), region_lock_);
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* r = ®ions_[i];
+ if (r->IsFree()) {
+ continue;
+ }
+ switch (kRegionType) {
+ case RegionType::kRegionTypeAll:
+ bytes += r->BytesAllocated();
+ break;
+ case RegionType::kRegionTypeFromSpace:
+ if (r->IsInFromSpace()) {
+ bytes += r->BytesAllocated();
+ }
+ break;
+ case RegionType::kRegionTypeUnevacFromSpace:
+ if (r->IsInUnevacFromSpace()) {
+ bytes += r->BytesAllocated();
+ }
+ break;
+ case RegionType::kRegionTypeToSpace:
+ if (r->IsInToSpace()) {
+ bytes += r->BytesAllocated();
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unexpected space type : " << kRegionType;
+ }
+ }
+ return bytes;
+}
+
+template<RegionSpace::RegionType kRegionType>
+uint64_t RegionSpace::GetObjectsAllocatedInternal() {
+ uint64_t bytes = 0;
+ MutexLock mu(Thread::Current(), region_lock_);
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* r = ®ions_[i];
+ if (r->IsFree()) {
+ continue;
+ }
+ switch (kRegionType) {
+ case RegionType::kRegionTypeAll:
+ bytes += r->ObjectsAllocated();
+ break;
+ case RegionType::kRegionTypeFromSpace:
+ if (r->IsInFromSpace()) {
+ bytes += r->ObjectsAllocated();
+ }
+ break;
+ case RegionType::kRegionTypeUnevacFromSpace:
+ if (r->IsInUnevacFromSpace()) {
+ bytes += r->ObjectsAllocated();
+ }
+ break;
+ case RegionType::kRegionTypeToSpace:
+ if (r->IsInToSpace()) {
+ bytes += r->ObjectsAllocated();
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unexpected space type : " << kRegionType;
+ }
+ }
+ return bytes;
+}
+
+template<bool kToSpaceOnly>
+void RegionSpace::WalkInternal(ObjectCallback* callback, void* arg) {
+ // TODO: MutexLock on region_lock_ won't work due to lock order
+ // issues (the classloader classes lock and the monitor lock). We
+ // call this with threads suspended.
+ Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* r = ®ions_[i];
+ if (r->IsFree() || (kToSpaceOnly && !r->IsInToSpace())) {
+ continue;
+ }
+ if (r->IsLarge()) {
+ mirror::Object* obj = reinterpret_cast<mirror::Object*>(r->Begin());
+ if (obj->GetClass() != nullptr) {
+ callback(obj, arg);
+ }
+ } else if (r->IsLargeTail()) {
+ // Do nothing.
+ } else {
+ uint8_t* pos = r->Begin();
+ uint8_t* top = r->Top();
+ while (pos < top) {
+ mirror::Object* obj = reinterpret_cast<mirror::Object*>(pos);
+ if (obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) {
+ callback(obj, arg);
+ pos = reinterpret_cast<uint8_t*>(GetNextObject(obj));
+ } else {
+ break;
+ }
+ }
+ }
+ }
+}
+
+inline mirror::Object* RegionSpace::GetNextObject(mirror::Object* obj) {
+ const uintptr_t position = reinterpret_cast<uintptr_t>(obj) + obj->SizeOf();
+ return reinterpret_cast<mirror::Object*>(RoundUp(position, kAlignment));
+}
+
+template<bool kForEvac>
+mirror::Object* RegionSpace::AllocLarge(size_t num_bytes, size_t* bytes_allocated,
+ size_t* usable_size) {
+ DCHECK(IsAligned<kAlignment>(num_bytes));
+ DCHECK_GT(num_bytes, kRegionSize);
+ size_t num_regs = RoundUp(num_bytes, kRegionSize) / kRegionSize;
+ DCHECK_GT(num_regs, 0U);
+ DCHECK_LT((num_regs - 1) * kRegionSize, num_bytes);
+ DCHECK_LE(num_bytes, num_regs * kRegionSize);
+ MutexLock mu(Thread::Current(), region_lock_);
+ if (!kForEvac) {
+ // Retain sufficient free regions for full evacuation.
+ if ((num_non_free_regions_ + num_regs) * 2 > num_regions_) {
+ return nullptr;
+ }
+ }
+ // Find a large enough contiguous free regions.
+ size_t left = 0;
+ while (left + num_regs - 1 < num_regions_) {
+ bool found = true;
+ size_t right = left;
+ DCHECK_LT(right, left + num_regs)
+ << "The inner loop Should iterate at least once";
+ while (right < left + num_regs) {
+ if (regions_[right].IsFree()) {
+ ++right;
+ } else {
+ found = false;
+ break;
+ }
+ }
+ if (found) {
+ // right points to the one region past the last free region.
+ DCHECK_EQ(left + num_regs, right);
+ Region* first_reg = ®ions_[left];
+ DCHECK(first_reg->IsFree());
+ first_reg->UnfreeLarge(time_);
+ ++num_non_free_regions_;
+ first_reg->SetTop(first_reg->Begin() + num_bytes);
+ for (size_t p = left + 1; p < right; ++p) {
+ DCHECK_LT(p, num_regions_);
+ DCHECK(regions_[p].IsFree());
+ regions_[p].UnfreeLargeTail(time_);
+ ++num_non_free_regions_;
+ }
+ *bytes_allocated = num_bytes;
+ if (usable_size != nullptr) {
+ *usable_size = num_regs * kRegionSize;
+ }
+ return reinterpret_cast<mirror::Object*>(first_reg->Begin());
+ } else {
+ // right points to the non-free region. Start with the one after it.
+ left = right + 1;
+ }
+ }
+ return nullptr;
+}
+
+} // namespace space
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_SPACE_REGION_SPACE_INL_H_
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
new file mode 100644
index 0000000..2c556d9
--- /dev/null
+++ b/runtime/gc/space/region_space.cc
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bump_pointer_space.h"
+#include "bump_pointer_space-inl.h"
+#include "mirror/object-inl.h"
+#include "mirror/class-inl.h"
+#include "thread_list.h"
+
+namespace art {
+namespace gc {
+namespace space {
+
+// If a region has live objects whose size is less than this percent
+// value of the region size, evaculate the region.
+static constexpr uint kEvaculateLivePercentThreshold = 75U;
+
+RegionSpace* RegionSpace::Create(const std::string& name, size_t capacity,
+ uint8_t* requested_begin) {
+ capacity = RoundUp(capacity, kRegionSize);
+ std::string error_msg;
+ std::unique_ptr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), requested_begin, capacity,
+ PROT_READ | PROT_WRITE, true, &error_msg));
+ if (mem_map.get() == nullptr) {
+ LOG(ERROR) << "Failed to allocate pages for alloc space (" << name << ") of size "
+ << PrettySize(capacity) << " with message " << error_msg;
+ MemMap::DumpMaps(LOG(ERROR));
+ return nullptr;
+ }
+ return new RegionSpace(name, mem_map.release());
+}
+
+RegionSpace::RegionSpace(const std::string& name, MemMap* mem_map)
+ : ContinuousMemMapAllocSpace(name, mem_map, mem_map->Begin(), mem_map->End(), mem_map->End(),
+ kGcRetentionPolicyAlwaysCollect),
+ region_lock_("Region lock", kRegionSpaceRegionLock), time_(1U) {
+ size_t mem_map_size = mem_map->Size();
+ CHECK_ALIGNED(mem_map_size, kRegionSize);
+ CHECK_ALIGNED(mem_map->Begin(), kRegionSize);
+ num_regions_ = mem_map_size / kRegionSize;
+ num_non_free_regions_ = 0U;
+ DCHECK_GT(num_regions_, 0U);
+ regions_.reset(new Region[num_regions_]);
+ uint8_t* region_addr = mem_map->Begin();
+ for (size_t i = 0; i < num_regions_; ++i, region_addr += kRegionSize) {
+ regions_[i] = Region(i, region_addr, region_addr + kRegionSize);
+ }
+ if (kIsDebugBuild) {
+ CHECK_EQ(regions_[0].Begin(), Begin());
+ for (size_t i = 0; i < num_regions_; ++i) {
+ CHECK(regions_[i].IsFree());
+ CHECK_EQ(static_cast<size_t>(regions_[i].End() - regions_[i].Begin()), kRegionSize);
+ if (i + 1 < num_regions_) {
+ CHECK_EQ(regions_[i].End(), regions_[i + 1].Begin());
+ }
+ }
+ CHECK_EQ(regions_[num_regions_ - 1].End(), Limit());
+ }
+ full_region_ = Region();
+ DCHECK(!full_region_.IsFree());
+ DCHECK(full_region_.IsAllocated());
+ current_region_ = &full_region_;
+ evac_region_ = nullptr;
+ size_t ignored;
+ DCHECK(full_region_.Alloc(kAlignment, &ignored, nullptr) == nullptr);
+}
+
+size_t RegionSpace::FromSpaceSize() {
+ uint64_t num_regions = 0;
+ MutexLock mu(Thread::Current(), region_lock_);
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* r = ®ions_[i];
+ if (r->IsInFromSpace()) {
+ ++num_regions;
+ }
+ }
+ return num_regions * kRegionSize;
+}
+
+size_t RegionSpace::UnevacFromSpaceSize() {
+ uint64_t num_regions = 0;
+ MutexLock mu(Thread::Current(), region_lock_);
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* r = ®ions_[i];
+ if (r->IsInUnevacFromSpace()) {
+ ++num_regions;
+ }
+ }
+ return num_regions * kRegionSize;
+}
+
+size_t RegionSpace::ToSpaceSize() {
+ uint64_t num_regions = 0;
+ MutexLock mu(Thread::Current(), region_lock_);
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* r = ®ions_[i];
+ if (r->IsInToSpace()) {
+ ++num_regions;
+ }
+ }
+ return num_regions * kRegionSize;
+}
+
+inline bool RegionSpace::Region::ShouldBeEvacuated() {
+ DCHECK((IsAllocated() || IsLarge()) && IsInToSpace());
+ // if the region was allocated after the start of the
+ // previous GC or the live ratio is below threshold, evacuate
+ // it.
+ bool result;
+ if (is_newly_allocated_) {
+ result = true;
+ } else {
+ bool is_live_percent_valid = live_bytes_ != static_cast<size_t>(-1);
+ if (is_live_percent_valid) {
+ uint live_percent = GetLivePercent();
+ if (IsAllocated()) {
+ // Side node: live_percent == 0 does not necessarily mean
+ // there's no live objects due to rounding (there may be a
+ // few).
+ result = live_percent < kEvaculateLivePercentThreshold;
+ } else {
+ DCHECK(IsLarge());
+ result = live_percent == 0U;
+ }
+ } else {
+ result = false;
+ }
+ }
+ return result;
+}
+
+// Determine which regions to evacuate and mark them as
+// from-space. Mark the rest as unevacuated from-space.
+void RegionSpace::SetFromSpace(accounting::ReadBarrierTable* rb_table, bool force_evacuate_all) {
+ ++time_;
+ if (kUseTableLookupReadBarrier) {
+ DCHECK(rb_table->IsAllCleared());
+ rb_table->SetAll();
+ }
+ MutexLock mu(Thread::Current(), region_lock_);
+ size_t num_expected_large_tails = 0;
+ bool prev_large_evacuated = false;
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* r = ®ions_[i];
+ RegionState state = r->State();
+ RegionType type = r->Type();
+ if (!r->IsFree()) {
+ DCHECK(r->IsInToSpace());
+ if (LIKELY(num_expected_large_tails == 0U)) {
+ DCHECK((state == RegionState::kRegionStateAllocated ||
+ state == RegionState::kRegionStateLarge) &&
+ type == RegionType::kRegionTypeToSpace);
+ bool should_evacuate = force_evacuate_all || r->ShouldBeEvacuated();
+ if (should_evacuate) {
+ r->SetAsFromSpace();
+ DCHECK(r->IsInFromSpace());
+ } else {
+ r->SetAsUnevacFromSpace();
+ DCHECK(r->IsInUnevacFromSpace());
+ }
+ if (UNLIKELY(state == RegionState::kRegionStateLarge &&
+ type == RegionType::kRegionTypeToSpace)) {
+ prev_large_evacuated = should_evacuate;
+ num_expected_large_tails = RoundUp(r->BytesAllocated(), kRegionSize) / kRegionSize - 1;
+ DCHECK_GT(num_expected_large_tails, 0U);
+ }
+ } else {
+ DCHECK(state == RegionState::kRegionStateLargeTail &&
+ type == RegionType::kRegionTypeToSpace);
+ if (prev_large_evacuated) {
+ r->SetAsFromSpace();
+ DCHECK(r->IsInFromSpace());
+ } else {
+ r->SetAsUnevacFromSpace();
+ DCHECK(r->IsInUnevacFromSpace());
+ }
+ --num_expected_large_tails;
+ }
+ } else {
+ DCHECK_EQ(num_expected_large_tails, 0U);
+ if (kUseTableLookupReadBarrier) {
+ // Clear the rb table for to-space regions.
+ rb_table->Clear(r->Begin(), r->End());
+ }
+ }
+ }
+ current_region_ = &full_region_;
+ evac_region_ = &full_region_;
+}
+
+void RegionSpace::ClearFromSpace() {
+ MutexLock mu(Thread::Current(), region_lock_);
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* r = ®ions_[i];
+ if (r->IsInFromSpace()) {
+ r->Clear();
+ --num_non_free_regions_;
+ } else if (r->IsInUnevacFromSpace()) {
+ r->SetUnevacFromSpaceAsToSpace();
+ }
+ }
+ evac_region_ = nullptr;
+}
+
+void RegionSpace::AssertAllRegionLiveBytesZeroOrCleared() {
+ if (kIsDebugBuild) {
+ MutexLock mu(Thread::Current(), region_lock_);
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* r = ®ions_[i];
+ size_t live_bytes = r->LiveBytes();
+ CHECK(live_bytes == 0U || live_bytes == static_cast<size_t>(-1)) << live_bytes;
+ }
+ }
+}
+
+void RegionSpace::LogFragmentationAllocFailure(std::ostream& os,
+ size_t /* failed_alloc_bytes */) {
+ size_t max_contiguous_allocation = 0;
+ MutexLock mu(Thread::Current(), region_lock_);
+ if (current_region_->End() - current_region_->Top() > 0) {
+ max_contiguous_allocation = current_region_->End() - current_region_->Top();
+ }
+ if (num_non_free_regions_ * 2 < num_regions_) {
+ // We reserve half of the regions for evaluation only. If we
+ // occupy more than half the regions, do not report the free
+ // regions as available.
+ size_t max_contiguous_free_regions = 0;
+ size_t num_contiguous_free_regions = 0;
+ bool prev_free_region = false;
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* r = ®ions_[i];
+ if (r->IsFree()) {
+ if (!prev_free_region) {
+ CHECK_EQ(num_contiguous_free_regions, 0U);
+ prev_free_region = true;
+ }
+ ++num_contiguous_free_regions;
+ } else {
+ if (prev_free_region) {
+ CHECK_NE(num_contiguous_free_regions, 0U);
+ max_contiguous_free_regions = std::max(max_contiguous_free_regions,
+ num_contiguous_free_regions);
+ num_contiguous_free_regions = 0U;
+ prev_free_region = false;
+ }
+ }
+ }
+ max_contiguous_allocation = std::max(max_contiguous_allocation,
+ max_contiguous_free_regions * kRegionSize);
+ }
+ os << "; failed due to fragmentation (largest possible contiguous allocation "
+ << max_contiguous_allocation << " bytes)";
+ // Caller's job to print failed_alloc_bytes.
+}
+
+void RegionSpace::Clear() {
+ MutexLock mu(Thread::Current(), region_lock_);
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* r = ®ions_[i];
+ if (!r->IsFree()) {
+ --num_non_free_regions_;
+ }
+ r->Clear();
+ }
+ current_region_ = &full_region_;
+ evac_region_ = &full_region_;
+}
+
+void RegionSpace::Dump(std::ostream& os) const {
+ os << GetName() << " "
+ << reinterpret_cast<void*>(Begin()) << "-" << reinterpret_cast<void*>(Limit());
+}
+
+void RegionSpace::FreeLarge(mirror::Object* large_obj, size_t bytes_allocated) {
+ DCHECK(Contains(large_obj));
+ DCHECK(IsAligned<kRegionSize>(large_obj));
+ MutexLock mu(Thread::Current(), region_lock_);
+ uint8_t* begin_addr = reinterpret_cast<uint8_t*>(large_obj);
+ uint8_t* end_addr = AlignUp(reinterpret_cast<uint8_t*>(large_obj) + bytes_allocated, kRegionSize);
+ CHECK_LT(begin_addr, end_addr);
+ for (uint8_t* addr = begin_addr; addr < end_addr; addr += kRegionSize) {
+ Region* reg = RefToRegionLocked(reinterpret_cast<mirror::Object*>(addr));
+ if (addr == begin_addr) {
+ DCHECK(reg->IsLarge());
+ } else {
+ DCHECK(reg->IsLargeTail());
+ }
+ reg->Clear();
+ --num_non_free_regions_;
+ }
+ if (end_addr < Limit()) {
+ // If we aren't at the end of the space, check that the next region is not a large tail.
+ Region* following_reg = RefToRegionLocked(reinterpret_cast<mirror::Object*>(end_addr));
+ DCHECK(!following_reg->IsLargeTail());
+ }
+}
+
+void RegionSpace::DumpRegions(std::ostream& os) {
+ MutexLock mu(Thread::Current(), region_lock_);
+ for (size_t i = 0; i < num_regions_; ++i) {
+ regions_[i].Dump(os);
+ }
+}
+
+void RegionSpace::DumpNonFreeRegions(std::ostream& os) {
+ MutexLock mu(Thread::Current(), region_lock_);
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* reg = ®ions_[i];
+ if (!reg->IsFree()) {
+ reg->Dump(os);
+ }
+ }
+}
+
+void RegionSpace::RecordAlloc(mirror::Object* ref) {
+ CHECK(ref != nullptr);
+ Region* r = RefToRegion(ref);
+ reinterpret_cast<Atomic<uint64_t>*>(&r->objects_allocated_)->FetchAndAddSequentiallyConsistent(1);
+}
+
+bool RegionSpace::AllocNewTlab(Thread* self) {
+ MutexLock mu(self, region_lock_);
+ RevokeThreadLocalBuffersLocked(self);
+ // Retain sufficient free regions for full evacuation.
+ if ((num_non_free_regions_ + 1) * 2 > num_regions_) {
+ return false;
+ }
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* r = ®ions_[i];
+ if (r->IsFree()) {
+ r->Unfree(time_);
+ ++num_non_free_regions_;
+ // TODO: this is buggy. Debug it.
+ // r->SetNewlyAllocated();
+ r->SetTop(r->End());
+ r->is_a_tlab_ = true;
+ r->thread_ = self;
+ self->SetTlab(r->Begin(), r->End());
+ return true;
+ }
+ }
+ return false;
+}
+
+void RegionSpace::RevokeThreadLocalBuffers(Thread* thread) {
+ MutexLock mu(Thread::Current(), region_lock_);
+ RevokeThreadLocalBuffersLocked(thread);
+}
+
+void RegionSpace::RevokeThreadLocalBuffersLocked(Thread* thread) {
+ uint8_t* tlab_start = thread->GetTlabStart();
+ DCHECK_EQ(thread->HasTlab(), tlab_start != nullptr);
+ if (tlab_start != nullptr) {
+ DCHECK(IsAligned<kRegionSize>(tlab_start));
+ Region* r = RefToRegionLocked(reinterpret_cast<mirror::Object*>(tlab_start));
+ DCHECK(r->IsAllocated());
+ DCHECK_EQ(thread->GetThreadLocalBytesAllocated(), kRegionSize);
+ r->RecordThreadLocalAllocations(thread->GetThreadLocalObjectsAllocated(),
+ thread->GetThreadLocalBytesAllocated());
+ r->is_a_tlab_ = false;
+ r->thread_ = nullptr;
+ }
+ thread->SetTlab(nullptr, nullptr);
+}
+
+void RegionSpace::RevokeAllThreadLocalBuffers() {
+ Thread* self = Thread::Current();
+ MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+ MutexLock mu2(self, *Locks::thread_list_lock_);
+ std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
+ for (Thread* thread : thread_list) {
+ RevokeThreadLocalBuffers(thread);
+ }
+}
+
+void RegionSpace::AssertThreadLocalBuffersAreRevoked(Thread* thread) {
+ if (kIsDebugBuild) {
+ DCHECK(!thread->HasTlab());
+ }
+}
+
+void RegionSpace::AssertAllThreadLocalBuffersAreRevoked() {
+ if (kIsDebugBuild) {
+ Thread* self = Thread::Current();
+ MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+ MutexLock mu2(self, *Locks::thread_list_lock_);
+ std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
+ for (Thread* thread : thread_list) {
+ AssertThreadLocalBuffersAreRevoked(thread);
+ }
+ }
+}
+
+void RegionSpace::Region::Dump(std::ostream& os) const {
+ os << "Region[" << idx_ << "]=" << reinterpret_cast<void*>(begin_) << "-" << reinterpret_cast<void*>(top_)
+ << "-" << reinterpret_cast<void*>(end_)
+ << " state=" << static_cast<uint>(state_) << " type=" << static_cast<uint>(type_)
+ << " objects_allocated=" << objects_allocated_
+ << " alloc_time=" << alloc_time_ << " live_bytes=" << live_bytes_
+ << " is_newly_allocated=" << is_newly_allocated_ << " is_a_tlab=" << is_a_tlab_ << " thread=" << thread_ << "\n";
+}
+
+} // namespace space
+} // namespace gc
+} // namespace art
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
new file mode 100644
index 0000000..4160547
--- /dev/null
+++ b/runtime/gc/space/region_space.h
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_GC_SPACE_REGION_SPACE_H_
+#define ART_RUNTIME_GC_SPACE_REGION_SPACE_H_
+
+#include "gc/accounting/read_barrier_table.h"
+#include "object_callbacks.h"
+#include "space.h"
+#include "thread.h"
+
+namespace art {
+namespace gc {
+namespace space {
+
+// A space that consists of equal-sized regions.
+class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
+ public:
+ typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg);
+
+ SpaceType GetType() const OVERRIDE {
+ return kSpaceTypeRegionSpace;
+ }
+
+ // Create a region space with the requested sizes. The requested base address is not
+ // guaranteed to be granted, if it is required, the caller should call Begin on the returned
+ // space to confirm the request was granted.
+ static RegionSpace* Create(const std::string& name, size_t capacity, uint8_t* requested_begin);
+
+ // Allocate num_bytes, returns nullptr if the space is full.
+ mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated,
+ size_t* usable_size) OVERRIDE;
+ // Thread-unsafe allocation for when mutators are suspended, used by the semispace collector.
+ mirror::Object* AllocThreadUnsafe(Thread* self, size_t num_bytes, size_t* bytes_allocated,
+ size_t* usable_size)
+ OVERRIDE EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // The main allocation routine.
+ template<bool kForEvac>
+ ALWAYS_INLINE mirror::Object* AllocNonvirtual(size_t num_bytes, size_t* bytes_allocated,
+ size_t* usable_size);
+ // Allocate/free large objects (objects that are larger than the region size.)
+ template<bool kForEvac>
+ mirror::Object* AllocLarge(size_t num_bytes, size_t* bytes_allocated, size_t* usable_size);
+ void FreeLarge(mirror::Object* large_obj, size_t bytes_allocated);
+
+ // Return the storage space required by obj.
+ size_t AllocationSize(mirror::Object* obj, size_t* usable_size) OVERRIDE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return AllocationSizeNonvirtual(obj, usable_size);
+ }
+ size_t AllocationSizeNonvirtual(mirror::Object* obj, size_t* usable_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ size_t Free(Thread*, mirror::Object*) OVERRIDE {
+ UNIMPLEMENTED(FATAL);
+ return 0;
+ }
+ size_t FreeList(Thread*, size_t, mirror::Object**) OVERRIDE {
+ UNIMPLEMENTED(FATAL);
+ return 0;
+ }
+ accounting::ContinuousSpaceBitmap* GetLiveBitmap() const OVERRIDE {
+ // No live bitmap.
+ return nullptr;
+ }
+ accounting::ContinuousSpaceBitmap* GetMarkBitmap() const OVERRIDE {
+ // No mark bitmap.
+ return nullptr;
+ }
+
+ void Clear() OVERRIDE LOCKS_EXCLUDED(region_lock_);
+
+ void Dump(std::ostream& os) const;
+ void DumpRegions(std::ostream& os);
+ void DumpNonFreeRegions(std::ostream& os);
+
+ void RevokeThreadLocalBuffers(Thread* thread) LOCKS_EXCLUDED(region_lock_);
+ void RevokeThreadLocalBuffersLocked(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(region_lock_);
+ void RevokeAllThreadLocalBuffers() LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_,
+ Locks::thread_list_lock_);
+ void AssertThreadLocalBuffersAreRevoked(Thread* thread) LOCKS_EXCLUDED(region_lock_);
+ void AssertAllThreadLocalBuffersAreRevoked() LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_,
+ Locks::thread_list_lock_);
+
+ enum class RegionType : uint8_t {
+ kRegionTypeAll, // All types.
+ kRegionTypeFromSpace, // From-space. To be evacuated.
+ kRegionTypeUnevacFromSpace, // Unevacuated from-space. Not to be evacuated.
+ kRegionTypeToSpace, // To-space.
+ kRegionTypeNone, // None.
+ };
+
+ enum class RegionState : uint8_t {
+ kRegionStateFree, // Free region.
+ kRegionStateAllocated, // Allocated region.
+ kRegionStateLarge, // Large allocated (allocation larger than the region size).
+ kRegionStateLargeTail, // Large tail (non-first regions of a large allocation).
+ };
+
+ template<RegionType kRegionType> uint64_t GetBytesAllocatedInternal();
+ template<RegionType kRegionType> uint64_t GetObjectsAllocatedInternal();
+ uint64_t GetBytesAllocated() {
+ return GetBytesAllocatedInternal<RegionType::kRegionTypeAll>();
+ }
+ uint64_t GetObjectsAllocated() {
+ return GetObjectsAllocatedInternal<RegionType::kRegionTypeAll>();
+ }
+ uint64_t GetBytesAllocatedInFromSpace() {
+ return GetBytesAllocatedInternal<RegionType::kRegionTypeFromSpace>();
+ }
+ uint64_t GetObjectsAllocatedInFromSpace() {
+ return GetObjectsAllocatedInternal<RegionType::kRegionTypeFromSpace>();
+ }
+ uint64_t GetBytesAllocatedInUnevacFromSpace() {
+ return GetBytesAllocatedInternal<RegionType::kRegionTypeUnevacFromSpace>();
+ }
+ uint64_t GetObjectsAllocatedInUnevacFromSpace() {
+ return GetObjectsAllocatedInternal<RegionType::kRegionTypeUnevacFromSpace>();
+ }
+
+ bool CanMoveObjects() const OVERRIDE {
+ return true;
+ }
+
+ bool Contains(const mirror::Object* obj) const {
+ const uint8_t* byte_obj = reinterpret_cast<const uint8_t*>(obj);
+ return byte_obj >= Begin() && byte_obj < Limit();
+ }
+
+ RegionSpace* AsRegionSpace() OVERRIDE {
+ return this;
+ }
+
+ // Go through all of the blocks and visit the continuous objects.
+ void Walk(ObjectCallback* callback, void* arg)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ WalkInternal<false>(callback, arg);
+ }
+
+ void WalkToSpace(ObjectCallback* callback, void* arg)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ WalkInternal<true>(callback, arg);
+ }
+
+ accounting::ContinuousSpaceBitmap::SweepCallback* GetSweepCallback() OVERRIDE {
+ return nullptr;
+ }
+ void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) OVERRIDE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Object alignment within the space.
+ static constexpr size_t kAlignment = kObjectAlignment;
+ // The region size.
+ static constexpr size_t kRegionSize = 1 * MB;
+
+ bool IsInFromSpace(mirror::Object* ref) {
+ if (HasAddress(ref)) {
+ Region* r = RefToRegionUnlocked(ref);
+ return r->IsInFromSpace();
+ }
+ return false;
+ }
+
+ bool IsInUnevacFromSpace(mirror::Object* ref) {
+ if (HasAddress(ref)) {
+ Region* r = RefToRegionUnlocked(ref);
+ return r->IsInUnevacFromSpace();
+ }
+ return false;
+ }
+
+ bool IsInToSpace(mirror::Object* ref) {
+ if (HasAddress(ref)) {
+ Region* r = RefToRegionUnlocked(ref);
+ return r->IsInToSpace();
+ }
+ return false;
+ }
+
+ RegionType GetRegionType(mirror::Object* ref) {
+ if (HasAddress(ref)) {
+ Region* r = RefToRegionUnlocked(ref);
+ return r->Type();
+ }
+ return RegionType::kRegionTypeNone;
+ }
+
+ void SetFromSpace(accounting::ReadBarrierTable* rb_table, bool force_evacuate_all)
+ LOCKS_EXCLUDED(region_lock_);
+
+ size_t FromSpaceSize();
+ size_t UnevacFromSpaceSize();
+ size_t ToSpaceSize();
+ void ClearFromSpace();
+
+ void AddLiveBytes(mirror::Object* ref, size_t alloc_size) {
+ Region* reg = RefToRegionUnlocked(ref);
+ reg->AddLiveBytes(alloc_size);
+ }
+
+ void AssertAllRegionLiveBytesZeroOrCleared();
+
+ void RecordAlloc(mirror::Object* ref);
+ bool AllocNewTlab(Thread* self);
+
+ uint32_t Time() {
+ return time_;
+ }
+
+ private:
+ RegionSpace(const std::string& name, MemMap* mem_map);
+
+ template<bool kToSpaceOnly>
+ void WalkInternal(ObjectCallback* callback, void* arg) NO_THREAD_SAFETY_ANALYSIS;
+
+ class Region {
+ public:
+ Region()
+ : idx_(static_cast<size_t>(-1)),
+ begin_(nullptr), top_(nullptr), end_(nullptr),
+ state_(RegionState::kRegionStateAllocated), type_(RegionType::kRegionTypeToSpace),
+ objects_allocated_(0), alloc_time_(0), live_bytes_(static_cast<size_t>(-1)),
+ is_newly_allocated_(false), is_a_tlab_(false), thread_(nullptr) {}
+
+ Region(size_t idx, uint8_t* begin, uint8_t* end)
+ : idx_(idx), begin_(begin), top_(begin), end_(end),
+ state_(RegionState::kRegionStateFree), type_(RegionType::kRegionTypeNone),
+ objects_allocated_(0), alloc_time_(0), live_bytes_(static_cast<size_t>(-1)),
+ is_newly_allocated_(false), is_a_tlab_(false), thread_(nullptr) {
+ DCHECK_LT(begin, end);
+ DCHECK_EQ(static_cast<size_t>(end - begin), kRegionSize);
+ }
+
+ RegionState State() const {
+ return state_;
+ }
+
+ RegionType Type() const {
+ return type_;
+ }
+
+ void Clear() {
+ top_ = begin_;
+ state_ = RegionState::kRegionStateFree;
+ type_ = RegionType::kRegionTypeNone;
+ objects_allocated_ = 0;
+ alloc_time_ = 0;
+ live_bytes_ = static_cast<size_t>(-1);
+ if (!kMadviseZeroes) {
+ memset(begin_, 0, end_ - begin_);
+ }
+ madvise(begin_, end_ - begin_, MADV_DONTNEED);
+ is_newly_allocated_ = false;
+ is_a_tlab_ = false;
+ thread_ = nullptr;
+ }
+
+ ALWAYS_INLINE mirror::Object* Alloc(size_t num_bytes, size_t* bytes_allocated,
+ size_t* usable_size);
+
+ bool IsFree() const {
+ bool is_free = state_ == RegionState::kRegionStateFree;
+ if (is_free) {
+ DCHECK(IsInNoSpace());
+ DCHECK_EQ(begin_, top_);
+ DCHECK_EQ(objects_allocated_, 0U);
+ }
+ return is_free;
+ }
+
+ // Given a free region, declare it non-free (allocated).
+ void Unfree(uint32_t alloc_time) {
+ DCHECK(IsFree());
+ state_ = RegionState::kRegionStateAllocated;
+ type_ = RegionType::kRegionTypeToSpace;
+ alloc_time_ = alloc_time;
+ }
+
+ void UnfreeLarge(uint32_t alloc_time) {
+ DCHECK(IsFree());
+ state_ = RegionState::kRegionStateLarge;
+ type_ = RegionType::kRegionTypeToSpace;
+ alloc_time_ = alloc_time;
+ }
+
+ void UnfreeLargeTail(uint32_t alloc_time) {
+ DCHECK(IsFree());
+ state_ = RegionState::kRegionStateLargeTail;
+ type_ = RegionType::kRegionTypeToSpace;
+ alloc_time_ = alloc_time;
+ }
+
+ void SetNewlyAllocated() {
+ is_newly_allocated_ = true;
+ }
+
+ // Non-large, non-large-tail allocated.
+ bool IsAllocated() const {
+ return state_ == RegionState::kRegionStateAllocated;
+ }
+
+ // Large allocated.
+ bool IsLarge() const {
+ bool is_large = state_ == RegionState::kRegionStateLarge;
+ if (is_large) {
+ DCHECK_LT(begin_ + 1 * MB, top_);
+ }
+ return is_large;
+ }
+
+ // Large-tail allocated.
+ bool IsLargeTail() const {
+ bool is_large_tail = state_ == RegionState::kRegionStateLargeTail;
+ if (is_large_tail) {
+ DCHECK_EQ(begin_, top_);
+ }
+ return is_large_tail;
+ }
+
+ size_t Idx() const {
+ return idx_;
+ }
+
+ bool IsInFromSpace() const {
+ return type_ == RegionType::kRegionTypeFromSpace;
+ }
+
+ bool IsInToSpace() const {
+ return type_ == RegionType::kRegionTypeToSpace;
+ }
+
+ bool IsInUnevacFromSpace() const {
+ return type_ == RegionType::kRegionTypeUnevacFromSpace;
+ }
+
+ bool IsInNoSpace() const {
+ return type_ == RegionType::kRegionTypeNone;
+ }
+
+ void SetAsFromSpace() {
+ DCHECK(!IsFree() && IsInToSpace());
+ type_ = RegionType::kRegionTypeFromSpace;
+ live_bytes_ = static_cast<size_t>(-1);
+ }
+
+ void SetAsUnevacFromSpace() {
+ DCHECK(!IsFree() && IsInToSpace());
+ type_ = RegionType::kRegionTypeUnevacFromSpace;
+ live_bytes_ = 0U;
+ }
+
+ void SetUnevacFromSpaceAsToSpace() {
+ DCHECK(!IsFree() && IsInUnevacFromSpace());
+ type_ = RegionType::kRegionTypeToSpace;
+ }
+
+ ALWAYS_INLINE bool ShouldBeEvacuated();
+
+ void AddLiveBytes(size_t live_bytes) {
+ DCHECK(IsInUnevacFromSpace());
+ DCHECK(!IsLargeTail());
+ DCHECK_NE(live_bytes_, static_cast<size_t>(-1));
+ live_bytes_ += live_bytes;
+ DCHECK_LE(live_bytes_, BytesAllocated());
+ }
+
+ size_t LiveBytes() const {
+ return live_bytes_;
+ }
+
+ uint GetLivePercent() const {
+ DCHECK(IsInToSpace());
+ DCHECK(!IsLargeTail());
+ DCHECK_NE(live_bytes_, static_cast<size_t>(-1));
+ DCHECK_LE(live_bytes_, BytesAllocated());
+ size_t bytes_allocated = RoundUp(BytesAllocated(), kRegionSize);
+ DCHECK_GE(bytes_allocated, 0U);
+ uint result = (live_bytes_ * 100U) / bytes_allocated;
+ DCHECK_LE(result, 100U);
+ return result;
+ }
+
+ size_t BytesAllocated() const {
+ if (IsLarge()) {
+ DCHECK_LT(begin_ + kRegionSize, top_);
+ return static_cast<size_t>(top_ - begin_);
+ } else if (IsLargeTail()) {
+ DCHECK_EQ(begin_, top_);
+ return 0;
+ } else {
+ DCHECK(IsAllocated()) << static_cast<uint>(state_);
+ DCHECK_LE(begin_, top_);
+ size_t bytes = static_cast<size_t>(top_ - begin_);
+ DCHECK_LE(bytes, kRegionSize);
+ return bytes;
+ }
+ }
+
+ size_t ObjectsAllocated() const {
+ if (IsLarge()) {
+ DCHECK_LT(begin_ + 1 * MB, top_);
+ DCHECK_EQ(objects_allocated_, 0U);
+ return 1;
+ } else if (IsLargeTail()) {
+ DCHECK_EQ(begin_, top_);
+ DCHECK_EQ(objects_allocated_, 0U);
+ return 0;
+ } else {
+ DCHECK(IsAllocated()) << static_cast<uint>(state_);
+ return objects_allocated_;
+ }
+ }
+
+ uint8_t* Begin() const {
+ return begin_;
+ }
+
+ uint8_t* Top() const {
+ return top_;
+ }
+
+ void SetTop(uint8_t* new_top) {
+ top_ = new_top;
+ }
+
+ uint8_t* End() const {
+ return end_;
+ }
+
+ bool Contains(mirror::Object* ref) const {
+ return begin_ <= reinterpret_cast<uint8_t*>(ref) && reinterpret_cast<uint8_t*>(ref) < end_;
+ }
+
+ void Dump(std::ostream& os) const;
+
+ void RecordThreadLocalAllocations(size_t num_objects, size_t num_bytes) {
+ DCHECK(IsAllocated());
+ DCHECK_EQ(objects_allocated_, 0U);
+ DCHECK_EQ(top_, end_);
+ objects_allocated_ = num_objects;
+ top_ = begin_ + num_bytes;
+ DCHECK_EQ(top_, end_);
+ }
+
+ private:
+ size_t idx_; // The region's index in the region space.
+ uint8_t* begin_; // The begin address of the region.
+ // Can't use Atomic<uint8_t*> as Atomic's copy operator is implicitly deleted.
+ uint8_t* top_; // The current position of the allocation.
+ uint8_t* end_; // The end address of the region.
+ RegionState state_; // The region state (see RegionState).
+ RegionType type_; // The region type (see RegionType).
+ uint64_t objects_allocated_; // The number of objects allocated.
+ uint32_t alloc_time_; // The allocation time of the region.
+ size_t live_bytes_; // The live bytes. Used to compute the live percent.
+ bool is_newly_allocated_; // True if it's allocated after the last collection.
+ bool is_a_tlab_; // True if it's a tlab.
+ Thread* thread_; // The owning thread if it's a tlab.
+
+ friend class RegionSpace;
+ };
+
+ Region* RefToRegion(mirror::Object* ref) LOCKS_EXCLUDED(region_lock_) {
+ MutexLock mu(Thread::Current(), region_lock_);
+ return RefToRegionLocked(ref);
+ }
+
+ Region* RefToRegionUnlocked(mirror::Object* ref) NO_THREAD_SAFETY_ANALYSIS {
+ // For a performance reason (this is frequently called via
+ // IsInFromSpace() etc.) we avoid taking a lock here. Note that
+ // since we only change a region from to-space to from-space only
+ // during a pause (SetFromSpace()) and from from-space to free
+ // (after GC is done) as long as ref is a valid reference into an
+ // allocated region, it's safe to access the region state without
+ // the lock.
+ return RefToRegionLocked(ref);
+ }
+
+ Region* RefToRegionLocked(mirror::Object* ref) EXCLUSIVE_LOCKS_REQUIRED(region_lock_) {
+ DCHECK(HasAddress(ref));
+ uintptr_t offset = reinterpret_cast<uintptr_t>(ref) - reinterpret_cast<uintptr_t>(Begin());
+ size_t reg_idx = offset / kRegionSize;
+ DCHECK_LT(reg_idx, num_regions_);
+ Region* reg = ®ions_[reg_idx];
+ DCHECK_EQ(reg->Idx(), reg_idx);
+ DCHECK(reg->Contains(ref));
+ return reg;
+ }
+
+ mirror::Object* GetNextObject(mirror::Object* obj)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ Mutex region_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+
+ uint32_t time_; // The time as the number of collections since the startup.
+ size_t num_regions_; // The number of regions in this space.
+ size_t num_non_free_regions_; // The number of non-free regions in this space.
+ std::unique_ptr<Region[]> regions_ GUARDED_BY(region_lock_);
+ // The pointer to the region array.
+ Region* current_region_; // The region that's being allocated currently.
+ Region* evac_region_; // The region that's being evacuated to currently.
+ Region full_region_; // The dummy/sentinel region that looks full.
+
+ DISALLOW_COPY_AND_ASSIGN(RegionSpace);
+};
+
+std::ostream& operator<<(std::ostream& os, const RegionSpace::RegionState& value);
+std::ostream& operator<<(std::ostream& os, const RegionSpace::RegionType& value);
+
+} // namespace space
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_SPACE_REGION_SPACE_H_
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 74d1a2b..ced25a4 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -365,8 +365,9 @@
mark_bitmap_->Clear();
SetEnd(begin_ + starting_size_);
delete rosalloc_;
- rosalloc_ = CreateRosAlloc(mem_map_->Begin(), starting_size_, initial_size_, Capacity(),
- low_memory_mode_, Runtime::Current()->RunningOnValgrind());
+ rosalloc_ = CreateRosAlloc(mem_map_->Begin(), starting_size_, initial_size_,
+ NonGrowthLimitCapacity(), low_memory_mode_,
+ Runtime::Current()->RunningOnValgrind());
SetFootprintLimit(footprint_limit);
}
diff --git a/runtime/gc/space/space.cc b/runtime/gc/space/space.cc
index 486d79a..a2e2c1c 100644
--- a/runtime/gc/space/space.cc
+++ b/runtime/gc/space/space.cc
@@ -58,6 +58,11 @@
UNREACHABLE();
}
+RegionSpace* Space::AsRegionSpace() {
+ LOG(FATAL) << "Unreachable";
+ return nullptr;
+}
+
AllocSpace* Space::AsAllocSpace() {
UNIMPLEMENTED(FATAL) << "Unreachable";
UNREACHABLE();
diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h
index 860a4c9..d24650b 100644
--- a/runtime/gc/space/space.h
+++ b/runtime/gc/space/space.h
@@ -50,6 +50,7 @@
class RosAllocSpace;
class ImageSpace;
class LargeObjectSpace;
+class RegionSpace;
class ZygoteSpace;
static constexpr bool kDebugSpaces = kIsDebugBuild;
@@ -72,6 +73,7 @@
kSpaceTypeZygoteSpace,
kSpaceTypeBumpPointerSpace,
kSpaceTypeLargeObjectSpace,
+ kSpaceTypeRegionSpace,
};
std::ostream& operator<<(std::ostream& os, const SpaceType& space_type);
@@ -132,6 +134,11 @@
}
virtual BumpPointerSpace* AsBumpPointerSpace();
+ bool IsRegionSpace() const {
+ return GetType() == kSpaceTypeRegionSpace;
+ }
+ virtual RegionSpace* AsRegionSpace();
+
// Does this space hold large objects and implement the large object space abstraction?
bool IsLargeObjectSpace() const {
return GetType() == kSpaceTypeLargeObjectSpace;
diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h
index 9f39b80..09d10dd 100644
--- a/runtime/gc/space/space_test.h
+++ b/runtime/gc/space/space_test.h
@@ -127,6 +127,9 @@
}
void SpaceTest::InitTestBody(CreateSpaceFn create_space) {
+ // This will lead to error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
{
// Init < max == growth
std::unique_ptr<Space> space(create_space("test", 16 * MB, 32 * MB, 32 * MB, nullptr));
diff --git a/runtime/gc/task_processor.cc b/runtime/gc/task_processor.cc
new file mode 100644
index 0000000..1a3c6f5
--- /dev/null
+++ b/runtime/gc/task_processor.cc
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "task_processor.h"
+
+#include "scoped_thread_state_change.h"
+
+namespace art {
+namespace gc {
+
+TaskProcessor::TaskProcessor()
+ : lock_(new Mutex("Task processor lock", kReferenceProcessorLock)), is_running_(false) {
+ // Piggyback off the reference processor lock level.
+ cond_.reset(new ConditionVariable("Task processor condition", *lock_));
+}
+
+TaskProcessor::~TaskProcessor() {
+ delete lock_;
+}
+
+void TaskProcessor::AddTask(Thread* self, HeapTask* task) {
+ ScopedThreadStateChange tsc(self, kBlocked);
+ MutexLock mu(self, *lock_);
+ tasks_.insert(task);
+ cond_->Signal(self);
+}
+
+HeapTask* TaskProcessor::GetTask(Thread* self) {
+ ScopedThreadStateChange tsc(self, kBlocked);
+ MutexLock mu(self, *lock_);
+ while (true) {
+ if (tasks_.empty()) {
+ if (!is_running_) {
+ return nullptr;
+ }
+ cond_->Wait(self); // Empty queue, wait until we are signalled.
+ } else {
+ // Non empty queue, look at the top element and see if we are ready to run it.
+ const uint64_t current_time = NanoTime();
+ HeapTask* task = *tasks_.begin();
+ // If we are shutting down, return the task right away without waiting. Otherwise return the
+ // task if it is late enough.
+ uint64_t target_time = task->GetTargetRunTime();
+ if (!is_running_ || target_time <= current_time) {
+ tasks_.erase(tasks_.begin());
+ return task;
+ }
+ DCHECK_GT(target_time, current_time);
+ // Wait untl we hit the target run time.
+ const uint64_t delta_time = target_time - current_time;
+ const uint64_t ms_delta = NsToMs(delta_time);
+ const uint64_t ns_delta = delta_time - MsToNs(ms_delta);
+ cond_->TimedWait(self, static_cast<int64_t>(ms_delta), static_cast<int32_t>(ns_delta));
+ }
+ }
+ UNREACHABLE();
+ return nullptr;
+}
+
+void TaskProcessor::UpdateTargetRunTime(Thread* self, HeapTask* task, uint64_t new_target_time) {
+ MutexLock mu(self, *lock_);
+ // Find the task.
+ auto range = tasks_.equal_range(task);
+ for (auto it = range.first; it != range.second; ++it) {
+ if (*it == task) {
+ // Check if the target time was updated, if so re-insert then wait.
+ if (new_target_time != task->GetTargetRunTime()) {
+ tasks_.erase(it);
+ task->SetTargetRunTime(new_target_time);
+ tasks_.insert(task);
+ // If we became the first task then we may need to signal since we changed the task that we
+ // are sleeping on.
+ if (*tasks_.begin() == task) {
+ cond_->Signal(self);
+ }
+ return;
+ }
+ }
+ }
+}
+
+bool TaskProcessor::IsRunning() const {
+ MutexLock mu(Thread::Current(), *lock_);
+ return is_running_;
+}
+
+void TaskProcessor::Stop(Thread* self) {
+ MutexLock mu(self, *lock_);
+ is_running_ = false;
+ cond_->Broadcast(self);
+}
+
+void TaskProcessor::Start(Thread* self) {
+ MutexLock mu(self, *lock_);
+ is_running_ = true;
+}
+
+void TaskProcessor::RunAllTasks(Thread* self) {
+ while (true) {
+ // Wait and get a task, may be interrupted.
+ HeapTask* task = GetTask(self);
+ if (task != nullptr) {
+ task->Run(self);
+ task->Finalize();
+ } else if (!IsRunning()) {
+ break;
+ }
+ }
+}
+
+} // namespace gc
+} // namespace art
diff --git a/runtime/gc/task_processor.h b/runtime/gc/task_processor.h
new file mode 100644
index 0000000..765f035
--- /dev/null
+++ b/runtime/gc/task_processor.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_GC_TASK_PROCESSOR_H_
+#define ART_RUNTIME_GC_TASK_PROCESSOR_H_
+
+#include <memory>
+#include <set>
+
+#include "base/mutex.h"
+#include "globals.h"
+#include "thread_pool.h"
+
+namespace art {
+namespace gc {
+
+class HeapTask : public SelfDeletingTask {
+ public:
+ explicit HeapTask(uint64_t target_run_time) : target_run_time_(target_run_time) {
+ }
+ uint64_t GetTargetRunTime() const {
+ return target_run_time_;
+ }
+
+ private:
+ // Update the updated_target_run_time_, the task processor will re-insert the task when it is
+ // popped and update the target_run_time_.
+ void SetTargetRunTime(uint64_t new_target_run_time) {
+ target_run_time_ = new_target_run_time;
+ }
+
+ // Time in ns at which we want the task to run.
+ uint64_t target_run_time_;
+
+ friend class TaskProcessor;
+};
+
+// Used to process GC tasks (heap trim, heap transitions, concurrent GC).
+class TaskProcessor {
+ public:
+ TaskProcessor();
+ virtual ~TaskProcessor();
+ void AddTask(Thread* self, HeapTask* task) LOCKS_EXCLUDED(lock_);
+ HeapTask* GetTask(Thread* self) LOCKS_EXCLUDED(lock_);
+ void Start(Thread* self) LOCKS_EXCLUDED(lock_);
+ // Stop tells the RunAllTasks to finish up the remaining tasks as soon as
+ // possible then return.
+ void Stop(Thread* self) LOCKS_EXCLUDED(lock_);
+ void RunAllTasks(Thread* self) LOCKS_EXCLUDED(lock_);
+ bool IsRunning() const LOCKS_EXCLUDED(lock_);
+ void UpdateTargetRunTime(Thread* self, HeapTask* target_time, uint64_t new_target_time)
+ LOCKS_EXCLUDED(lock_);
+
+ private:
+ class CompareByTargetRunTime {
+ public:
+ bool operator()(const HeapTask* a, const HeapTask* b) const {
+ return a->GetTargetRunTime() < b->GetTargetRunTime();
+ }
+ };
+
+ mutable Mutex* lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ bool is_running_ GUARDED_BY(lock_);
+ std::unique_ptr<ConditionVariable> cond_ GUARDED_BY(lock_);
+ std::multiset<HeapTask*, CompareByTargetRunTime> tasks_ GUARDED_BY(lock_);
+};
+
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_TASK_PROCESSOR_H_
diff --git a/runtime/gc/task_processor_test.cc b/runtime/gc/task_processor_test.cc
new file mode 100644
index 0000000..5dd6d8f
--- /dev/null
+++ b/runtime/gc/task_processor_test.cc
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common_runtime_test.h"
+#include "task_processor.h"
+#include "thread_pool.h"
+#include "thread-inl.h"
+#include "utils.h"
+
+namespace art {
+namespace gc {
+
+class TaskProcessorTest : public CommonRuntimeTest {
+ public:
+};
+
+class RecursiveTask : public HeapTask {
+ public:
+ RecursiveTask(TaskProcessor* task_processor, Atomic<size_t>* counter, size_t max_recursion)
+ : HeapTask(NanoTime() + MsToNs(10)), task_processor_(task_processor), counter_(counter),
+ max_recursion_(max_recursion) {
+ }
+ virtual void Run(Thread* self) OVERRIDE {
+ if (max_recursion_ > 0) {
+ task_processor_->AddTask(self,
+ new RecursiveTask(task_processor_, counter_, max_recursion_ - 1));
+ counter_->FetchAndAddSequentiallyConsistent(1U);
+ }
+ }
+
+ private:
+ TaskProcessor* const task_processor_;
+ Atomic<size_t>* const counter_;
+ const size_t max_recursion_;
+};
+
+class WorkUntilDoneTask : public SelfDeletingTask {
+ public:
+ WorkUntilDoneTask(TaskProcessor* task_processor, Atomic<bool>* done_running)
+ : task_processor_(task_processor), done_running_(done_running) {
+ }
+ virtual void Run(Thread* self) OVERRIDE {
+ task_processor_->RunAllTasks(self);
+ done_running_->StoreSequentiallyConsistent(true);
+ }
+
+ private:
+ TaskProcessor* const task_processor_;
+ Atomic<bool>* done_running_;
+};
+
+TEST_F(TaskProcessorTest, Interrupt) {
+ ThreadPool thread_pool("task processor test", 1U);
+ Thread* const self = Thread::Current();
+ TaskProcessor task_processor;
+ static constexpr size_t kRecursion = 10;
+ Atomic<bool> done_running(false);
+ Atomic<size_t> counter(0);
+ task_processor.AddTask(self, new RecursiveTask(&task_processor, &counter, kRecursion));
+ task_processor.Start(self);
+ // Add a task which will wait until interrupted to the thread pool.
+ thread_pool.AddTask(self, new WorkUntilDoneTask(&task_processor, &done_running));
+ thread_pool.StartWorkers(self);
+ ASSERT_FALSE(done_running);
+ // Wait until all the tasks are done, but since we didn't interrupt, done_running should be 0.
+ while (counter.LoadSequentiallyConsistent() != kRecursion) {
+ usleep(10);
+ }
+ ASSERT_FALSE(done_running);
+ task_processor.Stop(self);
+ thread_pool.Wait(self, true, false);
+ // After the interrupt and wait, the WorkUntilInterruptedTasktask should have terminated and
+ // set done_running_ to true.
+ ASSERT_TRUE(done_running.LoadSequentiallyConsistent());
+
+ // Test that we finish remaining tasks before returning from RunTasksUntilInterrupted.
+ counter.StoreSequentiallyConsistent(0);
+ done_running.StoreSequentiallyConsistent(false);
+ // Self interrupt before any of the other tasks run, but since we added them we should keep on
+ // working until all the tasks are completed.
+ task_processor.Stop(self);
+ task_processor.AddTask(self, new RecursiveTask(&task_processor, &counter, kRecursion));
+ thread_pool.AddTask(self, new WorkUntilDoneTask(&task_processor, &done_running));
+ thread_pool.StartWorkers(self);
+ thread_pool.Wait(self, true, false);
+ ASSERT_TRUE(done_running.LoadSequentiallyConsistent());
+ ASSERT_EQ(counter.LoadSequentiallyConsistent(), kRecursion);
+}
+
+class TestOrderTask : public HeapTask {
+ public:
+ explicit TestOrderTask(uint64_t expected_time, size_t expected_counter, size_t* counter)
+ : HeapTask(expected_time), expected_counter_(expected_counter), counter_(counter) {
+ }
+ virtual void Run(Thread* thread) OVERRIDE {
+ UNUSED(thread); // Fix cppling bug.
+ ASSERT_EQ(*counter_, expected_counter_);
+ ++*counter_;
+ }
+
+ private:
+ const size_t expected_counter_;
+ size_t* const counter_;
+};
+
+TEST_F(TaskProcessorTest, Ordering) {
+ static const size_t kNumTasks = 25;
+ const uint64_t current_time = NanoTime();
+ Thread* const self = Thread::Current();
+ TaskProcessor task_processor;
+ task_processor.Stop(self);
+ size_t counter = 0;
+ std::vector<std::pair<uint64_t, size_t>> orderings;
+ for (size_t i = 0; i < kNumTasks; ++i) {
+ orderings.push_back(std::make_pair(current_time + MsToNs(10U * i), i));
+ }
+ for (size_t i = 0; i < kNumTasks; ++i) {
+ std::swap(orderings[i], orderings[(i * 87654231 + 12345) % orderings.size()]);
+ }
+ for (const auto& pair : orderings) {
+ auto* task = new TestOrderTask(pair.first, pair.second, &counter);
+ task_processor.AddTask(self, task);
+ }
+ ThreadPool thread_pool("task processor test", 1U);
+ Atomic<bool> done_running(false);
+ // Add a task which will wait until interrupted to the thread pool.
+ thread_pool.AddTask(self, new WorkUntilDoneTask(&task_processor, &done_running));
+ ASSERT_FALSE(done_running.LoadSequentiallyConsistent());
+ thread_pool.StartWorkers(self);
+ thread_pool.Wait(self, true, false);
+ ASSERT_TRUE(done_running.LoadSequentiallyConsistent());
+ ASSERT_EQ(counter, kNumTasks);
+}
+
+} // namespace gc
+} // namespace art
diff --git a/runtime/gc_root.h b/runtime/gc_root.h
index aee5586..7e0be64 100644
--- a/runtime/gc_root.h
+++ b/runtime/gc_root.h
@@ -19,22 +19,75 @@
#include "base/macros.h"
#include "base/mutex.h" // For Locks::mutator_lock_.
-#include "object_callbacks.h"
namespace art {
+namespace mirror {
+class Object;
+} // namespace mirror
+
+enum RootType {
+ kRootUnknown = 0,
+ kRootJNIGlobal,
+ kRootJNILocal,
+ kRootJavaFrame,
+ kRootNativeStack,
+ kRootStickyClass,
+ kRootThreadBlock,
+ kRootMonitorUsed,
+ kRootThreadObject,
+ kRootInternedString,
+ kRootDebugger,
+ kRootVMInternal,
+ kRootJNIMonitor,
+};
+std::ostream& operator<<(std::ostream& os, const RootType& root_type);
+
+class RootInfo {
+ public:
+ // Thread id 0 is for non thread roots.
+ explicit RootInfo(RootType type, uint32_t thread_id = 0)
+ : type_(type), thread_id_(thread_id) {
+ }
+ virtual ~RootInfo() {
+ }
+ RootType GetType() const {
+ return type_;
+ }
+ uint32_t GetThreadId() const {
+ return thread_id_;
+ }
+ virtual void Describe(std::ostream& os) const {
+ os << "Type=" << type_ << " thread_id=" << thread_id_;
+ }
+
+ private:
+ const RootType type_;
+ const uint32_t thread_id_;
+};
+
+// Returns the new address of the object, returns root if it has not moved. tid and root_type are
+// only used by hprof.
+typedef void (RootCallback)(mirror::Object** root, void* arg, const RootInfo& root_info);
+
template<class MirrorType>
class PACKED(4) GcRoot {
public:
template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ALWAYS_INLINE MirrorType* Read() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VisitRoot(RootCallback* callback, void* arg, uint32_t thread_id, RootType root_type) const {
+ void VisitRoot(RootCallback* callback, void* arg, const RootInfo& info) const {
DCHECK(!IsNull());
- callback(reinterpret_cast<mirror::Object**>(&root_), arg, thread_id, root_type);
+ callback(reinterpret_cast<mirror::Object**>(&root_), arg, info);
DCHECK(!IsNull());
}
+ void VisitRootIfNonNull(RootCallback* callback, void* arg, const RootInfo& info) const {
+ if (!IsNull()) {
+ VisitRoot(callback, arg, info);
+ }
+ }
+
// This is only used by IrtIterator.
ALWAYS_INLINE MirrorType** AddressWithoutBarrier() {
return &root_;
diff --git a/runtime/globals.h b/runtime/globals.h
index 3104229..0845475 100644
--- a/runtime/globals.h
+++ b/runtime/globals.h
@@ -58,12 +58,6 @@
static constexpr bool kIsTargetBuild = false;
#endif
-#if defined(ART_USE_PORTABLE_COMPILER)
-static constexpr bool kUsePortableCompiler = true;
-#else
-static constexpr bool kUsePortableCompiler = false;
-#endif
-
#if defined(ART_USE_OPTIMIZING_COMPILER)
static constexpr bool kUseOptimizingCompiler = true;
#else
@@ -71,7 +65,7 @@
#endif
// Garbage collector constants.
-static constexpr bool kMovingCollector = true && !kUsePortableCompiler;
+static constexpr bool kMovingCollector = true;
static constexpr bool kMarkCompactSupport = false && kMovingCollector;
// True if we allow moving field arrays, this can cause complication with mark compact.
static constexpr bool kMoveFieldArrays = !kMarkCompactSupport;
@@ -98,22 +92,34 @@
static constexpr bool kUseBrooksReadBarrier = false;
#endif
+#ifdef USE_TABLE_LOOKUP_READ_BARRIER
+static constexpr bool kUseTableLookupReadBarrier = true;
+#else
+static constexpr bool kUseTableLookupReadBarrier = false;
+#endif
+
static constexpr bool kUseBakerOrBrooksReadBarrier = kUseBakerReadBarrier || kUseBrooksReadBarrier;
+static constexpr bool kUseReadBarrier = kUseBakerReadBarrier || kUseBrooksReadBarrier ||
+ kUseTableLookupReadBarrier;
// If true, references within the heap are poisoned (negated).
+#ifdef ART_HEAP_POISONING
+static constexpr bool kPoisonHeapReferences = true;
+#else
static constexpr bool kPoisonHeapReferences = false;
+#endif
// Kinds of tracing clocks.
-enum TraceClockSource {
- kTraceClockSourceThreadCpu,
- kTraceClockSourceWall,
- kTraceClockSourceDual, // Both wall and thread CPU clocks.
+enum class TraceClockSource {
+ kThreadCpu,
+ kWall,
+ kDual, // Both wall and thread CPU clocks.
};
-#if defined(HAVE_POSIX_CLOCKS)
-static constexpr TraceClockSource kDefaultTraceClockSource = kTraceClockSourceDual;
+#if defined(__linux__)
+static constexpr TraceClockSource kDefaultTraceClockSource = TraceClockSource::kDual;
#else
-static constexpr TraceClockSource kDefaultTraceClockSource = kTraceClockSourceWall;
+static constexpr TraceClockSource kDefaultTraceClockSource = TraceClockSource::kWall;
#endif
static constexpr bool kDefaultMustRelocate = true;
diff --git a/runtime/handle_scope-inl.h b/runtime/handle_scope-inl.h
index 9ddaf61..222083b 100644
--- a/runtime/handle_scope-inl.h
+++ b/runtime/handle_scope-inl.h
@@ -21,12 +21,14 @@
#include "handle.h"
#include "thread.h"
+#include "verify_object-inl.h"
namespace art {
template<size_t kNumReferences>
inline StackHandleScope<kNumReferences>::StackHandleScope(Thread* self, mirror::Object* fill_value)
: HandleScope(self->GetTopHandleScope(), kNumReferences), self_(self), pos_(0) {
+ DCHECK_EQ(self, Thread::Current());
static_assert(kNumReferences >= 1, "StackHandleScope must contain at least 1 reference");
// TODO: Figure out how to use a compile assert.
CHECK_EQ(&storage_[0], GetReferences());
@@ -42,6 +44,71 @@
DCHECK_EQ(top_handle_scope, this);
}
+inline size_t HandleScope::SizeOf(uint32_t num_references) {
+ size_t header_size = sizeof(HandleScope);
+ size_t data_size = sizeof(StackReference<mirror::Object>) * num_references;
+ return header_size + data_size;
+}
+
+inline size_t HandleScope::SizeOf(size_t pointer_size, uint32_t num_references) {
+ // Assume that the layout is packed.
+ size_t header_size = pointer_size + sizeof(number_of_references_);
+ size_t data_size = sizeof(StackReference<mirror::Object>) * num_references;
+ return header_size + data_size;
+}
+
+inline mirror::Object* HandleScope::GetReference(size_t i) const {
+ DCHECK_LT(i, number_of_references_);
+ return GetReferences()[i].AsMirrorPtr();
+}
+
+inline Handle<mirror::Object> HandleScope::GetHandle(size_t i) {
+ DCHECK_LT(i, number_of_references_);
+ return Handle<mirror::Object>(&GetReferences()[i]);
+}
+
+inline MutableHandle<mirror::Object> HandleScope::GetMutableHandle(size_t i) {
+ DCHECK_LT(i, number_of_references_);
+ return MutableHandle<mirror::Object>(&GetReferences()[i]);
+}
+
+inline void HandleScope::SetReference(size_t i, mirror::Object* object) {
+ DCHECK_LT(i, number_of_references_);
+ GetReferences()[i].Assign(object);
+}
+
+inline bool HandleScope::Contains(StackReference<mirror::Object>* handle_scope_entry) const {
+ // A HandleScope should always contain something. One created by the
+ // jni_compiler should have a jobject/jclass as a native method is
+ // passed in a this pointer or a class
+ DCHECK_GT(number_of_references_, 0U);
+ return &GetReferences()[0] <= handle_scope_entry &&
+ handle_scope_entry <= &GetReferences()[number_of_references_ - 1];
+}
+
+template<size_t kNumReferences> template<class T>
+inline MutableHandle<T> StackHandleScope<kNumReferences>::NewHandle(T* object) {
+ SetReference(pos_, object);
+ MutableHandle<T> h(GetHandle<T>(pos_));
+ pos_++;
+ return h;
+}
+
+template<size_t kNumReferences> template<class T>
+inline HandleWrapper<T> StackHandleScope<kNumReferences>::NewHandleWrapper(T** object) {
+ SetReference(pos_, *object);
+ MutableHandle<T> h(GetHandle<T>(pos_));
+ pos_++;
+ return HandleWrapper<T>(object, h);
+}
+
+template<size_t kNumReferences>
+inline void StackHandleScope<kNumReferences>::SetReference(size_t i, mirror::Object* object) {
+ DCHECK_LT(i, kNumReferences);
+ VerifyObject(object);
+ GetReferences()[i].Assign(object);
+}
+
} // namespace art
#endif // ART_RUNTIME_HANDLE_SCOPE_INL_H_
diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h
index 2c4f0f9..782bbea 100644
--- a/runtime/handle_scope.h
+++ b/runtime/handle_scope.h
@@ -22,6 +22,7 @@
#include "handle.h"
#include "stack.h"
#include "utils.h"
+#include "verify_object.h"
namespace art {
namespace mirror {
@@ -47,19 +48,10 @@
// takes the pointer size explicitly so that at compile time we can cross-compile correctly.
// Returns the size of a HandleScope containing num_references handles.
- static size_t SizeOf(uint32_t num_references) {
- size_t header_size = sizeof(HandleScope);
- size_t data_size = sizeof(StackReference<mirror::Object>) * num_references;
- return header_size + data_size;
- }
+ static size_t SizeOf(uint32_t num_references);
// Returns the size of a HandleScope containing num_references handles.
- static size_t SizeOf(size_t pointer_size, uint32_t num_references) {
- // Assume that the layout is packed.
- size_t header_size = pointer_size + sizeof(number_of_references_);
- size_t data_size = sizeof(StackReference<mirror::Object>) * num_references;
- return header_size + data_size;
- }
+ static size_t SizeOf(size_t pointer_size, uint32_t num_references);
// Link to previous HandleScope or null.
HandleScope* GetLink() const {
@@ -67,37 +59,18 @@
}
ALWAYS_INLINE mirror::Object* GetReference(size_t i) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK_LT(i, number_of_references_);
- return GetReferences()[i].AsMirrorPtr();
- }
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
ALWAYS_INLINE Handle<mirror::Object> GetHandle(size_t i)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK_LT(i, number_of_references_);
- return Handle<mirror::Object>(&GetReferences()[i]);
- }
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
ALWAYS_INLINE MutableHandle<mirror::Object> GetMutableHandle(size_t i)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK_LT(i, number_of_references_);
- return MutableHandle<mirror::Object>(&GetReferences()[i]);
- }
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
ALWAYS_INLINE void SetReference(size_t i, mirror::Object* object)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK_LT(i, number_of_references_);
- GetReferences()[i].Assign(object);
- }
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool Contains(StackReference<mirror::Object>* handle_scope_entry) const {
- // A HandleScope should always contain something. One created by the
- // jni_compiler should have a jobject/jclass as a native method is
- // passed in a this pointer or a class
- DCHECK_GT(number_of_references_, 0U);
- return &GetReferences()[0] <= handle_scope_entry &&
- handle_scope_entry <= &GetReferences()[number_of_references_ - 1];
- }
+ ALWAYS_INLINE bool Contains(StackReference<mirror::Object>* handle_scope_entry) const;
// Offset of link within HandleScope, used by generated code.
static size_t LinkOffset(size_t pointer_size ATTRIBUTE_UNUSED) {
@@ -174,27 +147,14 @@
ALWAYS_INLINE ~StackHandleScope();
template<class T>
- ALWAYS_INLINE MutableHandle<T> NewHandle(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- SetReference(pos_, object);
- MutableHandle<T> h(GetHandle<T>(pos_));
- pos_++;
- return h;
- }
+ ALWAYS_INLINE MutableHandle<T> NewHandle(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<class T>
ALWAYS_INLINE HandleWrapper<T> NewHandleWrapper(T** object)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- SetReference(pos_, *object);
- MutableHandle<T> h(GetHandle<T>(pos_));
- pos_++;
- return HandleWrapper<T>(object, h);
- }
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
ALWAYS_INLINE void SetReference(size_t i, mirror::Object* object)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK_LT(i, kNumReferences);
- GetReferences()[i].Assign(object);
- }
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
template<class T>
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 3069581..d2e93bc 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -44,10 +44,13 @@
#include "common_throws.h"
#include "debugger.h"
#include "dex_file-inl.h"
+#include "gc_root.h"
#include "gc/accounting/heap_bitmap.h"
#include "gc/heap.h"
#include "gc/space/space.h"
#include "globals.h"
+#include "jdwp/jdwp.h"
+#include "jdwp/jdwp_priv.h"
#include "mirror/art_field-inl.h"
#include "mirror/class.h"
#include "mirror/class-inl.h"
@@ -61,46 +64,17 @@
namespace hprof {
-#define UNIQUE_ERROR -((((uintptr_t)__func__) << 16 | __LINE__) & (0x7fffffff))
+static constexpr bool kDirectStream = true;
-#define HPROF_TIME 0
-#define HPROF_NULL_STACK_TRACE 0
-#define HPROF_NULL_THREAD 0
+static constexpr uint32_t kHprofTime = 0;
+static constexpr uint32_t kHprofNullStackTrace = 0;
+static constexpr uint32_t kHprofNullThread = 0;
-#define U2_TO_BUF_BE(buf, offset, value) \
- do { \
- unsigned char* buf_ = (unsigned char*)(buf); \
- int offset_ = static_cast<int>(offset); \
- uint16_t value_ = (uint16_t)(value); \
- buf_[offset_ + 0] = (unsigned char)(value_ >> 8); \
- buf_[offset_ + 1] = (unsigned char)(value_ ); \
- } while (0)
+static constexpr size_t kMaxObjectsPerSegment = 128;
+static constexpr size_t kMaxBytesPerSegment = 4096;
-#define U4_TO_BUF_BE(buf, offset, value) \
- do { \
- unsigned char* buf_ = (unsigned char*)(buf); \
- int offset_ = static_cast<int>(offset); \
- uint32_t value_ = (uint32_t)(value); \
- buf_[offset_ + 0] = (unsigned char)(value_ >> 24); \
- buf_[offset_ + 1] = (unsigned char)(value_ >> 16); \
- buf_[offset_ + 2] = (unsigned char)(value_ >> 8); \
- buf_[offset_ + 3] = (unsigned char)(value_ ); \
- } while (0)
-
-#define U8_TO_BUF_BE(buf, offset, value) \
- do { \
- unsigned char* buf_ = (unsigned char*)(buf); \
- int offset_ = static_cast<int>(offset); \
- uint64_t value_ = (uint64_t)(value); \
- buf_[offset_ + 0] = (unsigned char)(value_ >> 56); \
- buf_[offset_ + 1] = (unsigned char)(value_ >> 48); \
- buf_[offset_ + 2] = (unsigned char)(value_ >> 40); \
- buf_[offset_ + 3] = (unsigned char)(value_ >> 32); \
- buf_[offset_ + 4] = (unsigned char)(value_ >> 24); \
- buf_[offset_ + 5] = (unsigned char)(value_ >> 16); \
- buf_[offset_ + 6] = (unsigned char)(value_ >> 8); \
- buf_[offset_ + 7] = (unsigned char)(value_ ); \
- } while (0)
+// The static field-name for the synthetic object generated to account for class static overhead.
+static constexpr const char* kStaticOverheadName = "$staticOverhead";
enum HprofTag {
HPROF_TAG_STRING = 0x01,
@@ -170,216 +144,267 @@
typedef uint32_t HprofStringId;
typedef uint32_t HprofClassObjectId;
-// Represents a top-level hprof record, whose serialized format is:
-// U1 TAG: denoting the type of the record
-// U4 TIME: number of microseconds since the time stamp in the header
-// U4 LENGTH: number of bytes that follow this uint32_t field and belong to this record
-// U1* BODY: as many bytes as specified in the above uint32_t field
-class HprofRecord {
+class EndianOutput {
public:
- HprofRecord() : alloc_length_(128), fp_(nullptr), tag_(0), time_(0), length_(0), dirty_(false) {
- body_ = reinterpret_cast<unsigned char*>(malloc(alloc_length_));
+ EndianOutput() : length_(0), sum_length_(0), max_length_(0), started_(false) {}
+ virtual ~EndianOutput() {}
+
+ void StartNewRecord(uint8_t tag, uint32_t time) {
+ if (length_ > 0) {
+ EndRecord();
+ }
+ DCHECK_EQ(length_, 0U);
+ AddU1(tag);
+ AddU4(time);
+ AddU4(0xdeaddead); // Length, replaced on flush.
+ started_ = true;
}
- ~HprofRecord() {
- free(body_);
- }
-
- int StartNewRecord(FILE* fp, uint8_t tag, uint32_t time) {
- int rc = Flush();
- if (rc != 0) {
- return rc;
+ void EndRecord() {
+ // Replace length in header.
+ if (started_) {
+ UpdateU4(sizeof(uint8_t) + sizeof(uint32_t),
+ length_ - sizeof(uint8_t) - 2 * sizeof(uint32_t));
}
- fp_ = fp;
- tag_ = tag;
- time_ = time;
+ HandleEndRecord();
+
+ sum_length_ += length_;
+ max_length_ = std::max(max_length_, length_);
length_ = 0;
- dirty_ = true;
- return 0;
+ started_ = false;
}
- int Flush() {
- if (dirty_) {
- unsigned char headBuf[sizeof(uint8_t) + 2 * sizeof(uint32_t)];
-
- headBuf[0] = tag_;
- U4_TO_BUF_BE(headBuf, 1, time_);
- U4_TO_BUF_BE(headBuf, 5, length_);
-
- int nb = fwrite(headBuf, 1, sizeof(headBuf), fp_);
- if (nb != sizeof(headBuf)) {
- return UNIQUE_ERROR;
- }
- nb = fwrite(body_, 1, length_, fp_);
- if (nb != static_cast<int>(length_)) {
- return UNIQUE_ERROR;
- }
-
- dirty_ = false;
- }
- // TODO if we used less than half (or whatever) of allocLen, shrink the buffer.
- return 0;
+ void AddU1(uint8_t value) {
+ AddU1List(&value, 1);
+ }
+ void AddU2(uint16_t value) {
+ AddU2List(&value, 1);
+ }
+ void AddU4(uint32_t value) {
+ AddU4List(&value, 1);
}
- int AddU1(uint8_t value) {
- int err = GuaranteeRecordAppend(1);
- if (UNLIKELY(err != 0)) {
- return err;
- }
-
- body_[length_++] = value;
- return 0;
+ void AddU8(uint64_t value) {
+ AddU8List(&value, 1);
}
- int AddU2(uint16_t value) {
- return AddU2List(&value, 1);
- }
-
- int AddU4(uint32_t value) {
- return AddU4List(&value, 1);
- }
-
- int AddU8(uint64_t value) {
- return AddU8List(&value, 1);
- }
-
- int AddObjectId(const mirror::Object* value) {
- return AddU4(PointerToLowMemUInt32(value));
+ void AddObjectId(const mirror::Object* value) {
+ AddU4(PointerToLowMemUInt32(value));
}
// The ID for the synthetic object generated to account for class static overhead.
- int AddClassStaticsId(const mirror::Class* value) {
- return AddU4(1 | PointerToLowMemUInt32(value));
+ void AddClassStaticsId(const mirror::Class* value) {
+ AddU4(1 | PointerToLowMemUInt32(value));
}
- int AddJniGlobalRefId(jobject value) {
- return AddU4(PointerToLowMemUInt32(value));
+ void AddJniGlobalRefId(jobject value) {
+ AddU4(PointerToLowMemUInt32(value));
}
- int AddClassId(HprofClassObjectId value) {
- return AddU4(value);
+ void AddClassId(HprofClassObjectId value) {
+ AddU4(value);
}
- int AddStringId(HprofStringId value) {
- return AddU4(value);
+ void AddStringId(HprofStringId value) {
+ AddU4(value);
}
- int AddU1List(const uint8_t* values, size_t numValues) {
- int err = GuaranteeRecordAppend(numValues);
- if (UNLIKELY(err != 0)) {
- return err;
- }
-
- memcpy(body_ + length_, values, numValues);
- length_ += numValues;
- return 0;
+ void AddU1List(const uint8_t* values, size_t count) {
+ HandleU1List(values, count);
+ length_ += count;
+ }
+ void AddU2List(const uint16_t* values, size_t count) {
+ HandleU2List(values, count);
+ length_ += count * sizeof(uint16_t);
+ }
+ void AddU4List(const uint32_t* values, size_t count) {
+ HandleU4List(values, count);
+ length_ += count * sizeof(uint32_t);
+ }
+ virtual void UpdateU4(size_t offset ATTRIBUTE_UNUSED, uint32_t new_value ATTRIBUTE_UNUSED) {
+ DCHECK_LE(offset, length_ - 4);
+ }
+ void AddU8List(const uint64_t* values, size_t count) {
+ HandleU8List(values, count);
+ length_ += count * sizeof(uint64_t);
}
- int AddU2List(const uint16_t* values, size_t numValues) {
- int err = GuaranteeRecordAppend(numValues * 2);
- if (UNLIKELY(err != 0)) {
- return err;
- }
-
- unsigned char* insert = body_ + length_;
- for (size_t i = 0; i < numValues; ++i) {
- U2_TO_BUF_BE(insert, 0, *values++);
- insert += sizeof(*values);
- }
- length_ += numValues * 2;
- return 0;
- }
-
- int AddU4List(const uint32_t* values, size_t numValues) {
- int err = GuaranteeRecordAppend(numValues * 4);
- if (UNLIKELY(err != 0)) {
- return err;
- }
-
- unsigned char* insert = body_ + length_;
- for (size_t i = 0; i < numValues; ++i) {
- U4_TO_BUF_BE(insert, 0, *values++);
- insert += sizeof(*values);
- }
- length_ += numValues * 4;
- return 0;
- }
-
- void UpdateU4(size_t offset, uint32_t new_value) {
- U4_TO_BUF_BE(body_, offset, new_value);
- }
-
- int AddU8List(const uint64_t* values, size_t numValues) {
- int err = GuaranteeRecordAppend(numValues * 8);
- if (err != 0) {
- return err;
- }
-
- unsigned char* insert = body_ + length_;
- for (size_t i = 0; i < numValues; ++i) {
- U8_TO_BUF_BE(insert, 0, *values++);
- insert += sizeof(*values);
- }
- length_ += numValues * 8;
- return 0;
- }
-
- int AddIdList(mirror::ObjectArray<mirror::Object>* values)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- int32_t length = values->GetLength();
+ void AddIdList(mirror::ObjectArray<mirror::Object>* values)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const int32_t length = values->GetLength();
for (int32_t i = 0; i < length; ++i) {
- int err = AddObjectId(values->GetWithoutChecks(i));
- if (UNLIKELY(err != 0)) {
- return err;
- }
+ AddObjectId(values->GetWithoutChecks(i));
}
- return 0;
}
- int AddUtf8String(const char* str) {
+ void AddUtf8String(const char* str) {
// The terminating NUL character is NOT written.
- return AddU1List((const uint8_t*)str, strlen(str));
+ AddU1List((const uint8_t*)str, strlen(str));
}
- size_t Size() const {
+ size_t Length() const {
return length_;
}
- private:
- int GuaranteeRecordAppend(size_t nmore) {
- size_t minSize = length_ + nmore;
- if (minSize > alloc_length_) {
- size_t newAllocLen = alloc_length_ * 2;
- if (newAllocLen < minSize) {
- newAllocLen = alloc_length_ + nmore + nmore/2;
- }
- unsigned char* newBody = (unsigned char*)realloc(body_, newAllocLen);
- if (newBody != NULL) {
- body_ = newBody;
- alloc_length_ = newAllocLen;
- } else {
- // TODO: set an error flag so future ops will fail
- return UNIQUE_ERROR;
- }
- }
-
- CHECK_LE(length_ + nmore, alloc_length_);
- return 0;
+ size_t SumLength() const {
+ return sum_length_;
}
- size_t alloc_length_;
- unsigned char* body_;
+ size_t MaxLength() const {
+ return max_length_;
+ }
- FILE* fp_;
- uint8_t tag_;
- uint32_t time_;
- size_t length_;
- bool dirty_;
+ protected:
+ virtual void HandleU1List(const uint8_t* values ATTRIBUTE_UNUSED,
+ size_t count ATTRIBUTE_UNUSED) {
+ }
+ virtual void HandleU2List(const uint16_t* values ATTRIBUTE_UNUSED,
+ size_t count ATTRIBUTE_UNUSED) {
+ }
+ virtual void HandleU4List(const uint32_t* values ATTRIBUTE_UNUSED,
+ size_t count ATTRIBUTE_UNUSED) {
+ }
+ virtual void HandleU8List(const uint64_t* values ATTRIBUTE_UNUSED,
+ size_t count ATTRIBUTE_UNUSED) {
+ }
+ virtual void HandleEndRecord() {
+ }
- DISALLOW_COPY_AND_ASSIGN(HprofRecord);
+ size_t length_; // Current record size.
+ size_t sum_length_; // Size of all data.
+ size_t max_length_; // Maximum seen length.
+ bool started_; // Was StartRecord called?
};
+// This keeps things buffered until flushed.
+class EndianOutputBuffered : public EndianOutput {
+ public:
+ explicit EndianOutputBuffered(size_t reserve_size) {
+ buffer_.reserve(reserve_size);
+ }
+ virtual ~EndianOutputBuffered() {}
+
+ void UpdateU4(size_t offset, uint32_t new_value) OVERRIDE {
+ DCHECK_LE(offset, length_ - 4);
+ buffer_[offset + 0] = static_cast<uint8_t>((new_value >> 24) & 0xFF);
+ buffer_[offset + 1] = static_cast<uint8_t>((new_value >> 16) & 0xFF);
+ buffer_[offset + 2] = static_cast<uint8_t>((new_value >> 8) & 0xFF);
+ buffer_[offset + 3] = static_cast<uint8_t>((new_value >> 0) & 0xFF);
+ }
+
+ protected:
+ void HandleU1List(const uint8_t* values, size_t count) OVERRIDE {
+ DCHECK_EQ(length_, buffer_.size());
+ buffer_.insert(buffer_.end(), values, values + count);
+ }
+
+ void HandleU2List(const uint16_t* values, size_t count) OVERRIDE {
+ DCHECK_EQ(length_, buffer_.size());
+ for (size_t i = 0; i < count; ++i) {
+ uint16_t value = *values;
+ buffer_.push_back(static_cast<uint8_t>((value >> 8) & 0xFF));
+ buffer_.push_back(static_cast<uint8_t>((value >> 0) & 0xFF));
+ values++;
+ }
+ }
+
+ void HandleU4List(const uint32_t* values, size_t count) OVERRIDE {
+ DCHECK_EQ(length_, buffer_.size());
+ for (size_t i = 0; i < count; ++i) {
+ uint32_t value = *values;
+ buffer_.push_back(static_cast<uint8_t>((value >> 24) & 0xFF));
+ buffer_.push_back(static_cast<uint8_t>((value >> 16) & 0xFF));
+ buffer_.push_back(static_cast<uint8_t>((value >> 8) & 0xFF));
+ buffer_.push_back(static_cast<uint8_t>((value >> 0) & 0xFF));
+ values++;
+ }
+ }
+
+ void HandleU8List(const uint64_t* values, size_t count) OVERRIDE {
+ DCHECK_EQ(length_, buffer_.size());
+ for (size_t i = 0; i < count; ++i) {
+ uint64_t value = *values;
+ buffer_.push_back(static_cast<uint8_t>((value >> 56) & 0xFF));
+ buffer_.push_back(static_cast<uint8_t>((value >> 48) & 0xFF));
+ buffer_.push_back(static_cast<uint8_t>((value >> 40) & 0xFF));
+ buffer_.push_back(static_cast<uint8_t>((value >> 32) & 0xFF));
+ buffer_.push_back(static_cast<uint8_t>((value >> 24) & 0xFF));
+ buffer_.push_back(static_cast<uint8_t>((value >> 16) & 0xFF));
+ buffer_.push_back(static_cast<uint8_t>((value >> 8) & 0xFF));
+ buffer_.push_back(static_cast<uint8_t>((value >> 0) & 0xFF));
+ values++;
+ }
+ }
+
+ void HandleEndRecord() OVERRIDE {
+ DCHECK_EQ(buffer_.size(), length_);
+ if (kIsDebugBuild && started_) {
+ uint32_t stored_length =
+ static_cast<uint32_t>(buffer_[5]) << 24 |
+ static_cast<uint32_t>(buffer_[6]) << 16 |
+ static_cast<uint32_t>(buffer_[7]) << 8 |
+ static_cast<uint32_t>(buffer_[8]);
+ DCHECK_EQ(stored_length, length_ - sizeof(uint8_t) - 2 * sizeof(uint32_t));
+ }
+ HandleFlush(buffer_.data(), length_);
+ buffer_.clear();
+ }
+
+ virtual void HandleFlush(const uint8_t* buffer ATTRIBUTE_UNUSED, size_t length ATTRIBUTE_UNUSED) {
+ }
+
+ std::vector<uint8_t> buffer_;
+};
+
+class FileEndianOutput FINAL : public EndianOutputBuffered {
+ public:
+ FileEndianOutput(File* fp, size_t reserved_size)
+ : EndianOutputBuffered(reserved_size), fp_(fp), errors_(false) {
+ DCHECK(fp != nullptr);
+ }
+ ~FileEndianOutput() {
+ }
+
+ bool Errors() {
+ return errors_;
+ }
+
+ protected:
+ void HandleFlush(const uint8_t* buffer, size_t length) OVERRIDE {
+ if (!errors_) {
+ errors_ = !fp_->WriteFully(buffer, length);
+ }
+ }
+
+ private:
+ File* fp_;
+ bool errors_;
+};
+
+class NetStateEndianOutput FINAL : public EndianOutputBuffered {
+ public:
+ NetStateEndianOutput(JDWP::JdwpNetStateBase* net_state, size_t reserved_size)
+ : EndianOutputBuffered(reserved_size), net_state_(net_state) {
+ DCHECK(net_state != nullptr);
+ }
+ ~NetStateEndianOutput() {}
+
+ protected:
+ void HandleFlush(const uint8_t* buffer, size_t length) OVERRIDE {
+ std::vector<iovec> iov;
+ iov.push_back(iovec());
+ iov[0].iov_base = const_cast<void*>(reinterpret_cast<const void*>(buffer));
+ iov[0].iov_len = length;
+ net_state_->WriteBufferedPacketLocked(iov);
+ }
+
+ private:
+ JDWP::JdwpNetStateBase* net_state_;
+};
+
+#define __ output->
+
class Hprof {
public:
Hprof(const char* output_filename, int fd, bool direct_to_ddms)
@@ -387,226 +412,180 @@
fd_(fd),
direct_to_ddms_(direct_to_ddms),
start_ns_(NanoTime()),
- current_record_(),
- gc_thread_serial_number_(0),
- gc_scan_state_(0),
current_heap_(HPROF_HEAP_DEFAULT),
objects_in_segment_(0),
- header_fp_(NULL),
- header_data_ptr_(NULL),
- header_data_size_(0),
- body_fp_(NULL),
- body_data_ptr_(NULL),
- body_data_size_(0),
next_string_id_(0x400000) {
LOG(INFO) << "hprof: heap dump \"" << filename_ << "\" starting...";
-
- header_fp_ = open_memstream(&header_data_ptr_, &header_data_size_);
- if (header_fp_ == NULL) {
- PLOG(FATAL) << "header open_memstream failed";
- }
-
- body_fp_ = open_memstream(&body_data_ptr_, &body_data_size_);
- if (body_fp_ == NULL) {
- PLOG(FATAL) << "body open_memstream failed";
- }
- }
-
- ~Hprof() {
- if (header_fp_ != NULL) {
- fclose(header_fp_);
- }
- if (body_fp_ != NULL) {
- fclose(body_fp_);
- }
- free(header_data_ptr_);
- free(body_data_ptr_);
}
void Dump()
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
LOCKS_EXCLUDED(Locks::heap_bitmap_lock_) {
- // Walk the roots and the heap.
- current_record_.StartNewRecord(body_fp_, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
- Runtime::Current()->VisitRoots(RootVisitor, this);
- Thread* self = Thread::Current();
+ // First pass to measure the size of the dump.
+ size_t overall_size;
+ size_t max_length;
{
- ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
- Runtime::Current()->GetHeap()->VisitObjects(VisitObjectCallback, this);
+ EndianOutput count_output;
+ ProcessHeap(&count_output, false);
+ overall_size = count_output.SumLength();
+ max_length = count_output.MaxLength();
}
- current_record_.StartNewRecord(body_fp_, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
- current_record_.Flush();
- fflush(body_fp_);
- // Write the header.
- WriteFixedHeader();
- // Write the string and class tables, and any stack traces, to the header.
- // (jhat requires that these appear before any of the data in the body that refers to them.)
- WriteStringTable();
- WriteClassTable();
- WriteStackTraces();
- current_record_.Flush();
- fflush(header_fp_);
-
- bool okay = true;
+ bool okay;
if (direct_to_ddms_) {
- // Send the data off to DDMS.
- iovec iov[2];
- iov[0].iov_base = header_data_ptr_;
- iov[0].iov_len = header_data_size_;
- iov[1].iov_base = body_data_ptr_;
- iov[1].iov_len = body_data_size_;
- Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
+ if (kDirectStream) {
+ okay = DumpToDdmsDirect(overall_size, max_length, CHUNK_TYPE("HPDS"));
+ } else {
+ okay = DumpToDdmsBuffered(overall_size, max_length);
+ }
} else {
- // Where exactly are we writing to?
- int out_fd;
- if (fd_ >= 0) {
- out_fd = dup(fd_);
- if (out_fd < 0) {
- ThrowRuntimeException("Couldn't dump heap; dup(%d) failed: %s", fd_, strerror(errno));
- return;
- }
- } else {
- out_fd = open(filename_.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644);
- if (out_fd < 0) {
- ThrowRuntimeException("Couldn't dump heap; open(\"%s\") failed: %s", filename_.c_str(),
- strerror(errno));
- return;
- }
- }
-
- std::unique_ptr<File> file(new File(out_fd, filename_, true));
- okay = file->WriteFully(header_data_ptr_, header_data_size_) &&
- file->WriteFully(body_data_ptr_, body_data_size_);
- if (okay) {
- okay = file->FlushCloseOrErase() == 0;
- } else {
- file->Erase();
- }
- if (!okay) {
- std::string msg(StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s",
- filename_.c_str(), strerror(errno)));
- ThrowRuntimeException("%s", msg.c_str());
- LOG(ERROR) << msg;
- }
+ okay = DumpToFile(overall_size, max_length);
}
- // Throw out a log message for the benefit of "runhat".
if (okay) {
uint64_t duration = NanoTime() - start_ns_;
LOG(INFO) << "hprof: heap dump completed ("
- << PrettySize(header_data_size_ + body_data_size_ + 1023)
+ << PrettySize(RoundUp(overall_size, 1024))
<< ") in " << PrettyDuration(duration);
}
}
private:
- static void RootVisitor(mirror::Object** obj, void* arg, uint32_t thread_id, RootType root_type)
+ struct Env {
+ Hprof* hprof;
+ EndianOutput* output;
+ };
+
+ static void RootVisitor(mirror::Object** obj, void* arg, const RootInfo& root_info)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(arg != nullptr);
DCHECK(obj != nullptr);
DCHECK(*obj != nullptr);
- reinterpret_cast<Hprof*>(arg)->VisitRoot(*obj, thread_id, root_type);
+ Env* env = reinterpret_cast<Env*>(arg);
+ env->hprof->VisitRoot(*obj, root_info, env->output);
}
static void VisitObjectCallback(mirror::Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(obj != NULL);
- DCHECK(arg != NULL);
- reinterpret_cast<Hprof*>(arg)->DumpHeapObject(obj);
+ DCHECK(obj != nullptr);
+ DCHECK(arg != nullptr);
+ Env* env = reinterpret_cast<Env*>(arg);
+ env->hprof->DumpHeapObject(obj, env->output);
}
- void VisitRoot(const mirror::Object* obj, uint32_t thread_id, RootType type)
+ void DumpHeapObject(mirror::Object* obj, EndianOutput* output)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- int DumpHeapObject(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void DumpHeapClass(mirror::Class* klass, EndianOutput* output)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void Finish() {
+ void DumpHeapArray(mirror::Array* obj, mirror::Class* klass, EndianOutput* output)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass, EndianOutput* output)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void ProcessHeap(EndianOutput* output, bool header_first)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Reset current heap and object count.
+ current_heap_ = HPROF_HEAP_DEFAULT;
+ objects_in_segment_ = 0;
+
+ if (header_first) {
+ ProcessHeader(output);
+ ProcessBody(output);
+ } else {
+ ProcessBody(output);
+ ProcessHeader(output);
+ }
}
- int WriteClassTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- HprofRecord* rec = ¤t_record_;
+ void ProcessBody(EndianOutput* output) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Runtime* runtime = Runtime::Current();
+ // Walk the roots and the heap.
+ output->StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, kHprofTime);
+
+ Env env = { this, output };
+ runtime->VisitRoots(RootVisitor, &env);
+ runtime->GetHeap()->VisitObjectsPaused(VisitObjectCallback, &env);
+
+ output->StartNewRecord(HPROF_TAG_HEAP_DUMP_END, kHprofTime);
+ output->EndRecord();
+ }
+
+ void ProcessHeader(EndianOutput* output) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Write the header.
+ WriteFixedHeader(output);
+ // Write the string and class tables, and any stack traces, to the header.
+ // (jhat requires that these appear before any of the data in the body that refers to them.)
+ WriteStringTable(output);
+ WriteClassTable(output);
+ WriteStackTraces(output);
+ output->EndRecord();
+ }
+
+ void WriteClassTable(EndianOutput* output) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint32_t nextSerialNumber = 1;
for (mirror::Class* c : classes_) {
CHECK(c != nullptr);
-
- int err = current_record_.StartNewRecord(header_fp_, HPROF_TAG_LOAD_CLASS, HPROF_TIME);
- if (UNLIKELY(err != 0)) {
- return err;
- }
-
+ output->StartNewRecord(HPROF_TAG_LOAD_CLASS, kHprofTime);
// LOAD CLASS format:
// U4: class serial number (always > 0)
// ID: class object ID. We use the address of the class object structure as its ID.
// U4: stack trace serial number
// ID: class name string ID
- rec->AddU4(nextSerialNumber++);
- rec->AddObjectId(c);
- rec->AddU4(HPROF_NULL_STACK_TRACE);
- rec->AddStringId(LookupClassNameId(c));
+ __ AddU4(nextSerialNumber++);
+ __ AddObjectId(c);
+ __ AddU4(kHprofNullStackTrace);
+ __ AddStringId(LookupClassNameId(c));
}
-
- return 0;
}
- int WriteStringTable() {
- HprofRecord* rec = ¤t_record_;
-
- for (std::pair<std::string, HprofStringId> p : strings_) {
+ void WriteStringTable(EndianOutput* output) {
+ for (const std::pair<std::string, HprofStringId>& p : strings_) {
const std::string& string = p.first;
- size_t id = p.second;
+ const size_t id = p.second;
- int err = current_record_.StartNewRecord(header_fp_, HPROF_TAG_STRING, HPROF_TIME);
- if (err != 0) {
- return err;
- }
+ output->StartNewRecord(HPROF_TAG_STRING, kHprofTime);
// STRING format:
// ID: ID for this string
// U1*: UTF8 characters for string (NOT NULL terminated)
// (the record format encodes the length)
- err = rec->AddU4(id);
- if (err != 0) {
- return err;
- }
- err = rec->AddUtf8String(string.c_str());
- if (err != 0) {
- return err;
- }
+ __ AddU4(id);
+ __ AddUtf8String(string.c_str());
}
-
- return 0;
}
- void StartNewHeapDumpSegment() {
+ void StartNewHeapDumpSegment(EndianOutput* output) {
// This flushes the old segment and starts a new one.
- current_record_.StartNewRecord(body_fp_, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
+ output->StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, kHprofTime);
objects_in_segment_ = 0;
-
// Starting a new HEAP_DUMP resets the heap to default.
current_heap_ = HPROF_HEAP_DEFAULT;
}
- int MarkRootObject(const mirror::Object* obj, jobject jniObj);
+ void CheckHeapSegmentConstraints(EndianOutput* output) {
+ if (objects_in_segment_ >= kMaxObjectsPerSegment || output->Length() >= kMaxBytesPerSegment) {
+ StartNewHeapDumpSegment(output);
+ }
+ }
+
+ void VisitRoot(const mirror::Object* obj, const RootInfo& root_info, EndianOutput* output)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void MarkRootObject(const mirror::Object* obj, jobject jni_obj, HprofHeapTag heap_tag,
+ uint32_t thread_serial, EndianOutput* output);
HprofClassObjectId LookupClassId(mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (c == nullptr) {
- // c is the superclass of java.lang.Object or a primitive.
- return 0;
- }
-
- {
+ if (c != nullptr) {
auto result = classes_.insert(c);
const mirror::Class* present = *result.first;
CHECK_EQ(present, c);
+ // Make sure that we've assigned a string ID for this class' name
+ LookupClassNameId(c);
}
-
- // Make sure that we've assigned a string ID for this class' name
- LookupClassNameId(c);
-
- HprofClassObjectId result = PointerToLowMemUInt32(c);
- return result;
+ return PointerToLowMemUInt32(c);
}
HprofStringId LookupStringId(mirror::String* string) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -631,46 +610,125 @@
return LookupStringId(PrettyDescriptor(c));
}
- void WriteFixedHeader() {
- char magic[] = "JAVA PROFILE 1.0.3";
- unsigned char buf[4];
-
+ void WriteFixedHeader(EndianOutput* output) {
// Write the file header.
// U1: NUL-terminated magic string.
- fwrite(magic, 1, sizeof(magic), header_fp_);
+ const char magic[] = "JAVA PROFILE 1.0.3";
+ __ AddU1List(reinterpret_cast<const uint8_t*>(magic), sizeof(magic));
// U4: size of identifiers. We're using addresses as IDs and our heap references are stored
// as uint32_t.
// Note of warning: hprof-conv hard-codes the size of identifiers to 4.
static_assert(sizeof(mirror::HeapReference<mirror::Object>) == sizeof(uint32_t),
"Unexpected HeapReference size");
- U4_TO_BUF_BE(buf, 0, sizeof(uint32_t));
- fwrite(buf, 1, sizeof(uint32_t), header_fp_);
+ __ AddU4(sizeof(uint32_t));
// The current time, in milliseconds since 0:00 GMT, 1/1/70.
timeval now;
- uint64_t nowMs;
- if (gettimeofday(&now, NULL) < 0) {
- nowMs = 0;
- } else {
- nowMs = (uint64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
- }
-
+ const uint64_t nowMs = (gettimeofday(&now, nullptr) < 0) ? 0 :
+ (uint64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
+ // TODO: It seems it would be correct to use U8.
// U4: high word of the 64-bit time.
- U4_TO_BUF_BE(buf, 0, (uint32_t)(nowMs >> 32));
- fwrite(buf, 1, sizeof(uint32_t), header_fp_);
-
+ __ AddU4(static_cast<uint32_t>(nowMs >> 32));
// U4: low word of the 64-bit time.
- U4_TO_BUF_BE(buf, 0, (uint32_t)(nowMs & 0xffffffffULL));
- fwrite(buf, 1, sizeof(uint32_t), header_fp_); // xxx fix the time
+ __ AddU4(static_cast<uint32_t>(nowMs & 0xFFFFFFFF));
}
- void WriteStackTraces() {
+ void WriteStackTraces(EndianOutput* output) {
// Write a dummy stack trace record so the analysis tools don't freak out.
- current_record_.StartNewRecord(header_fp_, HPROF_TAG_STACK_TRACE, HPROF_TIME);
- current_record_.AddU4(HPROF_NULL_STACK_TRACE);
- current_record_.AddU4(HPROF_NULL_THREAD);
- current_record_.AddU4(0); // no frames
+ output->StartNewRecord(HPROF_TAG_STACK_TRACE, kHprofTime);
+ __ AddU4(kHprofNullStackTrace);
+ __ AddU4(kHprofNullThread);
+ __ AddU4(0); // no frames
+ }
+
+ bool DumpToDdmsBuffered(size_t overall_size ATTRIBUTE_UNUSED, size_t max_length ATTRIBUTE_UNUSED)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ LOG(FATAL) << "Unimplemented";
+ UNREACHABLE();
+ // // Send the data off to DDMS.
+ // iovec iov[2];
+ // iov[0].iov_base = header_data_ptr_;
+ // iov[0].iov_len = header_data_size_;
+ // iov[1].iov_base = body_data_ptr_;
+ // iov[1].iov_len = body_data_size_;
+ // Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
+ }
+
+ bool DumpToFile(size_t overall_size, size_t max_length)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Where exactly are we writing to?
+ int out_fd;
+ if (fd_ >= 0) {
+ out_fd = dup(fd_);
+ if (out_fd < 0) {
+ ThrowRuntimeException("Couldn't dump heap; dup(%d) failed: %s", fd_, strerror(errno));
+ return false;
+ }
+ } else {
+ out_fd = open(filename_.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if (out_fd < 0) {
+ ThrowRuntimeException("Couldn't dump heap; open(\"%s\") failed: %s", filename_.c_str(),
+ strerror(errno));
+ return false;
+ }
+ }
+
+ std::unique_ptr<File> file(new File(out_fd, filename_, true));
+ bool okay;
+ {
+ FileEndianOutput file_output(file.get(), max_length);
+ ProcessHeap(&file_output, true);
+ okay = !file_output.Errors();
+
+ if (okay) {
+ // Check for expected size.
+ CHECK_EQ(file_output.SumLength(), overall_size);
+ }
+ }
+
+ if (okay) {
+ okay = file->FlushCloseOrErase() == 0;
+ } else {
+ file->Erase();
+ }
+ if (!okay) {
+ std::string msg(StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s",
+ filename_.c_str(), strerror(errno)));
+ ThrowRuntimeException("%s", msg.c_str());
+ LOG(ERROR) << msg;
+ }
+
+ return okay;
+ }
+
+ bool DumpToDdmsDirect(size_t overall_size, size_t max_length, uint32_t chunk_type)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ CHECK(direct_to_ddms_);
+ JDWP::JdwpState* state = Dbg::GetJdwpState();
+ CHECK(state != nullptr);
+ JDWP::JdwpNetStateBase* net_state = state->netState;
+ CHECK(net_state != nullptr);
+
+ // Hold the socket lock for the whole time since we want this to be atomic.
+ MutexLock mu(Thread::Current(), *net_state->GetSocketLock());
+
+ // Prepare the Ddms chunk.
+ constexpr size_t kChunkHeaderSize = kJDWPHeaderLen + 8;
+ uint8_t chunk_header[kChunkHeaderSize] = { 0 };
+ state->SetupChunkHeader(chunk_type, overall_size, kChunkHeaderSize, chunk_header);
+
+ // Prepare the output and send the chunk header.
+ NetStateEndianOutput net_output(net_state, max_length);
+ net_output.AddU1List(chunk_header, kChunkHeaderSize);
+
+ // Write the dump.
+ ProcessHeap(&net_output, true);
+
+ // Check for expected size.
+ CHECK_EQ(net_output.SumLength(), overall_size + kChunkHeaderSize);
+
+ return true;
}
// If direct_to_ddms_ is set, "filename_" and "fd" will be ignored.
@@ -682,21 +740,9 @@
uint64_t start_ns_;
- HprofRecord current_record_;
-
- uint32_t gc_thread_serial_number_;
- uint8_t gc_scan_state_;
HprofHeapId current_heap_; // Which heap we're currently dumping.
size_t objects_in_segment_;
- FILE* header_fp_;
- char* header_data_ptr_;
- size_t header_data_size_;
-
- FILE* body_fp_;
- char* body_data_ptr_;
- size_t body_data_size_;
-
std::set<mirror::Class*> classes_;
HprofStringId next_string_id_;
SafeMap<std::string, HprofStringId> strings_;
@@ -704,56 +750,56 @@
DISALLOW_COPY_AND_ASSIGN(Hprof);
};
-#define OBJECTS_PER_SEGMENT ((size_t)128)
-#define BYTES_PER_SEGMENT ((size_t)4096)
-
-// The static field-name for the synthetic object generated to account for class static overhead.
-#define STATIC_OVERHEAD_NAME "$staticOverhead"
-
-static HprofBasicType SignatureToBasicTypeAndSize(const char* sig, size_t* sizeOut) {
+static HprofBasicType SignatureToBasicTypeAndSize(const char* sig, size_t* size_out) {
char c = sig[0];
HprofBasicType ret;
size_t size;
switch (c) {
- case '[':
- case 'L': ret = hprof_basic_object; size = 4; break;
- case 'Z': ret = hprof_basic_boolean; size = 1; break;
- case 'C': ret = hprof_basic_char; size = 2; break;
- case 'F': ret = hprof_basic_float; size = 4; break;
- case 'D': ret = hprof_basic_double; size = 8; break;
- case 'B': ret = hprof_basic_byte; size = 1; break;
- case 'S': ret = hprof_basic_short; size = 2; break;
- case 'I': ret = hprof_basic_int; size = 4; break;
- case 'J': ret = hprof_basic_long; size = 8; break;
- default: LOG(FATAL) << "UNREACHABLE"; UNREACHABLE();
+ case '[':
+ case 'L':
+ ret = hprof_basic_object;
+ size = 4;
+ break;
+ case 'Z':
+ ret = hprof_basic_boolean;
+ size = 1;
+ break;
+ case 'C':
+ ret = hprof_basic_char;
+ size = 2;
+ break;
+ case 'F':
+ ret = hprof_basic_float;
+ size = 4;
+ break;
+ case 'D':
+ ret = hprof_basic_double;
+ size = 8;
+ break;
+ case 'B':
+ ret = hprof_basic_byte;
+ size = 1;
+ break;
+ case 'S':
+ ret = hprof_basic_short;
+ size = 2;
+ break;
+ case 'I':
+ ret = hprof_basic_int;
+ size = 4;
+ break;
+ case 'J':
+ ret = hprof_basic_long;
+ size = 8;
+ break;
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ UNREACHABLE();
}
- if (sizeOut != NULL) {
- *sizeOut = size;
- }
-
- return ret;
-}
-
-static HprofBasicType PrimitiveToBasicTypeAndSize(Primitive::Type prim, size_t* sizeOut) {
- HprofBasicType ret;
- size_t size;
-
- switch (prim) {
- case Primitive::kPrimBoolean: ret = hprof_basic_boolean; size = 1; break;
- case Primitive::kPrimChar: ret = hprof_basic_char; size = 2; break;
- case Primitive::kPrimFloat: ret = hprof_basic_float; size = 4; break;
- case Primitive::kPrimDouble: ret = hprof_basic_double; size = 8; break;
- case Primitive::kPrimByte: ret = hprof_basic_byte; size = 1; break;
- case Primitive::kPrimShort: ret = hprof_basic_short; size = 2; break;
- case Primitive::kPrimInt: ret = hprof_basic_int; size = 4; break;
- case Primitive::kPrimLong: ret = hprof_basic_long; size = 8; break;
- default: LOG(FATAL) << "UNREACHABLE"; UNREACHABLE();
- }
-
- if (sizeOut != NULL) {
- *sizeOut = size;
+ if (size_out != nullptr) {
+ *size_out = size;
}
return ret;
@@ -763,95 +809,94 @@
// something when ctx->gc_scan_state_ is non-zero, which is usually
// only true when marking the root set or unreachable
// objects. Used to add rootset references to obj.
-int Hprof::MarkRootObject(const mirror::Object* obj, jobject jniObj) {
- HprofRecord* rec = ¤t_record_;
- HprofHeapTag heapTag = (HprofHeapTag)gc_scan_state_;
-
- if (heapTag == 0) {
- return 0;
+void Hprof::MarkRootObject(const mirror::Object* obj, jobject jni_obj, HprofHeapTag heap_tag,
+ uint32_t thread_serial, EndianOutput* output) {
+ if (heap_tag == 0) {
+ return;
}
- if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->Size() >= BYTES_PER_SEGMENT) {
- StartNewHeapDumpSegment();
- }
+ CheckHeapSegmentConstraints(output);
- switch (heapTag) {
- // ID: object ID
- case HPROF_ROOT_UNKNOWN:
- case HPROF_ROOT_STICKY_CLASS:
- case HPROF_ROOT_MONITOR_USED:
- case HPROF_ROOT_INTERNED_STRING:
- case HPROF_ROOT_DEBUGGER:
- case HPROF_ROOT_VM_INTERNAL:
- rec->AddU1(heapTag);
- rec->AddObjectId(obj);
- break;
+ switch (heap_tag) {
+ // ID: object ID
+ case HPROF_ROOT_UNKNOWN:
+ case HPROF_ROOT_STICKY_CLASS:
+ case HPROF_ROOT_MONITOR_USED:
+ case HPROF_ROOT_INTERNED_STRING:
+ case HPROF_ROOT_DEBUGGER:
+ case HPROF_ROOT_VM_INTERNAL:
+ __ AddU1(heap_tag);
+ __ AddObjectId(obj);
+ break;
- // ID: object ID
- // ID: JNI global ref ID
- case HPROF_ROOT_JNI_GLOBAL:
- rec->AddU1(heapTag);
- rec->AddObjectId(obj);
- rec->AddJniGlobalRefId(jniObj);
- break;
+ // ID: object ID
+ // ID: JNI global ref ID
+ case HPROF_ROOT_JNI_GLOBAL:
+ __ AddU1(heap_tag);
+ __ AddObjectId(obj);
+ __ AddJniGlobalRefId(jni_obj);
+ break;
- // ID: object ID
- // U4: thread serial number
- // U4: frame number in stack trace (-1 for empty)
- case HPROF_ROOT_JNI_LOCAL:
- case HPROF_ROOT_JNI_MONITOR:
- case HPROF_ROOT_JAVA_FRAME:
- rec->AddU1(heapTag);
- rec->AddObjectId(obj);
- rec->AddU4(gc_thread_serial_number_);
- rec->AddU4((uint32_t)-1);
- break;
+ // ID: object ID
+ // U4: thread serial number
+ // U4: frame number in stack trace (-1 for empty)
+ case HPROF_ROOT_JNI_LOCAL:
+ case HPROF_ROOT_JNI_MONITOR:
+ case HPROF_ROOT_JAVA_FRAME:
+ __ AddU1(heap_tag);
+ __ AddObjectId(obj);
+ __ AddU4(thread_serial);
+ __ AddU4((uint32_t)-1);
+ break;
- // ID: object ID
- // U4: thread serial number
- case HPROF_ROOT_NATIVE_STACK:
- case HPROF_ROOT_THREAD_BLOCK:
- rec->AddU1(heapTag);
- rec->AddObjectId(obj);
- rec->AddU4(gc_thread_serial_number_);
- break;
+ // ID: object ID
+ // U4: thread serial number
+ case HPROF_ROOT_NATIVE_STACK:
+ case HPROF_ROOT_THREAD_BLOCK:
+ __ AddU1(heap_tag);
+ __ AddObjectId(obj);
+ __ AddU4(thread_serial);
+ break;
- // ID: thread object ID
- // U4: thread serial number
- // U4: stack trace serial number
- case HPROF_ROOT_THREAD_OBJECT:
- rec->AddU1(heapTag);
- rec->AddObjectId(obj);
- rec->AddU4(gc_thread_serial_number_);
- rec->AddU4((uint32_t)-1); // xxx
- break;
+ // ID: thread object ID
+ // U4: thread serial number
+ // U4: stack trace serial number
+ case HPROF_ROOT_THREAD_OBJECT:
+ __ AddU1(heap_tag);
+ __ AddObjectId(obj);
+ __ AddU4(thread_serial);
+ __ AddU4((uint32_t)-1); // xxx
+ break;
- case HPROF_CLASS_DUMP:
- case HPROF_INSTANCE_DUMP:
- case HPROF_OBJECT_ARRAY_DUMP:
- case HPROF_PRIMITIVE_ARRAY_DUMP:
- case HPROF_HEAP_DUMP_INFO:
- case HPROF_PRIMITIVE_ARRAY_NODATA_DUMP:
- // Ignored.
- break;
+ case HPROF_CLASS_DUMP:
+ case HPROF_INSTANCE_DUMP:
+ case HPROF_OBJECT_ARRAY_DUMP:
+ case HPROF_PRIMITIVE_ARRAY_DUMP:
+ case HPROF_HEAP_DUMP_INFO:
+ case HPROF_PRIMITIVE_ARRAY_NODATA_DUMP:
+ // Ignored.
+ break;
- case HPROF_ROOT_FINALIZING:
- case HPROF_ROOT_REFERENCE_CLEANUP:
- case HPROF_UNREACHABLE:
- LOG(FATAL) << "obsolete tag " << static_cast<int>(heapTag);
- break;
+ case HPROF_ROOT_FINALIZING:
+ case HPROF_ROOT_REFERENCE_CLEANUP:
+ case HPROF_UNREACHABLE:
+ LOG(FATAL) << "obsolete tag " << static_cast<int>(heap_tag);
+ break;
}
++objects_in_segment_;
- return 0;
}
static int StackTraceSerialNumber(const mirror::Object* /*obj*/) {
- return HPROF_NULL_STACK_TRACE;
+ return kHprofNullStackTrace;
}
-int Hprof::DumpHeapObject(mirror::Object* obj) {
- HprofRecord* rec = ¤t_record_;
+void Hprof::DumpHeapObject(mirror::Object* obj, EndianOutput* output) {
+ // Ignore classes that are retired.
+ if (obj->IsClass() && obj->AsClass()->IsRetired()) {
+ return;
+ }
+
gc::space::ContinuousSpace* space =
Runtime::Current()->GetHeap()->FindContinuousSpaceFromObject(obj, true);
HprofHeapId heap_type = HPROF_HEAP_APP;
@@ -862,17 +907,15 @@
heap_type = HPROF_HEAP_IMAGE;
}
}
- if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->Size() >= BYTES_PER_SEGMENT) {
- StartNewHeapDumpSegment();
- }
+ CheckHeapSegmentConstraints(output);
if (heap_type != current_heap_) {
HprofStringId nameId;
// This object is in a different heap than the current one.
// Emit a HEAP_DUMP_INFO tag to change heaps.
- rec->AddU1(HPROF_HEAP_DUMP_INFO);
- rec->AddU4(static_cast<uint32_t>(heap_type)); // uint32_t: heap type
+ __ AddU1(HPROF_HEAP_DUMP_INFO);
+ __ AddU4(static_cast<uint32_t>(heap_type)); // uint32_t: heap type
switch (heap_type) {
case HPROF_HEAP_APP:
nameId = LookupStringId("app");
@@ -889,179 +932,194 @@
nameId = LookupStringId("<ILLEGAL>");
break;
}
- rec->AddStringId(nameId);
+ __ AddStringId(nameId);
current_heap_ = heap_type;
}
mirror::Class* c = obj->GetClass();
- if (c == NULL) {
+ if (c == nullptr) {
// This object will bother HprofReader, because it has a NULL
// class, so just don't dump it. It could be
// gDvm.unlinkedJavaLangClass or it could be an object just
// allocated which hasn't been initialized yet.
} else {
if (obj->IsClass()) {
- mirror::Class* thisClass = obj->AsClass();
- // obj is a ClassObject.
- size_t sFieldCount = thisClass->NumStaticFields();
- if (sFieldCount != 0) {
- int byteLength = sFieldCount * sizeof(JValue); // TODO bogus; fields are packed
- // Create a byte array to reflect the allocation of the
- // StaticField array at the end of this class.
- rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
- rec->AddClassStaticsId(thisClass);
- rec->AddU4(StackTraceSerialNumber(obj));
- rec->AddU4(byteLength);
- rec->AddU1(hprof_basic_byte);
- for (int i = 0; i < byteLength; ++i) {
- rec->AddU1(0);
- }
- }
-
- rec->AddU1(HPROF_CLASS_DUMP);
- rec->AddClassId(LookupClassId(thisClass));
- rec->AddU4(StackTraceSerialNumber(thisClass));
- rec->AddClassId(LookupClassId(thisClass->GetSuperClass()));
- rec->AddObjectId(thisClass->GetClassLoader());
- rec->AddObjectId(nullptr); // no signer
- rec->AddObjectId(nullptr); // no prot domain
- rec->AddObjectId(nullptr); // reserved
- rec->AddObjectId(nullptr); // reserved
- if (thisClass->IsClassClass()) {
- // ClassObjects have their static fields appended, so aren't all the same size.
- // But they're at least this size.
- rec->AddU4(sizeof(mirror::Class)); // instance size
- } else if (thisClass->IsArrayClass() || thisClass->IsPrimitive()) {
- rec->AddU4(0);
- } else {
- rec->AddU4(thisClass->GetObjectSize()); // instance size
- }
-
- rec->AddU2(0); // empty const pool
-
- // Static fields
- if (sFieldCount == 0) {
- rec->AddU2((uint16_t)0);
- } else {
- rec->AddU2((uint16_t)(sFieldCount+1));
- rec->AddStringId(LookupStringId(STATIC_OVERHEAD_NAME));
- rec->AddU1(hprof_basic_object);
- rec->AddClassStaticsId(thisClass);
-
- for (size_t i = 0; i < sFieldCount; ++i) {
- mirror::ArtField* f = thisClass->GetStaticField(i);
-
- size_t size;
- HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
- rec->AddStringId(LookupStringId(f->GetName()));
- rec->AddU1(t);
- if (size == 1) {
- rec->AddU1(static_cast<uint8_t>(f->Get32(thisClass)));
- } else if (size == 2) {
- rec->AddU2(static_cast<uint16_t>(f->Get32(thisClass)));
- } else if (size == 4) {
- rec->AddU4(f->Get32(thisClass));
- } else if (size == 8) {
- rec->AddU8(f->Get64(thisClass));
- } else {
- CHECK(false);
- }
- }
- }
-
- // Instance fields for this class (no superclass fields)
- int iFieldCount = thisClass->IsObjectClass() ? 0 : thisClass->NumInstanceFields();
- rec->AddU2((uint16_t)iFieldCount);
- for (int i = 0; i < iFieldCount; ++i) {
- mirror::ArtField* f = thisClass->GetInstanceField(i);
- HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), NULL);
- rec->AddStringId(LookupStringId(f->GetName()));
- rec->AddU1(t);
- }
+ DumpHeapClass(obj->AsClass(), output);
} else if (c->IsArrayClass()) {
- mirror::Array* aobj = obj->AsArray();
- uint32_t length = aobj->GetLength();
-
- if (obj->IsObjectArray()) {
- // obj is an object array.
- rec->AddU1(HPROF_OBJECT_ARRAY_DUMP);
-
- rec->AddObjectId(obj);
- rec->AddU4(StackTraceSerialNumber(obj));
- rec->AddU4(length);
- rec->AddClassId(LookupClassId(c));
-
- // Dump the elements, which are always objects or NULL.
- rec->AddIdList(aobj->AsObjectArray<mirror::Object>());
- } else {
- size_t size;
- HprofBasicType t = PrimitiveToBasicTypeAndSize(c->GetComponentType()->GetPrimitiveType(), &size);
-
- // obj is a primitive array.
- rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
-
- rec->AddObjectId(obj);
- rec->AddU4(StackTraceSerialNumber(obj));
- rec->AddU4(length);
- rec->AddU1(t);
-
- // Dump the raw, packed element values.
- if (size == 1) {
- rec->AddU1List((const uint8_t*)aobj->GetRawData(sizeof(uint8_t), 0), length);
- } else if (size == 2) {
- rec->AddU2List((const uint16_t*)aobj->GetRawData(sizeof(uint16_t), 0), length);
- } else if (size == 4) {
- rec->AddU4List((const uint32_t*)aobj->GetRawData(sizeof(uint32_t), 0), length);
- } else if (size == 8) {
- rec->AddU8List((const uint64_t*)aobj->GetRawData(sizeof(uint64_t), 0), length);
- }
- }
+ DumpHeapArray(obj->AsArray(), c, output);
} else {
- // obj is an instance object.
- rec->AddU1(HPROF_INSTANCE_DUMP);
- rec->AddObjectId(obj);
- rec->AddU4(StackTraceSerialNumber(obj));
- rec->AddClassId(LookupClassId(c));
-
- // Reserve some space for the length of the instance data, which we won't
- // know until we're done writing it.
- size_t size_patch_offset = rec->Size();
- rec->AddU4(0x77777777);
-
- // Write the instance data; fields for this class, followed by super class fields,
- // and so on. Don't write the klass or monitor fields of Object.class.
- mirror::Class* sclass = c;
- while (!sclass->IsObjectClass()) {
- int ifieldCount = sclass->NumInstanceFields();
- for (int i = 0; i < ifieldCount; ++i) {
- mirror::ArtField* f = sclass->GetInstanceField(i);
- size_t size;
- SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
- if (size == 1) {
- rec->AddU1(f->Get32(obj));
- } else if (size == 2) {
- rec->AddU2(f->Get32(obj));
- } else if (size == 4) {
- rec->AddU4(f->Get32(obj));
- } else {
- CHECK_EQ(size, 8U);
- rec->AddU8(f->Get64(obj));
- }
- }
-
- sclass = sclass->GetSuperClass();
- }
-
- // Patch the instance field length.
- rec->UpdateU4(size_patch_offset, rec->Size() - (size_patch_offset + 4));
+ DumpHeapInstanceObject(obj, c, output);
}
}
++objects_in_segment_;
- return 0;
}
-void Hprof::VisitRoot(const mirror::Object* obj, uint32_t thread_id, RootType type) {
+void Hprof::DumpHeapClass(mirror::Class* klass, EndianOutput* output) {
+ size_t sFieldCount = klass->NumStaticFields();
+ if (sFieldCount != 0) {
+ int byteLength = sFieldCount * sizeof(JValue); // TODO bogus; fields are packed
+ // Create a byte array to reflect the allocation of the
+ // StaticField array at the end of this class.
+ __ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
+ __ AddClassStaticsId(klass);
+ __ AddU4(StackTraceSerialNumber(klass));
+ __ AddU4(byteLength);
+ __ AddU1(hprof_basic_byte);
+ for (int i = 0; i < byteLength; ++i) {
+ __ AddU1(0);
+ }
+ }
+
+ __ AddU1(HPROF_CLASS_DUMP);
+ __ AddClassId(LookupClassId(klass));
+ __ AddU4(StackTraceSerialNumber(klass));
+ __ AddClassId(LookupClassId(klass->GetSuperClass()));
+ __ AddObjectId(klass->GetClassLoader());
+ __ AddObjectId(nullptr); // no signer
+ __ AddObjectId(nullptr); // no prot domain
+ __ AddObjectId(nullptr); // reserved
+ __ AddObjectId(nullptr); // reserved
+ if (klass->IsClassClass()) {
+ // ClassObjects have their static fields appended, so aren't all the same size.
+ // But they're at least this size.
+ __ AddU4(sizeof(mirror::Class)); // instance size
+ } else if (klass->IsArrayClass() || klass->IsPrimitive()) {
+ __ AddU4(0);
+ } else {
+ __ AddU4(klass->GetObjectSize()); // instance size
+ }
+
+ __ AddU2(0); // empty const pool
+
+ // Static fields
+ if (sFieldCount == 0) {
+ __ AddU2((uint16_t)0);
+ } else {
+ __ AddU2((uint16_t)(sFieldCount+1));
+ __ AddStringId(LookupStringId(kStaticOverheadName));
+ __ AddU1(hprof_basic_object);
+ __ AddClassStaticsId(klass);
+
+ for (size_t i = 0; i < sFieldCount; ++i) {
+ mirror::ArtField* f = klass->GetStaticField(i);
+
+ size_t size;
+ HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
+ __ AddStringId(LookupStringId(f->GetName()));
+ __ AddU1(t);
+ switch (size) {
+ case 1:
+ __ AddU1(static_cast<uint8_t>(f->Get32(klass)));
+ break;
+ case 2:
+ __ AddU2(static_cast<uint16_t>(f->Get32(klass)));
+ break;
+ case 4:
+ __ AddU4(f->Get32(klass));
+ break;
+ case 8:
+ __ AddU8(f->Get64(klass));
+ break;
+ default:
+ LOG(FATAL) << "Unexpected size " << size;
+ UNREACHABLE();
+ }
+ }
+ }
+
+ // Instance fields for this class (no superclass fields)
+ int iFieldCount = klass->IsObjectClass() ? 0 : klass->NumInstanceFields();
+ __ AddU2((uint16_t)iFieldCount);
+ for (int i = 0; i < iFieldCount; ++i) {
+ mirror::ArtField* f = klass->GetInstanceField(i);
+ __ AddStringId(LookupStringId(f->GetName()));
+ HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), nullptr);
+ __ AddU1(t);
+ }
+}
+
+void Hprof::DumpHeapArray(mirror::Array* obj, mirror::Class* klass, EndianOutput* output) {
+ uint32_t length = obj->GetLength();
+
+ if (obj->IsObjectArray()) {
+ // obj is an object array.
+ __ AddU1(HPROF_OBJECT_ARRAY_DUMP);
+
+ __ AddObjectId(obj);
+ __ AddU4(StackTraceSerialNumber(obj));
+ __ AddU4(length);
+ __ AddClassId(LookupClassId(klass));
+
+ // Dump the elements, which are always objects or NULL.
+ __ AddIdList(obj->AsObjectArray<mirror::Object>());
+ } else {
+ size_t size;
+ HprofBasicType t = SignatureToBasicTypeAndSize(
+ Primitive::Descriptor(klass->GetComponentType()->GetPrimitiveType()), &size);
+
+ // obj is a primitive array.
+ __ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
+
+ __ AddObjectId(obj);
+ __ AddU4(StackTraceSerialNumber(obj));
+ __ AddU4(length);
+ __ AddU1(t);
+
+ // Dump the raw, packed element values.
+ if (size == 1) {
+ __ AddU1List(reinterpret_cast<const uint8_t*>(obj->GetRawData(sizeof(uint8_t), 0)), length);
+ } else if (size == 2) {
+ __ AddU2List(reinterpret_cast<const uint16_t*>(obj->GetRawData(sizeof(uint16_t), 0)), length);
+ } else if (size == 4) {
+ __ AddU4List(reinterpret_cast<const uint32_t*>(obj->GetRawData(sizeof(uint32_t), 0)), length);
+ } else if (size == 8) {
+ __ AddU8List(reinterpret_cast<const uint64_t*>(obj->GetRawData(sizeof(uint64_t), 0)), length);
+ }
+ }
+}
+
+void Hprof::DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass,
+ EndianOutput* output) {
+ // obj is an instance object.
+ __ AddU1(HPROF_INSTANCE_DUMP);
+ __ AddObjectId(obj);
+ __ AddU4(StackTraceSerialNumber(obj));
+ __ AddClassId(LookupClassId(klass));
+
+ // Reserve some space for the length of the instance data, which we won't
+ // know until we're done writing it.
+ size_t size_patch_offset = output->Length();
+ __ AddU4(0x77777777);
+
+ // Write the instance data; fields for this class, followed by super class fields,
+ // and so on. Don't write the klass or monitor fields of Object.class.
+ while (!klass->IsObjectClass()) {
+ int ifieldCount = klass->NumInstanceFields();
+ for (int i = 0; i < ifieldCount; ++i) {
+ mirror::ArtField* f = klass->GetInstanceField(i);
+ size_t size;
+ SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
+ if (size == 1) {
+ __ AddU1(f->Get32(obj));
+ } else if (size == 2) {
+ __ AddU2(f->Get32(obj));
+ } else if (size == 4) {
+ __ AddU4(f->Get32(obj));
+ } else {
+ CHECK_EQ(size, 8U);
+ __ AddU8(f->Get64(obj));
+ }
+ }
+
+ klass = klass->GetSuperClass();
+ }
+
+ // Patch the instance field length.
+ __ UpdateU4(size_patch_offset, output->Length() - (size_patch_offset + 4));
+}
+
+void Hprof::VisitRoot(const mirror::Object* obj, const RootInfo& info, EndianOutput* output) {
static const HprofHeapTag xlate[] = {
HPROF_ROOT_UNKNOWN,
HPROF_ROOT_JNI_GLOBAL,
@@ -1079,15 +1137,11 @@
HPROF_ROOT_VM_INTERNAL,
HPROF_ROOT_JNI_MONITOR,
};
- CHECK_LT(type, sizeof(xlate) / sizeof(HprofHeapTag));
- if (obj == NULL) {
+ CHECK_LT(info.GetType(), sizeof(xlate) / sizeof(HprofHeapTag));
+ if (obj == nullptr) {
return;
}
- gc_scan_state_ = xlate[type];
- gc_thread_serial_number_ = thread_id;
- MarkRootObject(obj, 0);
- gc_scan_state_ = 0;
- gc_thread_serial_number_ = 0;
+ MarkRootObject(obj, 0, xlate[info.GetType()], info.GetThreadId(), output);
}
// If "direct_to_ddms" is true, the other arguments are ignored, and data is
@@ -1095,14 +1149,23 @@
// If "fd" is >= 0, the output will be written to that file descriptor.
// Otherwise, "filename" is used to create an output file.
void DumpHeap(const char* filename, int fd, bool direct_to_ddms) {
- CHECK(filename != NULL);
+ 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);
+ }
Runtime::Current()->GetThreadList()->SuspendAll();
Hprof hprof(filename, fd, direct_to_ddms);
hprof.Dump();
Runtime::Current()->GetThreadList()->ResumeAll();
+ if (heap->IsGcConcurrentAndMoving()) {
+ heap->DecrementDisableMovingGC(self);
+ }
}
} // namespace hprof
-
} // namespace art
diff --git a/runtime/image.cc b/runtime/image.cc
index b83eeb1..269a07d 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -111,7 +111,17 @@
}
mirror::ObjectArray<mirror::Object>* ImageHeader::GetImageRoots() const {
- return reinterpret_cast<mirror::ObjectArray<mirror::Object>*>(image_roots_);
+ // Need a read barrier as it's not visited during root scan.
+ // Pass in the address of the local variable to the read barrier
+ // rather than image_roots_ because it won't move (asserted below)
+ // and it's a const member.
+ mirror::ObjectArray<mirror::Object>* image_roots =
+ reinterpret_cast<mirror::ObjectArray<mirror::Object>*>(image_roots_);
+ mirror::ObjectArray<mirror::Object>* result =
+ ReadBarrier::BarrierForRoot<mirror::ObjectArray<mirror::Object>, kWithReadBarrier, true>(
+ &image_roots);
+ DCHECK_EQ(image_roots, result);
+ return result;
}
} // namespace art
diff --git a/runtime/image.h b/runtime/image.h
index 7e2b847..3c527b8 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -118,7 +118,8 @@
mirror::Object* GetImageRoot(ImageRoot image_root) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- mirror::ObjectArray<mirror::Object>* GetImageRoots() const;
+ mirror::ObjectArray<mirror::Object>* GetImageRoots() const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void RelocateImage(off_t delta);
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 0d84a1e..aa2a6b5 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -242,15 +242,15 @@
madvise(release_start, release_end - release_start, MADV_DONTNEED);
}
-void IndirectReferenceTable::VisitRoots(RootCallback* callback, void* arg, uint32_t tid,
- RootType root_type) {
+void IndirectReferenceTable::VisitRoots(RootCallback* callback, void* arg,
+ const RootInfo& root_info) {
for (auto ref : *this) {
if (*ref == nullptr) {
// Need to skip null entries to make it possible to do the
// non-null check after the call back.
continue;
}
- callback(ref, arg, tid, root_type);
+ callback(ref, arg, root_info);
DCHECK(*ref != nullptr);
}
}
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index fbd5714..7f7870a 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -31,6 +31,8 @@
namespace art {
+class RootInfo;
+
namespace mirror {
class Object;
} // namespace mirror
@@ -316,7 +318,7 @@
return IrtIterator(table_, Capacity(), Capacity());
}
- void VisitRoots(RootCallback* callback, void* arg, uint32_t tid, RootType root_type)
+ void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
uint32_t GetSegmentState() const {
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index 99ee597..1156cf5 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -43,6 +43,9 @@
}
TEST_F(IndirectReferenceTableTest, BasicTest) {
+ // This will lead to error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
ScopedObjectAccess soa(Thread::Current());
static const size_t kTableInitial = 10;
static const size_t kTableMax = 20;
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 639b0f0..90115c3 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -54,9 +54,10 @@
static constexpr bool kDeoptimizeForAccurateMethodEntryExitListeners = true;
static bool InstallStubsClassVisitor(mirror::Class* klass, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
- return instrumentation->InstallStubsForClass(klass);
+ instrumentation->InstallStubsForClass(klass);
+ return true; // we visit all classes.
}
Instrumentation::Instrumentation()
@@ -73,40 +74,31 @@
quick_alloc_entry_points_instrumentation_counter_(0) {
}
-bool Instrumentation::InstallStubsForClass(mirror::Class* klass) {
- for (size_t i = 0, e = klass->NumDirectMethods(); i < e; i++) {
- InstallStubsForMethod(klass->GetDirectMethod(i));
+void Instrumentation::InstallStubsForClass(mirror::Class* klass) {
+ if (klass->IsErroneous()) {
+ // We can't execute code in a erroneous class: do nothing.
+ } else if (!klass->IsResolved()) {
+ // We need the class to be resolved to install/uninstall stubs. Otherwise its methods
+ // could not be initialized or linked with regards to class inheritance.
+ } else {
+ for (size_t i = 0, e = klass->NumDirectMethods(); i < e; i++) {
+ InstallStubsForMethod(klass->GetDirectMethod(i));
+ }
+ for (size_t i = 0, e = klass->NumVirtualMethods(); i < e; i++) {
+ InstallStubsForMethod(klass->GetVirtualMethod(i));
+ }
}
- for (size_t i = 0, e = klass->NumVirtualMethods(); i < e; i++) {
- InstallStubsForMethod(klass->GetVirtualMethod(i));
- }
- return true;
}
-static void UpdateEntrypoints(mirror::ArtMethod* method, const void* quick_code,
- const void* portable_code, bool have_portable_code)
+static void UpdateEntrypoints(mirror::ArtMethod* method, const void* quick_code)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- method->SetEntryPointFromPortableCompiledCode(portable_code);
method->SetEntryPointFromQuickCompiledCode(quick_code);
- bool portable_enabled = method->IsPortableCompiled();
- if (have_portable_code && !portable_enabled) {
- method->SetIsPortableCompiled();
- } else if (portable_enabled) {
- method->ClearIsPortableCompiled();
- }
if (!method->IsResolutionMethod()) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
if (class_linker->IsQuickToInterpreterBridge(quick_code) ||
(class_linker->IsQuickResolutionStub(quick_code) &&
Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly() &&
!method->IsNative() && !method->IsProxyMethod())) {
- if (kIsDebugBuild) {
- if (quick_code == GetQuickToInterpreterBridge()) {
- DCHECK(portable_code == GetPortableToInterpreterBridge());
- } else if (class_linker->IsQuickResolutionStub(quick_code)) {
- DCHECK(class_linker->IsPortableResolutionStub(portable_code));
- }
- }
DCHECK(!method->IsNative()) << PrettyMethod(method);
DCHECK(!method->IsProxyMethod()) << PrettyMethod(method);
method->SetEntryPointFromInterpreter(art::artInterpreterToInterpreterBridge);
@@ -126,27 +118,21 @@
method->GetDeclaringClass()->DescriptorEquals("Ljava/lang/reflect/Proxy;")) {
return;
}
- const void* new_portable_code;
const void* new_quick_code;
bool uninstall = !entry_exit_stubs_installed_ && !interpreter_stubs_installed_;
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
bool is_class_initialized = method->GetDeclaringClass()->IsInitialized();
- bool have_portable_code = false;
if (uninstall) {
if ((forced_interpret_only_ || IsDeoptimized(method)) && !method->IsNative()) {
- new_portable_code = GetPortableToInterpreterBridge();
new_quick_code = GetQuickToInterpreterBridge();
} else if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) {
- new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
new_quick_code = class_linker->GetQuickOatCodeFor(method);
} else {
- new_portable_code = GetPortableResolutionStub();
new_quick_code = GetQuickResolutionStub();
}
} else { // !uninstall
if ((interpreter_stubs_installed_ || forced_interpret_only_ || IsDeoptimized(method)) &&
!method->IsNative()) {
- new_portable_code = GetPortableToInterpreterBridge();
new_quick_code = GetQuickToInterpreterBridge();
} else {
// Do not overwrite resolution trampoline. When the trampoline initializes the method's
@@ -154,20 +140,17 @@
// For more details, see ClassLinker::FixupStaticTrampolines.
if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) {
if (entry_exit_stubs_installed_) {
- new_portable_code = GetPortableToInterpreterBridge();
new_quick_code = GetQuickInstrumentationEntryPoint();
} else {
- new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
new_quick_code = class_linker->GetQuickOatCodeFor(method);
DCHECK(!class_linker->IsQuickToInterpreterBridge(new_quick_code));
}
} else {
- new_portable_code = GetPortableResolutionStub();
new_quick_code = GetQuickResolutionStub();
}
}
}
- UpdateEntrypoints(method, new_quick_code, new_portable_code, have_portable_code);
+ UpdateEntrypoints(method, new_quick_code);
}
// Places the instrumentation exit pc as the return PC for every quick frame. This also allows
@@ -195,7 +178,7 @@
return true; // Ignore upcalls.
}
if (GetCurrentQuickFrame() == NULL) {
- bool interpreter_frame = !m->IsPortableCompiled();
+ bool interpreter_frame = true;
InstrumentationStackFrame instrumentation_frame(GetThisObject(), m, 0, GetFrameId(),
interpreter_frame);
if (kVerboseInstrumentation) {
@@ -565,6 +548,7 @@
}
Thread* const self = Thread::Current();
Runtime* runtime = Runtime::Current();
+ Locks::mutator_lock_->AssertExclusiveHeld(self);
Locks::thread_list_lock_->AssertNotHeld(self);
if (desired_level > 0) {
if (require_interpreter) {
@@ -654,41 +638,27 @@
}
}
-void Instrumentation::UpdateMethodsCode(mirror::ArtMethod* method, const void* quick_code,
- const void* portable_code, bool have_portable_code) {
- const void* new_portable_code;
+void Instrumentation::UpdateMethodsCode(mirror::ArtMethod* method, const void* quick_code) {
+ DCHECK(method->GetDeclaringClass()->IsResolved());
const void* new_quick_code;
- bool new_have_portable_code;
if (LIKELY(!instrumentation_stubs_installed_)) {
- new_portable_code = portable_code;
new_quick_code = quick_code;
- new_have_portable_code = have_portable_code;
} else {
if ((interpreter_stubs_installed_ || IsDeoptimized(method)) && !method->IsNative()) {
- new_portable_code = GetPortableToInterpreterBridge();
new_quick_code = GetQuickToInterpreterBridge();
- new_have_portable_code = false;
} else {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
if (class_linker->IsQuickResolutionStub(quick_code) ||
class_linker->IsQuickToInterpreterBridge(quick_code)) {
- DCHECK(class_linker->IsPortableResolutionStub(portable_code) ||
- class_linker->IsPortableToInterpreterBridge(portable_code));
- new_portable_code = portable_code;
new_quick_code = quick_code;
- new_have_portable_code = have_portable_code;
} else if (entry_exit_stubs_installed_) {
new_quick_code = GetQuickInstrumentationEntryPoint();
- new_portable_code = GetPortableToInterpreterBridge();
- new_have_portable_code = false;
} else {
- new_portable_code = portable_code;
new_quick_code = quick_code;
- new_have_portable_code = have_portable_code;
}
}
}
- UpdateEntrypoints(method, new_quick_code, new_portable_code, new_have_portable_code);
+ UpdateEntrypoints(method, new_quick_code);
}
bool Instrumentation::AddDeoptimizedMethod(mirror::ArtMethod* method) {
@@ -701,12 +671,14 @@
return false;
}
// Not found. Add it.
+ static_assert(!kMovingMethods, "Not safe if methods can move");
int32_t hash_code = method->IdentityHashCode();
deoptimized_methods_.insert(std::make_pair(hash_code, GcRoot<mirror::ArtMethod>(method)));
return true;
}
bool Instrumentation::FindDeoptimizedMethod(mirror::ArtMethod* method) {
+ static_assert(!kMovingMethods, "Not safe if methods can move");
int32_t hash_code = method->IdentityHashCode();
auto range = deoptimized_methods_.equal_range(hash_code);
for (auto it = range.first; it != range.second; ++it) {
@@ -730,6 +702,7 @@
}
bool Instrumentation::RemoveDeoptimizedMethod(mirror::ArtMethod* method) {
+ static_assert(!kMovingMethods, "Not safe if methods can move");
int32_t hash_code = method->IdentityHashCode();
auto range = deoptimized_methods_.equal_range(hash_code);
for (auto it = range.first; it != range.second; ++it) {
@@ -761,8 +734,7 @@
<< " is already deoptimized";
}
if (!interpreter_stubs_installed_) {
- UpdateEntrypoints(method, GetQuickInstrumentationEntryPoint(), GetPortableToInterpreterBridge(),
- false);
+ UpdateEntrypoints(method, GetQuickInstrumentationEntryPoint());
// Install instrumentation exit stub and instrumentation frames. We may already have installed
// these previously so it will only cover the newly created frames.
@@ -793,12 +765,10 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
if (method->IsStatic() && !method->IsConstructor() &&
!method->GetDeclaringClass()->IsInitialized()) {
- UpdateEntrypoints(method, GetQuickResolutionStub(), GetPortableResolutionStub(), false);
+ UpdateEntrypoints(method, GetQuickResolutionStub());
} else {
- bool have_portable_code = false;
const void* quick_code = class_linker->GetQuickOatCodeFor(method);
- const void* portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
- UpdateEntrypoints(method, quick_code, portable_code, have_portable_code);
+ UpdateEntrypoints(method, quick_code);
}
// If there is no deoptimized method left, we can restore the stack of each thread.
@@ -1041,15 +1011,14 @@
// back to an upcall.
NthCallerVisitor visitor(self, 1, true);
visitor.WalkStack(true);
- bool deoptimize = (visitor.caller != NULL) &&
+ bool deoptimize = (visitor.caller != nullptr) &&
(interpreter_stubs_installed_ || IsDeoptimized(visitor.caller));
- if (deoptimize && kVerboseInstrumentation) {
- LOG(INFO) << "Deoptimizing into " << PrettyMethod(visitor.caller);
- }
if (deoptimize) {
if (kVerboseInstrumentation) {
- LOG(INFO) << "Deoptimizing from " << PrettyMethod(method)
- << " result is " << std::hex << return_value.GetJ();
+ LOG(INFO) << StringPrintf("Deoptimizing %s by returning from %s with result %#" PRIx64 " in ",
+ PrettyMethod(visitor.caller).c_str(),
+ PrettyMethod(method).c_str(),
+ return_value.GetJ()) << *self;
}
self->SetDeoptimizationReturnValue(return_value);
return GetTwoWordSuccessValue(*return_pc,
@@ -1095,7 +1064,7 @@
return;
}
for (auto pair : deoptimized_methods_) {
- pair.second.VisitRoot(callback, arg, 0, kRootVMInternal);
+ pair.second.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
}
}
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index effa9f7..cea0388 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -193,8 +193,7 @@
void ResetQuickAllocEntryPoints() EXCLUSIVE_LOCKS_REQUIRED(Locks::runtime_shutdown_lock_);
// Update the code of a method respecting any installed stubs.
- void UpdateMethodsCode(mirror::ArtMethod* method, const void* quick_code,
- const void* portable_code, bool have_portable_code)
+ void UpdateMethodsCode(mirror::ArtMethod* method, const void* quick_code)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Get the quick code for the given method. More efficient than asking the class linker as it
@@ -217,10 +216,6 @@
return forced_interpret_only_;
}
- bool ShouldPortableCodeDeoptimize() const {
- return instrumentation_stubs_installed_;
- }
-
bool AreExitStubsInstalled() const {
return instrumentation_stubs_installed_;
}
@@ -333,7 +328,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Call back for configure stubs.
- bool InstallStubsForClass(mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void InstallStubsForClass(mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void InstallStubsForMethod(mirror::ArtMethod* method)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 7ecb58e..19bfc4e 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -60,7 +60,7 @@
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (auto& root : new_strong_intern_roots_) {
mirror::String* old_ref = root.Read<kWithoutReadBarrier>();
- root.VisitRoot(callback, arg, 0, kRootInternedString);
+ root.VisitRoot(callback, arg, RootInfo(kRootInternedString));
mirror::String* new_ref = root.Read<kWithoutReadBarrier>();
if (new_ref != old_ref) {
// The GC moved a root in the log. Need to search the strong interns and update the
@@ -192,6 +192,7 @@
const DexFile::StringId* string_id = dex_file->FindStringId(utf8.c_str());
if (string_id != nullptr) {
uint32_t string_idx = dex_file->GetIndexForStringId(*string_id);
+ // GetResolvedString() contains a RB.
mirror::String* image_string = dex_cache->GetResolvedString(string_idx);
if (image_string != NULL) {
return image_string;
@@ -214,6 +215,13 @@
allow_new_interns_ = false;
}
+void InternTable::EnsureNewInternsDisallowed() {
+ // Lock and unlock once to ensure that no threads are still in the
+ // middle of adding new interns.
+ MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
+ CHECK(!allow_new_interns_);
+}
+
mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) {
if (s == nullptr) {
return nullptr;
@@ -329,10 +337,10 @@
void InternTable::Table::VisitRoots(RootCallback* callback, void* arg) {
for (auto& intern : pre_zygote_table_) {
- intern.VisitRoot(callback, arg, 0, kRootInternedString);
+ intern.VisitRoot(callback, arg, RootInfo(kRootInternedString));
}
for (auto& intern : post_zygote_table_) {
- intern.VisitRoot(callback, arg, 0, kRootInternedString);
+ intern.VisitRoot(callback, arg, RootInfo(kRootInternedString));
}
}
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index 371d3f7..2e31b7e 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -85,8 +85,9 @@
void DumpForSigQuit(std::ostream& os) const;
- void DisallowNewInterns() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void DisallowNewInterns() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void AllowNewInterns() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void EnsureNewInternsDisallowed() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Adds all of the resolved image strings from the image space into the intern table. The
// advantage of doing this is preventing expensive DexFile::FindStringId calls.
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index b04a18b..9d988e9 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -493,7 +493,23 @@
while (shadow_frame != NULL) {
self->SetTopOfShadowStack(shadow_frame);
const DexFile::CodeItem* code_item = shadow_frame->GetMethod()->GetCodeItem();
- value = Execute(self, code_item, *shadow_frame, value);
+ const uint32_t dex_pc = shadow_frame->GetDexPC();
+ uint32_t new_dex_pc;
+ if (UNLIKELY(self->IsExceptionPending())) {
+ const instrumentation::Instrumentation* const instrumentation =
+ Runtime::Current()->GetInstrumentation();
+ uint32_t found_dex_pc = FindNextInstructionFollowingException(self, *shadow_frame, dex_pc,
+ instrumentation);
+ new_dex_pc = found_dex_pc; // the dex pc of a matching catch handler
+ // or DexFile::kDexNoIndex if there is none.
+ } else {
+ const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]);
+ new_dex_pc = dex_pc + instr->SizeInCodeUnits(); // the dex pc of the next instruction.
+ }
+ if (new_dex_pc != DexFile::kDexNoIndex) {
+ shadow_frame->SetDexPC(new_dex_pc);
+ value = Execute(self, code_item, *shadow_frame, value);
+ }
ShadowFrame* old_frame = shadow_frame;
shadow_frame = shadow_frame->GetLink();
delete old_frame;
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 3c7db85..a29558e 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -145,6 +145,18 @@
case Primitive::kPrimInt:
shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetField32(field_offset)));
break;
+ case Primitive::kPrimBoolean:
+ shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetFieldBoolean(field_offset)));
+ break;
+ case Primitive::kPrimByte:
+ shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetFieldByte(field_offset)));
+ break;
+ case Primitive::kPrimChar:
+ shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetFieldChar(field_offset)));
+ break;
+ case Primitive::kPrimShort:
+ shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetFieldShort(field_offset)));
+ break;
case Primitive::kPrimLong:
shadow_frame.SetVRegLong(vregA, static_cast<int64_t>(obj->GetField64(field_offset)));
break;
@@ -163,9 +175,13 @@
template bool DoIGetQuick<_field_type>(ShadowFrame& shadow_frame, const Instruction* inst, \
uint16_t inst_data)
-EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimInt); // iget-quick.
-EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimLong); // iget-wide-quick.
-EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimInt); // iget-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimBoolean); // iget-boolean-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimByte); // iget-byte-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimChar); // iget-char-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimShort); // iget-short-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimLong); // iget-wide-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-quick.
#undef EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL
template<Primitive::Type field_type>
@@ -530,11 +546,13 @@
void AbortTransaction(Thread* self, const char* fmt, ...) {
CHECK(Runtime::Current()->IsActiveTransaction());
- // Throw an exception so we can abort the transaction and undo every change.
+ // Constructs abort message.
va_list args;
va_start(args, fmt);
- self->ThrowNewExceptionV(self->GetCurrentLocationForThrow(), "Ljava/lang/InternalError;", fmt,
- args);
+ std::string abort_msg;
+ StringAppendV(&abort_msg, fmt, args);
+ // Throws an exception so we can abort the transaction and rollback every change.
+ Runtime::Current()->AbortTransactionAndThrowInternalError(self, abort_msg);
va_end(args);
}
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index c332a7b..e4b3247 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -148,7 +148,10 @@
const void* const* currentHandlersTable;
bool notified_method_entry_event = false;
UPDATE_HANDLER_TABLE();
- if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing..
+ if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing.
+ if (kIsDebugBuild) {
+ self->AssertNoPendingException();
+ }
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
@@ -236,6 +239,7 @@
HANDLE_INSTRUCTION_START(MOVE_EXCEPTION) {
Throwable* exception = self->GetException(nullptr);
+ DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction";
shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception);
self->ClearException();
ADVANCE(1);
@@ -543,7 +547,7 @@
HANDLE_INSTRUCTION_START(NEW_ARRAY) {
int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data));
Object* obj = AllocArrayFromCode<do_access_check, true>(
- inst->VRegC_22c(), shadow_frame.GetMethod(), length, self,
+ inst->VRegC_22c(), length, shadow_frame.GetMethod(), self,
Runtime::Current()->GetHeap()->GetCurrentAllocator());
if (UNLIKELY(obj == NULL)) {
HANDLE_PENDING_EXCEPTION();
@@ -1249,6 +1253,30 @@
}
HANDLE_INSTRUCTION_END();
+ HANDLE_INSTRUCTION_START(IGET_BOOLEAN_QUICK) {
+ bool success = DoIGetQuick<Primitive::kPrimBoolean>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_BYTE_QUICK) {
+ bool success = DoIGetQuick<Primitive::kPrimByte>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_CHAR_QUICK) {
+ bool success = DoIGetQuick<Primitive::kPrimChar>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_SHORT_QUICK) {
+ bool success = DoIGetQuick<Primitive::kPrimShort>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
HANDLE_INSTRUCTION_START(IGET_WIDE_QUICK) {
bool success = DoIGetQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
@@ -2310,22 +2338,6 @@
UnexpectedOpcode(inst, shadow_frame);
HANDLE_INSTRUCTION_END();
- HANDLE_INSTRUCTION_START(UNUSED_EF)
- UnexpectedOpcode(inst, shadow_frame);
- HANDLE_INSTRUCTION_END();
-
- HANDLE_INSTRUCTION_START(UNUSED_F0)
- UnexpectedOpcode(inst, shadow_frame);
- HANDLE_INSTRUCTION_END();
-
- HANDLE_INSTRUCTION_START(UNUSED_F1)
- UnexpectedOpcode(inst, shadow_frame);
- HANDLE_INSTRUCTION_END();
-
- HANDLE_INSTRUCTION_START(UNUSED_F2)
- UnexpectedOpcode(inst, shadow_frame);
- HANDLE_INSTRUCTION_END();
-
HANDLE_INSTRUCTION_START(UNUSED_F3)
UnexpectedOpcode(inst, shadow_frame);
HANDLE_INSTRUCTION_END();
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index f9bbfa1..2f85587 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -69,7 +69,10 @@
uint32_t dex_pc = shadow_frame.GetDexPC();
bool notified_method_entry_event = false;
const instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation();
- if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing..
+ if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing.
+ if (kIsDebugBuild) {
+ self->AssertNoPendingException();
+ }
if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
shadow_frame.GetMethod(), 0);
@@ -161,6 +164,7 @@
case Instruction::MOVE_EXCEPTION: {
PREAMBLE();
Throwable* exception = self->GetException(nullptr);
+ DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction";
shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception);
self->ClearException();
inst = inst->Next_1xx();
@@ -455,7 +459,7 @@
PREAMBLE();
int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data));
Object* obj = AllocArrayFromCode<do_access_check, true>(
- inst->VRegC_22c(), shadow_frame.GetMethod(), length, self,
+ inst->VRegC_22c(), length, shadow_frame.GetMethod(), self,
Runtime::Current()->GetHeap()->GetCurrentAllocator());
if (UNLIKELY(obj == NULL)) {
HANDLE_PENDING_EXCEPTION();
@@ -1128,6 +1132,30 @@
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
+ case Instruction::IGET_BOOLEAN_QUICK: {
+ PREAMBLE();
+ bool success = DoIGetQuick<Primitive::kPrimBoolean>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_BYTE_QUICK: {
+ PREAMBLE();
+ bool success = DoIGetQuick<Primitive::kPrimByte>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_CHAR_QUICK: {
+ PREAMBLE();
+ bool success = DoIGetQuick<Primitive::kPrimChar>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_SHORT_QUICK: {
+ PREAMBLE();
+ bool success = DoIGetQuick<Primitive::kPrimShort>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
case Instruction::SGET_BOOLEAN: {
PREAMBLE();
bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
@@ -2137,7 +2165,7 @@
inst = inst->Next_2xx();
break;
case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
- case Instruction::UNUSED_EF ... Instruction::UNUSED_FF:
+ case Instruction::UNUSED_F3 ... Instruction::UNUSED_FF:
case Instruction::UNUSED_79:
case Instruction::UNUSED_7A:
UnexpectedOpcode(inst, shadow_frame);
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index 5d04fac..ea7c192 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -22,6 +22,7 @@
#include "base/mutex.h"
#include "base/stl_util.h"
#include "check_jni.h"
+#include "dex_file-inl.h"
#include "fault_handler.h"
#include "indirect_reference_table-inl.h"
#include "mirror/art_method.h"
@@ -31,6 +32,7 @@
#include "java_vm_ext.h"
#include "parsed_options.h"
#include "runtime-inl.h"
+#include "runtime_options.h"
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change.h"
#include "thread-inl.h"
@@ -247,7 +249,7 @@
}
private:
- AllocationTrackingSafeMap<std::string, SharedLibrary*, kAllocatorTagJNILibrarires> libraries_;
+ AllocationTrackingSafeMap<std::string, SharedLibrary*, kAllocatorTagJNILibraries> libraries_;
};
@@ -356,14 +358,15 @@
JII::AttachCurrentThreadAsDaemon
};
-JavaVMExt::JavaVMExt(Runtime* runtime, ParsedOptions* options)
+JavaVMExt::JavaVMExt(Runtime* runtime, const RuntimeArgumentMap& runtime_options)
: runtime_(runtime),
check_jni_abort_hook_(nullptr),
check_jni_abort_hook_data_(nullptr),
check_jni_(false), // Initialized properly in the constructor body below.
- force_copy_(options->force_copy_),
- tracing_enabled_(!options->jni_trace_.empty() || VLOG_IS_ON(third_party_jni)),
- trace_(options->jni_trace_),
+ force_copy_(runtime_options.Exists(RuntimeArgumentMap::JniOptsForceCopy)),
+ tracing_enabled_(runtime_options.Exists(RuntimeArgumentMap::JniTrace)
+ || VLOG_IS_ON(third_party_jni)),
+ trace_(runtime_options.GetOrDefault(RuntimeArgumentMap::JniTrace)),
globals_lock_("JNI global reference table lock"),
globals_(gGlobalsInitial, gGlobalsMax, kGlobal),
libraries_(new Libraries),
@@ -373,9 +376,7 @@
allow_new_weak_globals_(true),
weak_globals_add_condition_("weak globals add condition", weak_globals_lock_) {
functions = unchecked_functions_;
- if (options->check_jni_) {
- SetCheckJniEnabled(true);
- }
+ SetCheckJniEnabled(runtime_options.Exists(RuntimeArgumentMap::CheckJni));
}
JavaVMExt::~JavaVMExt() {
@@ -549,6 +550,13 @@
weak_globals_add_condition_.Broadcast(self);
}
+void JavaVMExt::EnsureNewWeakGlobalsDisallowed() {
+ // Lock and unlock once to ensure that no threads are still in the
+ // middle of adding new weak globals.
+ MutexLock mu(Thread::Current(), weak_globals_lock_);
+ CHECK(!allow_new_weak_globals_);
+}
+
mirror::Object* JavaVMExt::DecodeGlobal(Thread* self, IndirectRef ref) {
return globals_.SynchronizedGet(self, &globals_lock_, ref);
}
@@ -763,10 +771,8 @@
void JavaVMExt::VisitRoots(RootCallback* callback, void* arg) {
Thread* self = Thread::Current();
- {
- ReaderMutexLock mu(self, globals_lock_);
- globals_.VisitRoots(callback, arg, 0, kRootJNIGlobal);
- }
+ ReaderMutexLock mu(self, globals_lock_);
+ globals_.VisitRoots(callback, arg, RootInfo(kRootJNIGlobal));
// The weak_globals table is visited by the GC itself (because it mutates the table).
}
diff --git a/runtime/java_vm_ext.h b/runtime/java_vm_ext.h
index 749b9fb..037fbe5 100644
--- a/runtime/java_vm_ext.h
+++ b/runtime/java_vm_ext.h
@@ -34,10 +34,11 @@
class Libraries;
class ParsedOptions;
class Runtime;
+struct RuntimeArgumentMap;
class JavaVMExt : public JavaVM {
public:
- JavaVMExt(Runtime* runtime, ParsedOptions* options);
+ JavaVMExt(Runtime* runtime, const RuntimeArgumentMap& runtime_options);
~JavaVMExt();
bool ForceCopy() const {
@@ -104,9 +105,9 @@
void VisitRoots(RootCallback* callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void DisallowNewWeakGlobals() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+ void DisallowNewWeakGlobals() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void AllowNewWeakGlobals() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void EnsureNewWeakGlobalsDisallowed() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
jobject AddGlobalRef(Thread* self, mirror::Object* obj)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/java_vm_ext_test.cc b/runtime/java_vm_ext_test.cc
index 60c6a5c..2cbfa81 100644
--- a/runtime/java_vm_ext_test.cc
+++ b/runtime/java_vm_ext_test.cc
@@ -69,7 +69,12 @@
} else {
ok = vms_buf[0]->AttachCurrentThreadAsDaemon(&env, nullptr);
}
- EXPECT_EQ(gSmallStack ? JNI_ERR : JNI_OK, ok);
+ // TODO: Find a way to test with exact SMALL_STACK value, for which we would bail. The pthreads
+ // spec says that the stack size argument is a lower bound, and bionic currently gives us
+ // a chunk more on arm64.
+ if (!gSmallStack) {
+ EXPECT_EQ(JNI_OK, ok);
+ }
if (ok == JNI_OK) {
ok = vms_buf[0]->DetachCurrentThread();
EXPECT_EQ(JNI_OK, ok);
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
index 0c9451c..9f37998 100644
--- a/runtime/jdwp/jdwp.h
+++ b/runtime/jdwp/jdwp.h
@@ -27,6 +27,7 @@
#include <stddef.h>
#include <stdint.h>
#include <string.h>
+#include <vector>
struct iovec;
@@ -100,13 +101,15 @@
std::ostream& operator<<(std::ostream& os, const JdwpTransportType& rhs);
struct JdwpOptions {
- JdwpTransportType transport;
- bool server;
- bool suspend;
- std::string host;
- uint16_t port;
+ JdwpTransportType transport = kJdwpTransportUnknown;
+ bool server = false;
+ bool suspend = false;
+ std::string host = "";
+ uint16_t port = static_cast<uint16_t>(-1);
};
+bool operator==(const JdwpOptions& lhs, const JdwpOptions& rhs);
+
struct JdwpEvent;
class JdwpNetStateBase;
struct ModBasket;
@@ -188,7 +191,7 @@
* The VM has finished initializing. Only called when the debugger is
* connected at the time initialization completes.
*/
- bool PostVMStart() LOCKS_EXCLUDED(event_list_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void PostVMStart() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
* A location of interest has been reached. This is used for breakpoints,
@@ -202,7 +205,7 @@
*
* "returnValue" is non-null for MethodExit events only.
*/
- bool PostLocationEvent(const EventLocation* pLoc, mirror::Object* thisPtr, int eventFlags,
+ void PostLocationEvent(const EventLocation* pLoc, mirror::Object* thisPtr, int eventFlags,
const JValue* returnValue)
LOCKS_EXCLUDED(event_list_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -214,7 +217,7 @@
* "fieldValue" is non-null for field modification events only.
* "is_modification" is true for field modification, false for field access.
*/
- bool PostFieldEvent(const EventLocation* pLoc, mirror::ArtField* field, mirror::Object* thisPtr,
+ void PostFieldEvent(const EventLocation* pLoc, mirror::ArtField* field, mirror::Object* thisPtr,
const JValue* fieldValue, bool is_modification)
LOCKS_EXCLUDED(event_list_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -224,7 +227,7 @@
*
* Pass in a zeroed-out "*pCatchLoc" if the exception wasn't caught.
*/
- bool PostException(const EventLocation* pThrowLoc, mirror::Throwable* exception_object,
+ void PostException(const EventLocation* pThrowLoc, mirror::Throwable* exception_object,
const EventLocation* pCatchLoc, mirror::Object* thisPtr)
LOCKS_EXCLUDED(event_list_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -232,14 +235,14 @@
/*
* A thread has started or stopped.
*/
- bool PostThreadChange(Thread* thread, bool start)
+ void PostThreadChange(Thread* thread, bool start)
LOCKS_EXCLUDED(event_list_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
* Class has been prepared.
*/
- bool PostClassPrepare(mirror::Class* klass)
+ void PostClassPrepare(mirror::Class* klass)
LOCKS_EXCLUDED(event_list_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -251,6 +254,9 @@
// Called if/when we realize we're talking to DDMS.
void NotifyDdmsActive() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void SetupChunkHeader(uint32_t type, size_t data_len, size_t header_size, uint8_t* out_header);
+
/*
* Send up a chunk of DDM data.
*/
@@ -307,15 +313,16 @@
void SendRequestAndPossiblySuspend(ExpandBuf* pReq, JdwpSuspendPolicy suspend_policy,
ObjectId threadId)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void CleanupMatchList(JdwpEvent** match_list,
- size_t match_count)
+ void CleanupMatchList(const std::vector<JdwpEvent*>& match_list)
EXCLUSIVE_LOCKS_REQUIRED(event_list_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void EventFinish(ExpandBuf* pReq);
- void FindMatchingEvents(JdwpEventKind eventKind,
- const ModBasket& basket,
- JdwpEvent** match_list,
- size_t* pMatchCount)
+ bool FindMatchingEvents(JdwpEventKind eventKind, const ModBasket& basket,
+ std::vector<JdwpEvent*>* match_list)
+ LOCKS_EXCLUDED(event_list_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void FindMatchingEventsLocked(JdwpEventKind eventKind, const ModBasket& basket,
+ std::vector<JdwpEvent*>* match_list)
EXCLUSIVE_LOCKS_REQUIRED(event_list_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void UnregisterEvent(JdwpEvent* pEvent)
diff --git a/runtime/jdwp/jdwp_adb.cc b/runtime/jdwp/jdwp_adb.cc
index df7d068..adc2912 100644
--- a/runtime/jdwp/jdwp_adb.cc
+++ b/runtime/jdwp/jdwp_adb.cc
@@ -123,7 +123,7 @@
bool InitAdbTransport(JdwpState* state, const JdwpOptions*) {
VLOG(jdwp) << "ADB transport startup";
state->netState = new JdwpAdbState(state);
- return (state->netState != NULL);
+ return (state->netState != nullptr);
}
/*
@@ -145,7 +145,7 @@
iov.iov_len = 1;
msghdr msg;
- msg.msg_name = NULL;
+ msg.msg_name = nullptr;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
@@ -352,7 +352,7 @@
* re-issue the select. We're currently using #2, as it's more
* reliable than #1 and generally better than #3. Wastes two fds.
*/
- selCount = select(maxfd+1, &readfds, NULL, NULL, NULL);
+ selCount = select(maxfd + 1, &readfds, nullptr, nullptr, nullptr);
if (selCount < 0) {
if (errno == EINTR) {
continue;
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index 1e0a2d2..a8eaa26 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -172,9 +172,9 @@
* not be added to the list, and an appropriate error will be returned.
*/
JdwpError JdwpState::RegisterEvent(JdwpEvent* pEvent) {
- CHECK(pEvent != NULL);
- CHECK(pEvent->prev == NULL);
- CHECK(pEvent->next == NULL);
+ CHECK(pEvent != nullptr);
+ CHECK(pEvent->prev == nullptr);
+ CHECK(pEvent->next == nullptr);
{
/*
@@ -223,7 +223,7 @@
* Add to list.
*/
MutexLock mu(Thread::Current(), event_list_lock_);
- if (event_list_ != NULL) {
+ if (event_list_ != nullptr) {
pEvent->next = event_list_;
event_list_->prev = pEvent;
}
@@ -245,7 +245,7 @@
* Grab the eventLock before calling here.
*/
void JdwpState::UnregisterEvent(JdwpEvent* pEvent) {
- if (pEvent->prev == NULL) {
+ if (pEvent->prev == nullptr) {
/* head of the list */
CHECK(event_list_ == pEvent);
@@ -254,11 +254,11 @@
pEvent->prev->next = pEvent->next;
}
- if (pEvent->next != NULL) {
+ if (pEvent->next != nullptr) {
pEvent->next->prev = pEvent->prev;
- pEvent->next = NULL;
+ pEvent->next = nullptr;
}
- pEvent->prev = NULL;
+ pEvent->prev = nullptr;
{
/*
@@ -303,7 +303,7 @@
}
--event_list_size_;
- CHECK(event_list_size_ != 0 || event_list_ == NULL);
+ CHECK(event_list_size_ != 0 || event_list_ == nullptr);
}
/*
@@ -343,7 +343,7 @@
MutexLock mu(Thread::Current(), event_list_lock_);
JdwpEvent* pEvent = event_list_;
- while (pEvent != NULL) {
+ while (pEvent != nullptr) {
JdwpEvent* pNextEvent = pEvent->next;
UnregisterEvent(pEvent);
@@ -351,7 +351,7 @@
pEvent = pNextEvent;
}
- event_list_ = NULL;
+ event_list_ = nullptr;
}
/*
@@ -372,13 +372,13 @@
* Do not call this until the event has been removed from the list.
*/
void EventFree(JdwpEvent* pEvent) {
- if (pEvent == NULL) {
+ if (pEvent == nullptr) {
return;
}
/* make sure it was removed from the list */
- CHECK(pEvent->prev == NULL);
- CHECK(pEvent->next == NULL);
+ CHECK(pEvent->prev == nullptr);
+ CHECK(pEvent->next == nullptr);
/* want to check state->event_list_ != pEvent */
/*
@@ -387,11 +387,11 @@
for (int i = 0; i < pEvent->modCount; i++) {
if (pEvent->mods[i].modKind == MK_CLASS_MATCH) {
free(pEvent->mods[i].classMatch.classPattern);
- pEvent->mods[i].classMatch.classPattern = NULL;
+ pEvent->mods[i].classMatch.classPattern = nullptr;
}
if (pEvent->mods[i].modKind == MK_CLASS_EXCLUDE) {
free(pEvent->mods[i].classExclude.classPattern);
- pEvent->mods[i].classExclude.classPattern = NULL;
+ pEvent->mods[i].classExclude.classPattern = nullptr;
}
}
@@ -399,26 +399,12 @@
}
/*
- * Allocate storage for matching events. To keep things simple we
- * use an array with enough storage for the entire list.
- *
- * The state->eventLock should be held before calling.
- */
-static JdwpEvent** AllocMatchList(size_t event_count) {
- return new JdwpEvent*[event_count];
-}
-
-/*
* Run through the list and remove any entries with an expired "count" mod
- * from the event list, then free the match list.
+ * from the event list.
*/
-void JdwpState::CleanupMatchList(JdwpEvent** match_list, size_t match_count) {
- JdwpEvent** ppEvent = match_list;
-
- while (match_count--) {
- JdwpEvent* pEvent = *ppEvent;
-
- for (int i = 0; i < pEvent->modCount; i++) {
+void JdwpState::CleanupMatchList(const std::vector<JdwpEvent*>& match_list) {
+ for (JdwpEvent* pEvent : match_list) {
+ for (int i = 0; i < pEvent->modCount; ++i) {
if (pEvent->mods[i].modKind == MK_COUNT && pEvent->mods[i].count.count == 0) {
VLOG(jdwp) << StringPrintf("##### Removing expired event (requestId=%#" PRIx32 ")",
pEvent->requestId);
@@ -427,11 +413,7 @@
break;
}
}
-
- ppEvent++;
}
-
- delete[] match_list;
}
/*
@@ -536,40 +518,55 @@
}
/*
- * Find all events of type "eventKind" with mods that match up with the
- * rest of the arguments.
+ * Find all events of type "event_kind" with mods that match up with the
+ * rest of the arguments while holding the event list lock. This method
+ * is used by FindMatchingEvents below.
*
- * Found events are appended to "match_list", and "*pMatchCount" is advanced,
- * so this may be called multiple times for grouped events.
+ * Found events are appended to "match_list" so this may be called multiple times for grouped
+ * events.
*
* DO NOT call this multiple times for the same eventKind, as Count mods are
* decremented during the scan.
*/
-void JdwpState::FindMatchingEvents(JdwpEventKind eventKind, const ModBasket& basket,
- JdwpEvent** match_list, size_t* pMatchCount) {
- /* start after the existing entries */
- match_list += *pMatchCount;
-
+void JdwpState::FindMatchingEventsLocked(JdwpEventKind event_kind, const ModBasket& basket,
+ std::vector<JdwpEvent*>* match_list) {
for (JdwpEvent* pEvent = event_list_; pEvent != nullptr; pEvent = pEvent->next) {
- if (pEvent->eventKind == eventKind && ModsMatch(pEvent, basket)) {
- *match_list++ = pEvent;
- (*pMatchCount)++;
+ if (pEvent->eventKind == event_kind && ModsMatch(pEvent, basket)) {
+ match_list->push_back(pEvent);
}
}
}
/*
+ * Find all events of type "event_kind" with mods that match up with the
+ * rest of the arguments and return true if at least one event matches,
+ * false otherwise.
+ *
+ * Found events are appended to "match_list" so this may be called multiple
+ * times for grouped events.
+ *
+ * DO NOT call this multiple times for the same eventKind, as Count mods are
+ * decremented during the scan.
+ */
+bool JdwpState::FindMatchingEvents(JdwpEventKind event_kind, const ModBasket& basket,
+ std::vector<JdwpEvent*>* match_list) {
+ MutexLock mu(Thread::Current(), event_list_lock_);
+ match_list->reserve(event_list_size_);
+ FindMatchingEventsLocked(event_kind, basket, match_list);
+ return !match_list->empty();
+}
+
+/*
* Scan through the list of matches and determine the most severe
* suspension policy.
*/
-static JdwpSuspendPolicy scanSuspendPolicy(JdwpEvent** match_list, int match_count) {
+static JdwpSuspendPolicy ScanSuspendPolicy(const std::vector<JdwpEvent*>& match_list) {
JdwpSuspendPolicy policy = SP_NONE;
- while (match_count--) {
- if ((*match_list)->suspend_policy > policy) {
- policy = (*match_list)->suspend_policy;
+ for (JdwpEvent* pEvent : match_list) {
+ if (pEvent->suspend_policy > policy) {
+ policy = pEvent->suspend_policy;
}
- match_list++;
}
return policy;
@@ -626,19 +623,18 @@
void JdwpState::SendRequestAndPossiblySuspend(ExpandBuf* pReq, JdwpSuspendPolicy suspend_policy,
ObjectId threadId) {
- Thread* self = Thread::Current();
+ Thread* const self = Thread::Current();
self->AssertThreadSuspensionIsAllowable();
+ CHECK(pReq != nullptr);
/* send request and possibly suspend ourselves */
- if (pReq != NULL) {
- JDWP::ObjectId thread_self_id = Dbg::GetThreadSelfId();
- self->TransitionFromRunnableToSuspended(kWaitingForDebuggerSend);
- if (suspend_policy != SP_NONE) {
- SetWaitForEventThread(threadId);
- }
- EventFinish(pReq);
- SuspendByPolicy(suspend_policy, thread_self_id);
- self->TransitionFromSuspendedToRunnable();
+ JDWP::ObjectId thread_self_id = Dbg::GetThreadSelfId();
+ self->TransitionFromRunnableToSuspended(kWaitingForDebuggerSend);
+ if (suspend_policy != SP_NONE) {
+ SetWaitForEventThread(threadId);
}
+ EventFinish(pReq);
+ SuspendByPolicy(suspend_policy, thread_self_id);
+ self->TransitionFromSuspendedToRunnable();
}
/*
@@ -729,10 +725,10 @@
uint8_t* buf = expandBufGetBuffer(pReq);
Set4BE(buf, expandBufGetLength(pReq));
- Set4BE(buf+4, NextRequestSerial());
- Set1(buf+8, 0); /* flags */
- Set1(buf+9, kJdwpEventCommandSet);
- Set1(buf+10, kJdwpCompositeCommand);
+ Set4BE(buf + 4, NextRequestSerial());
+ Set1(buf + 8, 0); /* flags */
+ Set1(buf + 9, kJdwpEventCommandSet);
+ Set1(buf + 10, kJdwpCompositeCommand);
// Prevents from interleaving commands and events. Otherwise we could end up in sending an event
// before sending the reply of the command being processed and would lead to bad synchronization
@@ -753,43 +749,30 @@
* any application code has been executed". The thread ID in the message
* must be for the main thread.
*/
-bool JdwpState::PostVMStart() {
- JdwpSuspendPolicy suspend_policy;
+void JdwpState::PostVMStart() {
+ JdwpSuspendPolicy suspend_policy = (options_->suspend) ? SP_ALL : SP_NONE;
ObjectId threadId = Dbg::GetThreadSelfId();
- if (options_->suspend) {
- suspend_policy = SP_ALL;
- } else {
- suspend_policy = SP_NONE;
- }
+ VLOG(jdwp) << "EVENT: " << EK_VM_START;
+ VLOG(jdwp) << " suspend_policy=" << suspend_policy;
ExpandBuf* pReq = eventPrep();
- {
- MutexLock mu(Thread::Current(), event_list_lock_); // probably don't need this here
-
- VLOG(jdwp) << "EVENT: " << EK_VM_START;
- VLOG(jdwp) << " suspend_policy=" << suspend_policy;
-
- expandBufAdd1(pReq, suspend_policy);
- expandBufAdd4BE(pReq, 1);
-
- expandBufAdd1(pReq, EK_VM_START);
- expandBufAdd4BE(pReq, 0); /* requestId */
- expandBufAdd8BE(pReq, threadId);
- }
+ expandBufAdd1(pReq, suspend_policy);
+ expandBufAdd4BE(pReq, 1);
+ expandBufAdd1(pReq, EK_VM_START);
+ expandBufAdd4BE(pReq, 0); /* requestId */
+ expandBufAddObjectId(pReq, threadId);
Dbg::ManageDeoptimization();
/* send request and possibly suspend ourselves */
SendRequestAndPossiblySuspend(pReq, suspend_policy, threadId);
-
- return true;
}
-static void LogMatchingEventsAndThread(JdwpEvent** match_list, size_t match_count,
+static void LogMatchingEventsAndThread(const std::vector<JdwpEvent*> match_list,
ObjectId thread_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- for (size_t i = 0; i < match_count; ++i) {
+ for (size_t i = 0, e = match_list.size(); i < e; ++i) {
JdwpEvent* pEvent = match_list[i];
VLOG(jdwp) << "EVENT #" << i << ": " << pEvent->eventKind
<< StringPrintf(" (requestId=%#" PRIx32 ")", pEvent->requestId);
@@ -831,7 +814,7 @@
* - Single-step to a line with a breakpoint. Should get a single
* event message with both events in it.
*/
-bool JdwpState::PostLocationEvent(const EventLocation* pLoc, mirror::Object* thisPtr,
+void JdwpState::PostLocationEvent(const EventLocation* pLoc, mirror::Object* thisPtr,
int eventFlags, const JValue* returnValue) {
DCHECK(pLoc != nullptr);
DCHECK(pLoc->method != nullptr);
@@ -852,7 +835,7 @@
*/
if (basket.thread == GetDebugThread()) {
VLOG(jdwp) << "Ignoring location event in JDWP thread";
- return false;
+ return;
}
/*
@@ -866,73 +849,69 @@
*/
if (InvokeInProgress()) {
VLOG(jdwp) << "Not checking breakpoints during invoke (" << basket.className << ")";
- return false;
+ return;
}
- size_t match_count = 0;
- ExpandBuf* pReq = NULL;
- JdwpSuspendPolicy suspend_policy = SP_NONE;
- JdwpEvent** match_list = nullptr;
- ObjectId thread_id = 0;
+ std::vector<JdwpEvent*> match_list;
{
- {
- MutexLock mu(Thread::Current(), event_list_lock_);
- match_list = AllocMatchList(event_list_size_);
- if ((eventFlags & Dbg::kBreakpoint) != 0) {
- FindMatchingEvents(EK_BREAKPOINT, basket, match_list, &match_count);
- }
- if ((eventFlags & Dbg::kSingleStep) != 0) {
- FindMatchingEvents(EK_SINGLE_STEP, basket, match_list, &match_count);
- }
- if ((eventFlags & Dbg::kMethodEntry) != 0) {
- FindMatchingEvents(EK_METHOD_ENTRY, basket, match_list, &match_count);
- }
- if ((eventFlags & Dbg::kMethodExit) != 0) {
- FindMatchingEvents(EK_METHOD_EXIT, basket, match_list, &match_count);
- FindMatchingEvents(EK_METHOD_EXIT_WITH_RETURN_VALUE, basket, match_list, &match_count);
- }
+ // We use the locked version because we have multiple possible match events.
+ MutexLock mu(Thread::Current(), event_list_lock_);
+ match_list.reserve(event_list_size_);
+ if ((eventFlags & Dbg::kBreakpoint) != 0) {
+ FindMatchingEventsLocked(EK_BREAKPOINT, basket, &match_list);
}
- if (match_count != 0) {
- suspend_policy = scanSuspendPolicy(match_list, match_count);
-
- thread_id = Dbg::GetThreadId(basket.thread);
- JDWP::JdwpLocation jdwp_location;
- SetJdwpLocationFromEventLocation(pLoc, &jdwp_location);
-
- if (VLOG_IS_ON(jdwp)) {
- LogMatchingEventsAndThread(match_list, match_count, thread_id);
- VLOG(jdwp) << " location=" << jdwp_location;
- VLOG(jdwp) << " suspend_policy=" << suspend_policy;
- }
-
- pReq = eventPrep();
- expandBufAdd1(pReq, suspend_policy);
- expandBufAdd4BE(pReq, match_count);
-
- for (size_t i = 0; i < match_count; i++) {
- expandBufAdd1(pReq, match_list[i]->eventKind);
- expandBufAdd4BE(pReq, match_list[i]->requestId);
- expandBufAdd8BE(pReq, thread_id);
- expandBufAddLocation(pReq, jdwp_location);
- if (match_list[i]->eventKind == EK_METHOD_EXIT_WITH_RETURN_VALUE) {
- Dbg::OutputMethodReturnValue(jdwp_location.method_id, returnValue, pReq);
- }
- }
+ if ((eventFlags & Dbg::kSingleStep) != 0) {
+ FindMatchingEventsLocked(EK_SINGLE_STEP, basket, &match_list);
}
-
- {
- MutexLock mu(Thread::Current(), event_list_lock_);
- CleanupMatchList(match_list, match_count);
+ if ((eventFlags & Dbg::kMethodEntry) != 0) {
+ FindMatchingEventsLocked(EK_METHOD_ENTRY, basket, &match_list);
}
+ if ((eventFlags & Dbg::kMethodExit) != 0) {
+ FindMatchingEventsLocked(EK_METHOD_EXIT, basket, &match_list);
+ FindMatchingEventsLocked(EK_METHOD_EXIT_WITH_RETURN_VALUE, basket, &match_list);
+ }
+ }
+ if (match_list.empty()) {
+ // No matching event.
+ return;
+ }
+ JdwpSuspendPolicy suspend_policy = ScanSuspendPolicy(match_list);
+
+ ObjectId thread_id = Dbg::GetThreadId(basket.thread);
+ JDWP::JdwpLocation jdwp_location;
+ SetJdwpLocationFromEventLocation(pLoc, &jdwp_location);
+
+ if (VLOG_IS_ON(jdwp)) {
+ LogMatchingEventsAndThread(match_list, thread_id);
+ VLOG(jdwp) << " location=" << jdwp_location;
+ VLOG(jdwp) << " suspend_policy=" << suspend_policy;
+ }
+
+ ExpandBuf* pReq = eventPrep();
+ expandBufAdd1(pReq, suspend_policy);
+ expandBufAdd4BE(pReq, match_list.size());
+
+ for (const JdwpEvent* pEvent : match_list) {
+ expandBufAdd1(pReq, pEvent->eventKind);
+ expandBufAdd4BE(pReq, pEvent->requestId);
+ expandBufAddObjectId(pReq, thread_id);
+ expandBufAddLocation(pReq, jdwp_location);
+ if (pEvent->eventKind == EK_METHOD_EXIT_WITH_RETURN_VALUE) {
+ Dbg::OutputMethodReturnValue(jdwp_location.method_id, returnValue, pReq);
+ }
+ }
+
+ {
+ MutexLock mu(Thread::Current(), event_list_lock_);
+ CleanupMatchList(match_list);
}
Dbg::ManageDeoptimization();
SendRequestAndPossiblySuspend(pReq, suspend_policy, thread_id);
- return match_count != 0;
}
-bool JdwpState::PostFieldEvent(const EventLocation* pLoc, mirror::ArtField* field,
+void JdwpState::PostFieldEvent(const EventLocation* pLoc, mirror::ArtField* field,
mirror::Object* this_object, const JValue* fieldValue,
bool is_modification) {
DCHECK(pLoc != nullptr);
@@ -950,86 +929,73 @@
if (InvokeInProgress()) {
VLOG(jdwp) << "Not posting field event during invoke";
- return false;
+ return;
}
- size_t match_count = 0;
- ExpandBuf* pReq = NULL;
- JdwpSuspendPolicy suspend_policy = SP_NONE;
- JdwpEvent** match_list = nullptr;
- ObjectId thread_id = 0;
+ std::vector<JdwpEvent*> match_list;
+ const JdwpEventKind match_kind = (is_modification) ? EK_FIELD_MODIFICATION : EK_FIELD_ACCESS;
+ if (!FindMatchingEvents(match_kind, basket, &match_list)) {
+ // No matching event.
+ return;
+ }
+
+ JdwpSuspendPolicy suspend_policy = ScanSuspendPolicy(match_list);
+ ObjectId thread_id = Dbg::GetThreadId(basket.thread);
+ ObjectRegistry* registry = Dbg::GetObjectRegistry();
+ ObjectId instance_id = registry->Add(basket.thisPtr);
+ RefTypeId field_type_id = registry->AddRefType(field->GetDeclaringClass());
+ FieldId field_id = Dbg::ToFieldId(field);
+ JDWP::JdwpLocation jdwp_location;
+ SetJdwpLocationFromEventLocation(pLoc, &jdwp_location);
+
+ if (VLOG_IS_ON(jdwp)) {
+ LogMatchingEventsAndThread(match_list, thread_id);
+ VLOG(jdwp) << " location=" << jdwp_location;
+ VLOG(jdwp) << StringPrintf(" this=%#" PRIx64, instance_id);
+ VLOG(jdwp) << StringPrintf(" type=%#" PRIx64, field_type_id) << " "
+ << Dbg::GetClassName(field_id);
+ VLOG(jdwp) << StringPrintf(" field=%#" PRIx32, field_id) << " "
+ << Dbg::GetFieldName(field_id);
+ VLOG(jdwp) << " suspend_policy=" << suspend_policy;
+ }
+
+ ExpandBuf* pReq = eventPrep();
+ expandBufAdd1(pReq, suspend_policy);
+ expandBufAdd4BE(pReq, match_list.size());
+
+ // Get field's reference type tag.
+ JDWP::JdwpTypeTag type_tag = Dbg::GetTypeTag(field->GetDeclaringClass());
+
+ // Get instance type tag.
+ uint8_t tag;
{
- {
- MutexLock mu(Thread::Current(), event_list_lock_);
- match_list = AllocMatchList(event_list_size_);
- if (is_modification) {
- FindMatchingEvents(EK_FIELD_MODIFICATION, basket, match_list, &match_count);
- } else {
- FindMatchingEvents(EK_FIELD_ACCESS, basket, match_list, &match_count);
- }
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+ tag = Dbg::TagFromObject(soa, basket.thisPtr);
+ }
+
+ for (const JdwpEvent* pEvent : match_list) {
+ expandBufAdd1(pReq, pEvent->eventKind);
+ expandBufAdd4BE(pReq, pEvent->requestId);
+ expandBufAddObjectId(pReq, thread_id);
+ expandBufAddLocation(pReq, jdwp_location);
+ expandBufAdd1(pReq, type_tag);
+ expandBufAddRefTypeId(pReq, field_type_id);
+ expandBufAddFieldId(pReq, field_id);
+ expandBufAdd1(pReq, tag);
+ expandBufAddObjectId(pReq, instance_id);
+ if (is_modification) {
+ Dbg::OutputFieldValue(field_id, fieldValue, pReq);
}
- if (match_count != 0) {
- suspend_policy = scanSuspendPolicy(match_list, match_count);
+ }
- thread_id = Dbg::GetThreadId(basket.thread);
- ObjectRegistry* registry = Dbg::GetObjectRegistry();
- ObjectId instance_id = registry->Add(basket.thisPtr);
- RefTypeId field_type_id = registry->AddRefType(field->GetDeclaringClass());
- FieldId field_id = Dbg::ToFieldId(field);
- JDWP::JdwpLocation jdwp_location;
- SetJdwpLocationFromEventLocation(pLoc, &jdwp_location);
-
- if (VLOG_IS_ON(jdwp)) {
- LogMatchingEventsAndThread(match_list, match_count, thread_id);
- VLOG(jdwp) << " location=" << jdwp_location;
- VLOG(jdwp) << StringPrintf(" this=%#" PRIx64, instance_id);
- VLOG(jdwp) << StringPrintf(" type=%#" PRIx64, field_type_id) << " "
- << Dbg::GetClassName(field_id);
- VLOG(jdwp) << StringPrintf(" field=%#" PRIx32, field_id) << " "
- << Dbg::GetFieldName(field_id);
- VLOG(jdwp) << " suspend_policy=" << suspend_policy;
- }
-
- pReq = eventPrep();
- expandBufAdd1(pReq, suspend_policy);
- expandBufAdd4BE(pReq, match_count);
-
- // Get field's reference type tag.
- JDWP::JdwpTypeTag type_tag = Dbg::GetTypeTag(field->GetDeclaringClass());
-
- // Get instance type tag.
- uint8_t tag;
- {
- ScopedObjectAccessUnchecked soa(Thread::Current());
- tag = Dbg::TagFromObject(soa, basket.thisPtr);
- }
-
- for (size_t i = 0; i < match_count; i++) {
- expandBufAdd1(pReq, match_list[i]->eventKind);
- expandBufAdd4BE(pReq, match_list[i]->requestId);
- expandBufAdd8BE(pReq, thread_id);
- expandBufAddLocation(pReq, jdwp_location);
- expandBufAdd1(pReq, type_tag);
- expandBufAddRefTypeId(pReq, field_type_id);
- expandBufAddFieldId(pReq, field_id);
- expandBufAdd1(pReq, tag);
- expandBufAddObjectId(pReq, instance_id);
- if (is_modification) {
- Dbg::OutputFieldValue(field_id, fieldValue, pReq);
- }
- }
- }
-
- {
- MutexLock mu(Thread::Current(), event_list_lock_);
- CleanupMatchList(match_list, match_count);
- }
+ {
+ MutexLock mu(Thread::Current(), event_list_lock_);
+ CleanupMatchList(match_list);
}
Dbg::ManageDeoptimization();
SendRequestAndPossiblySuspend(pReq, suspend_policy, thread_id);
- return match_count != 0;
}
/*
@@ -1038,7 +1004,7 @@
* Valid mods:
* Count, ThreadOnly
*/
-bool JdwpState::PostThreadChange(Thread* thread, bool start) {
+void JdwpState::PostThreadChange(Thread* thread, bool start) {
CHECK_EQ(thread, Thread::Current());
/*
@@ -1046,61 +1012,53 @@
*/
if (InvokeInProgress()) {
LOG(WARNING) << "Not posting thread change during invoke";
- return false;
+ return;
+ }
+
+ // We need the java.lang.Thread object associated to the starting/ending
+ // thread to get its JDWP id. Therefore we can't report event if there
+ // is no Java peer. This happens when the runtime shuts down and re-attaches
+ // the current thread without creating a Java peer.
+ if (thread->GetPeer() == nullptr) {
+ return;
}
ModBasket basket;
basket.thread = thread;
- ExpandBuf* pReq = NULL;
- JdwpSuspendPolicy suspend_policy = SP_NONE;
- JdwpEvent** match_list = nullptr;
- size_t match_count = 0;
- ObjectId thread_id = 0;
+ std::vector<JdwpEvent*> match_list;
+ const JdwpEventKind match_kind = (start) ? EK_THREAD_START : EK_THREAD_DEATH;
+ if (!FindMatchingEvents(match_kind, basket, &match_list)) {
+ // No matching event.
+ return;
+ }
+
+ JdwpSuspendPolicy suspend_policy = ScanSuspendPolicy(match_list);
+ ObjectId thread_id = Dbg::GetThreadId(basket.thread);
+
+ if (VLOG_IS_ON(jdwp)) {
+ LogMatchingEventsAndThread(match_list, thread_id);
+ VLOG(jdwp) << " suspend_policy=" << suspend_policy;
+ }
+
+ ExpandBuf* pReq = eventPrep();
+ expandBufAdd1(pReq, suspend_policy);
+ expandBufAdd4BE(pReq, match_list.size());
+
+ for (const JdwpEvent* pEvent : match_list) {
+ expandBufAdd1(pReq, pEvent->eventKind);
+ expandBufAdd4BE(pReq, pEvent->requestId);
+ expandBufAdd8BE(pReq, thread_id);
+ }
+
{
- {
- // Don't allow the list to be updated while we scan it.
- MutexLock mu(Thread::Current(), event_list_lock_);
- match_list = AllocMatchList(event_list_size_);
- if (start) {
- FindMatchingEvents(EK_THREAD_START, basket, match_list, &match_count);
- } else {
- FindMatchingEvents(EK_THREAD_DEATH, basket, match_list, &match_count);
- }
- }
-
- if (match_count != 0) {
- suspend_policy = scanSuspendPolicy(match_list, match_count);
-
- thread_id = Dbg::GetThreadId(basket.thread);
-
- if (VLOG_IS_ON(jdwp)) {
- LogMatchingEventsAndThread(match_list, match_count, thread_id);
- VLOG(jdwp) << " suspend_policy=" << suspend_policy;
- }
-
- pReq = eventPrep();
- expandBufAdd1(pReq, suspend_policy);
- expandBufAdd4BE(pReq, match_count);
-
- for (size_t i = 0; i < match_count; i++) {
- expandBufAdd1(pReq, match_list[i]->eventKind);
- expandBufAdd4BE(pReq, match_list[i]->requestId);
- expandBufAdd8BE(pReq, thread_id);
- }
- }
-
- {
- MutexLock mu(Thread::Current(), event_list_lock_);
- CleanupMatchList(match_list, match_count);
- }
+ MutexLock mu(Thread::Current(), event_list_lock_);
+ CleanupMatchList(match_list);
}
Dbg::ManageDeoptimization();
SendRequestAndPossiblySuspend(pReq, suspend_policy, thread_id);
-
- return match_count != 0;
}
/*
@@ -1132,7 +1090,7 @@
* because there's a pretty good chance that we're not going to send it
* up the debugger.
*/
-bool JdwpState::PostException(const EventLocation* pThrowLoc, mirror::Throwable* exception_object,
+void JdwpState::PostException(const EventLocation* pThrowLoc, mirror::Throwable* exception_object,
const EventLocation* pCatchLoc, mirror::Object* thisPtr) {
DCHECK(exception_object != nullptr);
DCHECK(pThrowLoc != nullptr);
@@ -1159,72 +1117,61 @@
/* don't try to post an exception caused by the debugger */
if (InvokeInProgress()) {
VLOG(jdwp) << "Not posting exception hit during invoke (" << basket.className << ")";
- return false;
+ return;
}
- size_t match_count = 0;
- ExpandBuf* pReq = NULL;
- JdwpSuspendPolicy suspend_policy = SP_NONE;
- JdwpEvent** match_list = nullptr;
- ObjectId thread_id = 0;
+ std::vector<JdwpEvent*> match_list;
+ if (!FindMatchingEvents(EK_EXCEPTION, basket, &match_list)) {
+ // No matching event.
+ return;
+ }
+
+ JdwpSuspendPolicy suspend_policy = ScanSuspendPolicy(match_list);
+ ObjectId thread_id = Dbg::GetThreadId(basket.thread);
+ ObjectRegistry* registry = Dbg::GetObjectRegistry();
+ ObjectId exceptionId = registry->Add(exception_object);
+ JDWP::JdwpLocation jdwp_throw_location;
+ JDWP::JdwpLocation jdwp_catch_location;
+ SetJdwpLocationFromEventLocation(pThrowLoc, &jdwp_throw_location);
+ SetJdwpLocationFromEventLocation(pCatchLoc, &jdwp_catch_location);
+
+ if (VLOG_IS_ON(jdwp)) {
+ std::string exceptionClassName(PrettyDescriptor(exception_object->GetClass()));
+
+ LogMatchingEventsAndThread(match_list, thread_id);
+ VLOG(jdwp) << " throwLocation=" << jdwp_throw_location;
+ if (jdwp_catch_location.class_id == 0) {
+ VLOG(jdwp) << " catchLocation=uncaught";
+ } else {
+ VLOG(jdwp) << " catchLocation=" << jdwp_catch_location;
+ }
+ VLOG(jdwp) << StringPrintf(" exception=%#" PRIx64, exceptionId) << " "
+ << exceptionClassName;
+ VLOG(jdwp) << " suspend_policy=" << suspend_policy;
+ }
+
+ ExpandBuf* pReq = eventPrep();
+ expandBufAdd1(pReq, suspend_policy);
+ expandBufAdd4BE(pReq, match_list.size());
+
+ for (const JdwpEvent* pEvent : match_list) {
+ expandBufAdd1(pReq, pEvent->eventKind);
+ expandBufAdd4BE(pReq, pEvent->requestId);
+ expandBufAddObjectId(pReq, thread_id);
+ expandBufAddLocation(pReq, jdwp_throw_location);
+ expandBufAdd1(pReq, JT_OBJECT);
+ expandBufAddObjectId(pReq, exceptionId);
+ expandBufAddLocation(pReq, jdwp_catch_location);
+ }
+
{
- {
- MutexLock mu(Thread::Current(), event_list_lock_);
- match_list = AllocMatchList(event_list_size_);
- FindMatchingEvents(EK_EXCEPTION, basket, match_list, &match_count);
- }
- if (match_count != 0) {
- suspend_policy = scanSuspendPolicy(match_list, match_count);
-
- thread_id = Dbg::GetThreadId(basket.thread);
- ObjectRegistry* registry = Dbg::GetObjectRegistry();
- ObjectId exceptionId = registry->Add(exception_object);
- JDWP::JdwpLocation jdwp_throw_location;
- JDWP::JdwpLocation jdwp_catch_location;
- SetJdwpLocationFromEventLocation(pThrowLoc, &jdwp_throw_location);
- SetJdwpLocationFromEventLocation(pCatchLoc, &jdwp_catch_location);
-
- if (VLOG_IS_ON(jdwp)) {
- std::string exceptionClassName(PrettyDescriptor(exception_object->GetClass()));
-
- LogMatchingEventsAndThread(match_list, match_count, thread_id);
- VLOG(jdwp) << " throwLocation=" << jdwp_throw_location;
- if (jdwp_catch_location.class_id == 0) {
- VLOG(jdwp) << " catchLocation=uncaught";
- } else {
- VLOG(jdwp) << " catchLocation=" << jdwp_catch_location;
- }
- VLOG(jdwp) << StringPrintf(" exception=%#" PRIx64, exceptionId) << " "
- << exceptionClassName;
- VLOG(jdwp) << " suspend_policy=" << suspend_policy;
- }
-
- pReq = eventPrep();
- expandBufAdd1(pReq, suspend_policy);
- expandBufAdd4BE(pReq, match_count);
-
- for (size_t i = 0; i < match_count; i++) {
- expandBufAdd1(pReq, match_list[i]->eventKind);
- expandBufAdd4BE(pReq, match_list[i]->requestId);
- expandBufAdd8BE(pReq, thread_id);
- expandBufAddLocation(pReq, jdwp_throw_location);
- expandBufAdd1(pReq, JT_OBJECT);
- expandBufAdd8BE(pReq, exceptionId);
- expandBufAddLocation(pReq, jdwp_catch_location);
- }
- }
-
- {
- MutexLock mu(Thread::Current(), event_list_lock_);
- CleanupMatchList(match_list, match_count);
- }
+ MutexLock mu(Thread::Current(), event_list_lock_);
+ CleanupMatchList(match_list);
}
Dbg::ManageDeoptimization();
SendRequestAndPossiblySuspend(pReq, suspend_policy, thread_id);
-
- return match_count != 0;
}
/*
@@ -1233,7 +1180,7 @@
* Valid mods:
* Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude
*/
-bool JdwpState::PostClassPrepare(mirror::Class* klass) {
+void JdwpState::PostClassPrepare(mirror::Class* klass) {
DCHECK(klass != nullptr);
ModBasket basket;
@@ -1244,80 +1191,85 @@
/* suppress class prep caused by debugger */
if (InvokeInProgress()) {
VLOG(jdwp) << "Not posting class prep caused by invoke (" << basket.className << ")";
- return false;
+ return;
}
- ExpandBuf* pReq = NULL;
- JdwpSuspendPolicy suspend_policy = SP_NONE;
- JdwpEvent** match_list = nullptr;
- size_t match_count = 0;
- ObjectId thread_id = 0;
+ std::vector<JdwpEvent*> match_list;
+ if (!FindMatchingEvents(EK_CLASS_PREPARE, basket, &match_list)) {
+ // No matching event.
+ return;
+ }
+
+ JdwpSuspendPolicy suspend_policy = ScanSuspendPolicy(match_list);
+ ObjectId thread_id = Dbg::GetThreadId(basket.thread);
+ ObjectRegistry* registry = Dbg::GetObjectRegistry();
+ RefTypeId class_id = registry->AddRefType(basket.locationClass);
+
+ // OLD-TODO - we currently always send both "verified" and "prepared" since
+ // debuggers seem to like that. There might be some advantage to honesty,
+ // since the class may not yet be verified.
+ int status = JDWP::CS_VERIFIED | JDWP::CS_PREPARED;
+ JDWP::JdwpTypeTag tag = Dbg::GetTypeTag(basket.locationClass);
+ std::string temp;
+ std::string signature(basket.locationClass->GetDescriptor(&temp));
+
+ if (VLOG_IS_ON(jdwp)) {
+ LogMatchingEventsAndThread(match_list, thread_id);
+ VLOG(jdwp) << StringPrintf(" type=%#" PRIx64, class_id) << " " << signature;
+ VLOG(jdwp) << " suspend_policy=" << suspend_policy;
+ }
+
+ if (thread_id == debug_thread_id_) {
+ /*
+ * JDWP says that, for a class prep in the debugger thread, we
+ * should set thread to null and if any threads were supposed
+ * to be suspended then we suspend all other threads.
+ */
+ VLOG(jdwp) << " NOTE: class prepare in debugger thread!";
+ thread_id = 0;
+ if (suspend_policy == SP_EVENT_THREAD) {
+ suspend_policy = SP_ALL;
+ }
+ }
+
+ ExpandBuf* pReq = eventPrep();
+ expandBufAdd1(pReq, suspend_policy);
+ expandBufAdd4BE(pReq, match_list.size());
+
+ for (const JdwpEvent* pEvent : match_list) {
+ expandBufAdd1(pReq, pEvent->eventKind);
+ expandBufAdd4BE(pReq, pEvent->requestId);
+ expandBufAddObjectId(pReq, thread_id);
+ expandBufAdd1(pReq, tag);
+ expandBufAddRefTypeId(pReq, class_id);
+ expandBufAddUtf8String(pReq, signature);
+ expandBufAdd4BE(pReq, status);
+ }
+
{
- {
- MutexLock mu(Thread::Current(), event_list_lock_);
- match_list = AllocMatchList(event_list_size_);
- FindMatchingEvents(EK_CLASS_PREPARE, basket, match_list, &match_count);
- }
- if (match_count != 0) {
- suspend_policy = scanSuspendPolicy(match_list, match_count);
-
- thread_id = Dbg::GetThreadId(basket.thread);
- ObjectRegistry* registry = Dbg::GetObjectRegistry();
- RefTypeId class_id = registry->AddRefType(basket.locationClass);
-
- // OLD-TODO - we currently always send both "verified" and "prepared" since
- // debuggers seem to like that. There might be some advantage to honesty,
- // since the class may not yet be verified.
- int status = JDWP::CS_VERIFIED | JDWP::CS_PREPARED;
- JDWP::JdwpTypeTag tag = Dbg::GetTypeTag(basket.locationClass);
- std::string temp;
- std::string signature(basket.locationClass->GetDescriptor(&temp));
-
- if (VLOG_IS_ON(jdwp)) {
- LogMatchingEventsAndThread(match_list, match_count, thread_id);
- VLOG(jdwp) << StringPrintf(" type=%#" PRIx64, class_id) << " " << signature;
- VLOG(jdwp) << " suspend_policy=" << suspend_policy;
- }
-
- if (thread_id == debug_thread_id_) {
- /*
- * JDWP says that, for a class prep in the debugger thread, we
- * should set thread to null and if any threads were supposed
- * to be suspended then we suspend all other threads.
- */
- VLOG(jdwp) << " NOTE: class prepare in debugger thread!";
- thread_id = 0;
- if (suspend_policy == SP_EVENT_THREAD) {
- suspend_policy = SP_ALL;
- }
- }
-
- pReq = eventPrep();
- expandBufAdd1(pReq, suspend_policy);
- expandBufAdd4BE(pReq, match_count);
-
- for (size_t i = 0; i < match_count; i++) {
- expandBufAdd1(pReq, match_list[i]->eventKind);
- expandBufAdd4BE(pReq, match_list[i]->requestId);
- expandBufAdd8BE(pReq, thread_id);
- expandBufAdd1(pReq, tag);
- expandBufAdd8BE(pReq, class_id);
- expandBufAddUtf8String(pReq, signature);
- expandBufAdd4BE(pReq, status);
- }
- }
-
- {
- MutexLock mu(Thread::Current(), event_list_lock_);
- CleanupMatchList(match_list, match_count);
- }
+ MutexLock mu(Thread::Current(), event_list_lock_);
+ CleanupMatchList(match_list);
}
Dbg::ManageDeoptimization();
SendRequestAndPossiblySuspend(pReq, suspend_policy, thread_id);
+}
- return match_count != 0;
+/*
+ * Setup the header for a chunk of DDM data.
+ */
+void JdwpState::SetupChunkHeader(uint32_t type, size_t data_len, size_t header_size,
+ uint8_t* out_header) {
+ CHECK_EQ(header_size, static_cast<size_t>(kJDWPHeaderLen + 8));
+ /* form the header (JDWP plus DDMS) */
+ Set4BE(out_header, header_size + data_len);
+ Set4BE(out_header + 4, NextRequestSerial());
+ Set1(out_header + 8, 0); /* flags */
+ Set1(out_header + 9, kJDWPDdmCmdSet);
+ Set1(out_header + 10, kJDWPDdmCmd);
+ Set4BE(out_header + 11, type);
+ Set4BE(out_header + 15, data_len);
}
/*
@@ -1328,10 +1280,10 @@
* the fun event token gymnastics.
*/
void JdwpState::DdmSendChunkV(uint32_t type, const iovec* iov, int iov_count) {
- uint8_t header[kJDWPHeaderLen + 8];
+ uint8_t header[kJDWPHeaderLen + 8] = { 0 };
size_t dataLen = 0;
- CHECK(iov != NULL);
+ CHECK(iov != nullptr);
CHECK_GT(iov_count, 0);
CHECK_LT(iov_count, 10);
@@ -1346,14 +1298,7 @@
dataLen += iov[i].iov_len;
}
- /* form the header (JDWP plus DDMS) */
- Set4BE(header, sizeof(header) + dataLen);
- Set4BE(header+4, NextRequestSerial());
- Set1(header+8, 0); /* flags */
- Set1(header+9, kJDWPDdmCmdSet);
- Set1(header+10, kJDWPDdmCmd);
- Set4BE(header+11, type);
- Set4BE(header+15, dataLen);
+ SetupChunkHeader(type, dataLen, sizeof(header), header);
wrapiov[0].iov_base = header;
wrapiov[0].iov_len = sizeof(header);
@@ -1364,7 +1309,7 @@
bool safe_to_release_mutator_lock_over_send = !Locks::mutator_lock_->IsExclusiveHeld(self);
if (safe_to_release_mutator_lock_over_send) {
for (size_t i = 0; i < kMutatorLock; ++i) {
- if (self->GetHeldMutex(static_cast<LockLevel>(i)) != NULL) {
+ if (self->GetHeldMutex(static_cast<LockLevel>(i)) != nullptr) {
safe_to_release_mutator_lock_over_send = false;
break;
}
diff --git a/runtime/jdwp/jdwp_expand_buf.cc b/runtime/jdwp/jdwp_expand_buf.cc
index 0a64f28..cc85cdd 100644
--- a/runtime/jdwp/jdwp_expand_buf.cc
+++ b/runtime/jdwp/jdwp_expand_buf.cc
@@ -57,7 +57,7 @@
* Free a JdwpBuf and associated storage.
*/
void expandBufFree(ExpandBuf* pBuf) {
- if (pBuf == NULL) {
+ if (pBuf == nullptr) {
return;
}
@@ -93,7 +93,7 @@
}
uint8_t* newPtr = reinterpret_cast<uint8_t*>(realloc(pBuf->storage, pBuf->maxLen));
- if (newPtr == NULL) {
+ if (newPtr == nullptr) {
LOG(FATAL) << "realloc(" << pBuf->maxLen << ") failed";
}
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index be34bd3..a1d2a6c 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -106,8 +106,8 @@
Dbg::GetMethodName(method_id).c_str());
VLOG(jdwp) << StringPrintf(" %d args:", arg_count);
- std::unique_ptr<JdwpTag[]> argTypes(arg_count > 0 ? new JdwpTag[arg_count] : NULL);
- std::unique_ptr<uint64_t[]> argValues(arg_count > 0 ? new uint64_t[arg_count] : NULL);
+ std::unique_ptr<JdwpTag[]> argTypes(arg_count > 0 ? new JdwpTag[arg_count] : nullptr);
+ std::unique_ptr<uint64_t[]> argValues(arg_count > 0 ? new uint64_t[arg_count] : nullptr);
for (int32_t i = 0; i < arg_count; ++i) {
argTypes[i] = request->ReadTag();
size_t width = Dbg::GetTagWidth(argTypes[i]);
@@ -124,7 +124,9 @@
JdwpTag resultTag;
uint64_t resultValue;
ObjectId exceptObjId;
- JdwpError err = Dbg::InvokeMethod(thread_id, object_id, class_id, method_id, arg_count, argValues.get(), argTypes.get(), options, &resultTag, &resultValue, &exceptObjId);
+ JdwpError err = Dbg::InvokeMethod(thread_id, object_id, class_id, method_id, arg_count,
+ argValues.get(), argTypes.get(), options, &resultTag,
+ &resultValue, &exceptObjId);
if (err != ERR_NONE) {
return err;
}
@@ -203,7 +205,7 @@
// Get class vs. interface and status flags.
JDWP::JdwpTypeTag type_tag;
uint32_t class_status;
- JDWP::JdwpError status = Dbg::GetClassInfo(ids[i], &type_tag, &class_status, NULL);
+ JDWP::JdwpError status = Dbg::GetClassInfo(ids[i], &type_tag, &class_status, nullptr);
if (status != ERR_NONE) {
return status;
}
@@ -331,15 +333,15 @@
std::vector<std::string> class_path;
Split(Runtime::Current()->GetClassPathString(), ':', &class_path);
expandBufAdd4BE(pReply, class_path.size());
- for (size_t i = 0; i < class_path.size(); ++i) {
- expandBufAddUtf8String(pReply, class_path[i]);
+ for (const std::string& str : class_path) {
+ expandBufAddUtf8String(pReply, str);
}
std::vector<std::string> boot_class_path;
Split(Runtime::Current()->GetBootClassPathString(), ':', &boot_class_path);
expandBufAdd4BE(pReply, boot_class_path.size());
- for (size_t i = 0; i < boot_class_path.size(); ++i) {
- expandBufAddUtf8String(pReply, boot_class_path[i]);
+ for (const std::string& str : boot_class_path) {
+ expandBufAddUtf8String(pReply, str);
}
return ERR_NONE;
@@ -371,7 +373,7 @@
static JdwpError VM_CapabilitiesNew(JdwpState*, Request* request, ExpandBuf* reply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// The first few capabilities are the same as those reported by the older call.
- VM_Capabilities(NULL, request, reply);
+ VM_Capabilities(nullptr, request, reply);
expandBufAdd1(reply, false); // canRedefineClasses
expandBufAdd1(reply, false); // canAddMethod
@@ -507,7 +509,7 @@
RefTypeId refTypeId = request->ReadRefTypeId();
JDWP::JdwpTypeTag type_tag;
uint32_t class_status;
- JDWP::JdwpError status = Dbg::GetClassInfo(refTypeId, &type_tag, &class_status, NULL);
+ JDWP::JdwpError status = Dbg::GetClassInfo(refTypeId, &type_tag, &class_status, nullptr);
if (status != ERR_NONE) {
return status;
}
@@ -1345,8 +1347,10 @@
}
break;
default:
- LOG(WARNING) << "GLITCH: unsupported modKind=" << mod.modKind;
- break;
+ LOG(WARNING) << "Unsupported modifier " << mod.modKind << " for event " << pEvent->eventKind;
+ // Free allocated event to avoid leak before leaving.
+ EventFree(pEvent);
+ return JDWP::ERR_NOT_IMPLEMENTED;
}
}
@@ -1430,7 +1434,7 @@
static JdwpError DDM_Chunk(JdwpState* state, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
state->NotifyDdmsActive();
- uint8_t* replyBuf = NULL;
+ uint8_t* replyBuf = nullptr;
int replyLen = -1;
if (Dbg::DdmHandlePacket(request, &replyBuf, &replyLen)) {
// If they want to send something back, we copy it into the buffer.
@@ -1479,11 +1483,11 @@
{ 1, 12, VM_Capabilities, "VirtualMachine.Capabilities" },
{ 1, 13, VM_ClassPaths, "VirtualMachine.ClassPaths" },
{ 1, 14, VM_DisposeObjects, "VirtualMachine.DisposeObjects" },
- { 1, 15, NULL, "VirtualMachine.HoldEvents" },
- { 1, 16, NULL, "VirtualMachine.ReleaseEvents" },
+ { 1, 15, nullptr, "VirtualMachine.HoldEvents" },
+ { 1, 16, nullptr, "VirtualMachine.ReleaseEvents" },
{ 1, 17, VM_CapabilitiesNew, "VirtualMachine.CapabilitiesNew" },
- { 1, 18, NULL, "VirtualMachine.RedefineClasses" },
- { 1, 19, NULL, "VirtualMachine.SetDefaultStratum" },
+ { 1, 18, nullptr, "VirtualMachine.RedefineClasses" },
+ { 1, 19, nullptr, "VirtualMachine.SetDefaultStratum" },
{ 1, 20, VM_AllClassesWithGeneric, "VirtualMachine.AllClassesWithGeneric" },
{ 1, 21, VM_InstanceCounts, "VirtualMachine.InstanceCounts" },
@@ -1495,7 +1499,7 @@
{ 2, 5, RT_Methods, "ReferenceType.Methods" },
{ 2, 6, RT_GetValues, "ReferenceType.GetValues" },
{ 2, 7, RT_SourceFile, "ReferenceType.SourceFile" },
- { 2, 8, NULL, "ReferenceType.NestedTypes" },
+ { 2, 8, nullptr, "ReferenceType.NestedTypes" },
{ 2, 9, RT_Status, "ReferenceType.Status" },
{ 2, 10, RT_Interfaces, "ReferenceType.Interfaces" },
{ 2, 11, RT_ClassObject, "ReferenceType.ClassObject" },
@@ -1504,8 +1508,8 @@
{ 2, 14, RT_FieldsWithGeneric, "ReferenceType.FieldsWithGeneric" },
{ 2, 15, RT_MethodsWithGeneric, "ReferenceType.MethodsWithGeneric" },
{ 2, 16, RT_Instances, "ReferenceType.Instances" },
- { 2, 17, NULL, "ReferenceType.ClassFileVersion" },
- { 2, 18, NULL, "ReferenceType.ConstantPool" },
+ { 2, 17, nullptr, "ReferenceType.ClassFileVersion" },
+ { 2, 18, nullptr, "ReferenceType.ConstantPool" },
/* ClassType command set (3) */
{ 3, 1, CT_Superclass, "ClassType.Superclass" },
@@ -1522,7 +1526,7 @@
{ 6, 1, M_LineTable, "Method.LineTable" },
{ 6, 2, M_VariableTable, "Method.VariableTable" },
{ 6, 3, M_Bytecodes, "Method.Bytecodes" },
- { 6, 4, NULL, "Method.IsObsolete" },
+ { 6, 4, nullptr, "Method.IsObsolete" },
{ 6, 5, M_VariableTableWithGeneric, "Method.VariableTableWithGeneric" },
/* Field command set (8) */
@@ -1531,7 +1535,7 @@
{ 9, 1, OR_ReferenceType, "ObjectReference.ReferenceType" },
{ 9, 2, OR_GetValues, "ObjectReference.GetValues" },
{ 9, 3, OR_SetValues, "ObjectReference.SetValues" },
- { 9, 4, NULL, "ObjectReference.UNUSED" },
+ { 9, 4, nullptr, "ObjectReference.UNUSED" },
{ 9, 5, OR_MonitorInfo, "ObjectReference.MonitorInfo" },
{ 9, 6, OR_InvokeMethod, "ObjectReference.InvokeMethod" },
{ 9, 7, OR_DisableCollection, "ObjectReference.DisableCollection" },
@@ -1552,11 +1556,11 @@
{ 11, 7, TR_FrameCount, "ThreadReference.FrameCount" },
{ 11, 8, TR_OwnedMonitors, "ThreadReference.OwnedMonitors" },
{ 11, 9, TR_CurrentContendedMonitor, "ThreadReference.CurrentContendedMonitor" },
- { 11, 10, NULL, "ThreadReference.Stop" },
+ { 11, 10, nullptr, "ThreadReference.Stop" },
{ 11, 11, TR_Interrupt, "ThreadReference.Interrupt" },
{ 11, 12, TR_DebugSuspendCount, "ThreadReference.SuspendCount" },
{ 11, 13, TR_OwnedMonitorsStackDepthInfo, "ThreadReference.OwnedMonitorsStackDepthInfo" },
- { 11, 14, NULL, "ThreadReference.ForceEarlyReturn" },
+ { 11, 14, nullptr, "ThreadReference.ForceEarlyReturn" },
/* ThreadGroupReference command set (12) */
{ 12, 1, TGR_Name, "ThreadGroupReference.Name" },
@@ -1574,26 +1578,27 @@
/* EventRequest command set (15) */
{ 15, 1, ER_Set, "EventRequest.Set" },
{ 15, 2, ER_Clear, "EventRequest.Clear" },
- { 15, 3, NULL, "EventRequest.ClearAllBreakpoints" },
+ { 15, 3, nullptr, "EventRequest.ClearAllBreakpoints" },
/* StackFrame command set (16) */
{ 16, 1, SF_GetValues, "StackFrame.GetValues" },
{ 16, 2, SF_SetValues, "StackFrame.SetValues" },
{ 16, 3, SF_ThisObject, "StackFrame.ThisObject" },
- { 16, 4, NULL, "StackFrame.PopFrames" },
+ { 16, 4, nullptr, "StackFrame.PopFrames" },
/* ClassObjectReference command set (17) */
{ 17, 1, COR_ReflectedType, "ClassObjectReference.ReflectedType" },
/* Event command set (64) */
- { 64, 100, NULL, "Event.Composite" }, // sent from VM to debugger, never received by VM
+ { 64, 100, nullptr, "Event.Composite" }, // sent from VM to debugger, never received by VM
{ 199, 1, DDM_Chunk, "DDM.Chunk" },
};
static const char* GetCommandName(Request* request) {
for (size_t i = 0; i < arraysize(gHandlers); ++i) {
- if (gHandlers[i].cmdSet == request->GetCommandSet() && gHandlers[i].cmd == request->GetCommand()) {
+ if (gHandlers[i].cmdSet == request->GetCommandSet() &&
+ gHandlers[i].cmd == request->GetCommand()) {
return gHandlers[i].name;
}
}
@@ -1661,7 +1666,9 @@
size_t i;
for (i = 0; i < arraysize(gHandlers); ++i) {
- if (gHandlers[i].cmdSet == request->GetCommandSet() && gHandlers[i].cmd == request->GetCommand() && gHandlers[i].func != NULL) {
+ if (gHandlers[i].cmdSet == request->GetCommandSet() &&
+ gHandlers[i].cmd == request->GetCommand() &&
+ gHandlers[i].func != nullptr) {
VLOG(jdwp) << DescribeCommand(request);
result = (*gHandlers[i].func)(this, request, pReply);
if (result == ERR_NONE) {
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index c500ef5..b04aa6e 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -135,11 +135,16 @@
*/
ssize_t JdwpNetStateBase::WriteBufferedPacket(const std::vector<iovec>& iov) {
MutexLock mu(Thread::Current(), socket_lock_);
+ return WriteBufferedPacketLocked(iov);
+}
+
+ssize_t JdwpNetStateBase::WriteBufferedPacketLocked(const std::vector<iovec>& iov) {
+ socket_lock_.AssertHeld(Thread::Current());
return TEMP_FAILURE_RETRY(writev(clientSock, &iov[0], iov.size()));
}
bool JdwpState::IsConnected() {
- return netState != NULL && netState->IsConnected();
+ return netState != nullptr && netState->IsConnected();
}
void JdwpState::SendBufferedRequest(uint32_t type, const std::vector<iovec>& iov) {
@@ -202,18 +207,18 @@
thread_start_lock_("JDWP thread start lock", kJdwpStartLock),
thread_start_cond_("JDWP thread start condition variable", thread_start_lock_),
pthread_(0),
- thread_(NULL),
+ thread_(nullptr),
debug_thread_started_(false),
debug_thread_id_(0),
run(false),
- netState(NULL),
+ netState(nullptr),
attach_lock_("JDWP attach lock", kJdwpAttachLock),
attach_cond_("JDWP attach condition variable", attach_lock_),
last_activity_time_ms_(0),
request_serial_(0x10000000),
event_serial_(0x20000000),
event_list_lock_("JDWP event list lock", kJdwpEventListLock),
- event_list_(NULL),
+ event_list_(nullptr),
event_list_size_(0),
event_thread_lock_("JDWP event thread lock"),
event_thread_cond_("JDWP event thread condition variable", event_thread_lock_),
@@ -289,7 +294,7 @@
}
if (!state->IsActive()) {
LOG(ERROR) << "JDWP connection failed";
- return NULL;
+ return nullptr;
}
LOG(INFO) << "JDWP connected";
@@ -317,7 +322,7 @@
UnregisterAll();
{
MutexLock mu(Thread::Current(), event_list_lock_);
- CHECK(event_list_ == NULL);
+ CHECK(event_list_ == nullptr);
}
Dbg::ProcessDelayedFullUndeoptimizations();
@@ -336,7 +341,7 @@
* Tell the JDWP thread to shut down. Frees "state".
*/
JdwpState::~JdwpState() {
- if (netState != NULL) {
+ if (netState != nullptr) {
/*
* Close down the network to inspire the thread to halt.
*/
@@ -353,9 +358,9 @@
VLOG(jdwp) << "JDWP freeing netstate...";
delete netState;
- netState = NULL;
+ netState = nullptr;
}
- CHECK(netState == NULL);
+ CHECK(netState == nullptr);
ResetState();
}
@@ -398,10 +403,10 @@
*/
static void* StartJdwpThread(void* arg) {
JdwpState* state = reinterpret_cast<JdwpState*>(arg);
- CHECK(state != NULL);
+ CHECK(state != nullptr);
state->Run();
- return NULL;
+ return nullptr;
}
void JdwpState::Run() {
@@ -614,6 +619,18 @@
return !(lhs == rhs);
}
+bool operator==(const JdwpOptions& lhs, const JdwpOptions& rhs) {
+ if (&lhs == &rhs) {
+ return true;
+ }
+
+ return lhs.transport == rhs.transport &&
+ lhs.server == rhs.server &&
+ lhs.suspend == rhs.suspend &&
+ lhs.host == rhs.host &&
+ lhs.port == rhs.port;
+}
+
} // namespace JDWP
} // namespace art
diff --git a/runtime/jdwp/jdwp_priv.h b/runtime/jdwp/jdwp_priv.h
index 29ad185..f290be0 100644
--- a/runtime/jdwp/jdwp_priv.h
+++ b/runtime/jdwp/jdwp_priv.h
@@ -71,6 +71,10 @@
ssize_t WritePacket(ExpandBuf* pReply, size_t length) LOCKS_EXCLUDED(socket_lock_);
ssize_t WriteBufferedPacket(const std::vector<iovec>& iov) LOCKS_EXCLUDED(socket_lock_);
+ Mutex* GetSocketLock() {
+ return &socket_lock_;
+ }
+ ssize_t WriteBufferedPacketLocked(const std::vector<iovec>& iov);
int clientSock; // Active connection to debugger.
diff --git a/runtime/jdwp/jdwp_socket.cc b/runtime/jdwp/jdwp_socket.cc
index 7119ce5..cbdde24 100644
--- a/runtime/jdwp/jdwp_socket.cc
+++ b/runtime/jdwp/jdwp_socket.cc
@@ -77,12 +77,12 @@
/* scan through a range of ports, binding to the first available */
for (port = kBasePort; port <= kMaxPort; port++) {
state->netState = SocketStartup(state, port, true);
- if (state->netState != NULL) {
+ if (state->netState != nullptr) {
break;
}
}
}
- if (state->netState == NULL) {
+ if (state->netState == nullptr) {
LOG(ERROR) << "JDWP net startup failed (req port=" << options->port << ")";
return false;
}
@@ -157,7 +157,7 @@
fail:
netState->Shutdown();
delete netState;
- return NULL;
+ return nullptr;
}
/*
@@ -284,7 +284,7 @@
#else
h_errno = 0;
pEntry = gethostbyname(options->host.c_str());
- if (pEntry == NULL) {
+ if (pEntry == nullptr) {
PLOG(WARNING) << "gethostbyname('" << options->host << "') failed";
return false;
}
@@ -403,7 +403,7 @@
* re-issue the select. We're currently using #2, as it's more
* reliable than #1 and generally better than #3. Wastes two fds.
*/
- selCount = select(maxfd+1, &readfds, NULL, NULL, NULL);
+ selCount = select(maxfd + 1, &readfds, nullptr, nullptr, nullptr);
if (selCount < 0) {
if (errno == EINTR) {
continue;
diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc
index 9123994..e415c3d 100644
--- a/runtime/jdwp/object_registry.cc
+++ b/runtime/jdwp/object_registry.cc
@@ -104,7 +104,20 @@
}
void ObjectRegistry::Clear() {
- Thread* self = Thread::Current();
+ Thread* const self = Thread::Current();
+
+ // We must not hold the mutator lock exclusively if we want to delete weak global
+ // references. Otherwise this can lead to a deadlock with a running GC:
+ // 1. GC thread disables access to weak global references, then releases
+ // mutator lock.
+ // 2. JDWP thread takes mutator lock exclusively after suspending all
+ // threads.
+ // 3. GC thread waits for shared mutator lock which is held by JDWP
+ // thread.
+ // 4. JDWP thread clears weak global references but need to wait for GC
+ // thread to re-enable access to them.
+ Locks::mutator_lock_->AssertNotExclusiveHeld(self);
+
MutexLock mu(self, lock_);
VLOG(jdwp) << "Object registry contained " << object_to_entry_.size() << " entries";
// Delete all the JNI references.
@@ -138,7 +151,7 @@
jobject ObjectRegistry::GetJObject(JDWP::ObjectId id) {
if (id == 0) {
- return NULL;
+ return nullptr;
}
Thread* self = Thread::Current();
MutexLock mu(self, lock_);
@@ -194,7 +207,7 @@
ObjectRegistryEntry& entry = *it->second;
if (entry.jni_reference_type == JNIWeakGlobalRefType) {
JNIEnv* env = self->GetJniEnv();
- return env->IsSameObject(entry.jni_reference, NULL); // Has the jweak been collected?
+ return env->IsSameObject(entry.jni_reference, nullptr); // Has the jweak been collected?
} else {
return false; // We hold a strong reference, so we know this is live.
}
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 4797e69..37ad46e 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -2256,8 +2256,10 @@
java_buffer, WellKnownClasses::java_nio_DirectByteBuffer_capacity));
}
- static jobjectRefType GetObjectRefType(JNIEnv* env, jobject java_object) {
- CHECK_NON_NULL_ARGUMENT_RETURN(java_object, JNIInvalidRefType);
+ static jobjectRefType GetObjectRefType(JNIEnv* env ATTRIBUTE_UNUSED, jobject java_object) {
+ if (java_object == nullptr) {
+ return JNIInvalidRefType;
+ }
// Do we definitely know what kind of reference this is?
IndirectRef ref = reinterpret_cast<IndirectRef>(java_object);
@@ -2274,7 +2276,7 @@
return JNILocalRefType;
}
LOG(FATAL) << "IndirectRefKind[" << kind << "]";
- return JNIInvalidRefType;
+ UNREACHABLE();
}
private:
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 62b6b34..906aa4c 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -634,6 +634,9 @@
}
TEST_F(JniInternalTest, FindClass) {
+ // This tests leads to warnings in the log.
+ ScopedLogSeverity sls(LogSeverity::ERROR);
+
FindClassTest(false);
FindClassTest(true);
}
@@ -890,40 +893,44 @@
// Sanity check that no exceptions are pending.
ASSERT_FALSE(env_->ExceptionCheck());
- // Check that registering method without name causes a NoSuchMethodError.
+ // The following can print errors to the log we'd like to ignore.
{
- JNINativeMethod methods[] = { { nullptr, "()V", native_function } };
- EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
- }
- ExpectException(jlnsme);
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+ // Check that registering method without name causes a NoSuchMethodError.
+ {
+ JNINativeMethod methods[] = { { nullptr, "()V", native_function } };
+ EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+ }
+ ExpectException(jlnsme);
- // Check that registering method without signature causes a NoSuchMethodError.
- {
- JNINativeMethod methods[] = { { "notify", nullptr, native_function } };
- EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
- }
- ExpectException(jlnsme);
+ // Check that registering method without signature causes a NoSuchMethodError.
+ {
+ JNINativeMethod methods[] = { { "notify", nullptr, native_function } };
+ EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+ }
+ ExpectException(jlnsme);
- // Check that registering method without function causes a NoSuchMethodError.
- {
- JNINativeMethod methods[] = { { "notify", "()V", nullptr } };
- EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
- }
- ExpectException(jlnsme);
+ // Check that registering method without function causes a NoSuchMethodError.
+ {
+ JNINativeMethod methods[] = { { "notify", "()V", nullptr } };
+ EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+ }
+ ExpectException(jlnsme);
- // Check that registering to a non-existent java.lang.Object.foo() causes a NoSuchMethodError.
- {
- JNINativeMethod methods[] = { { "foo", "()V", native_function } };
- EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
- }
- ExpectException(jlnsme);
+ // Check that registering to a non-existent java.lang.Object.foo() causes a NoSuchMethodError.
+ {
+ JNINativeMethod methods[] = { { "foo", "()V", native_function } };
+ EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+ }
+ ExpectException(jlnsme);
- // Check that registering non-native methods causes a NoSuchMethodError.
- {
- JNINativeMethod methods[] = { { "equals", "(Ljava/lang/Object;)Z", native_function } };
- EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+ // Check that registering non-native methods causes a NoSuchMethodError.
+ {
+ JNINativeMethod methods[] = { { "equals", "(Ljava/lang/Object;)Z", native_function } };
+ EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+ }
+ ExpectException(jlnsme);
}
- ExpectException(jlnsme);
// Check that registering native methods is successful.
{
@@ -1300,16 +1307,20 @@
jweak weak_global = env_->NewWeakGlobalRef(local);
EXPECT_EQ(JNIWeakGlobalRefType, env_->GetObjectRefType(weak_global));
- CheckJniAbortCatcher jni_abort_catcher;
- jobject invalid = reinterpret_cast<jobject>(this);
- EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(invalid));
- jni_abort_catcher.Check("use of invalid jobject");
+ {
+ CheckJniAbortCatcher jni_abort_catcher;
+ jobject invalid = reinterpret_cast<jobject>(this);
+ EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(invalid));
+ jni_abort_catcher.Check("use of invalid jobject");
+ }
// TODO: invoke a native method and test that its arguments are considered local references.
- // Null as object should fail.
+ // Null as pointer should not fail and return invalid-ref. b/18820997
EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(nullptr));
- jni_abort_catcher.Check("java_object == null");
+
+ // TODO: Null as reference should return the original type.
+ // This requires running a GC so a non-null object gets freed.
}
TEST_F(JniInternalTest, StaleWeakGlobal) {
@@ -1624,7 +1635,6 @@
TEST_F(JniInternalTest, GetPrimitiveField_SetPrimitiveField) {
- TEST_DISABLED_FOR_PORTABLE();
Thread::Current()->TransitionFromSuspendedToRunnable();
LoadDex("AllFields");
bool started = runtime_->Start();
@@ -1655,7 +1665,6 @@
}
TEST_F(JniInternalTest, GetObjectField_SetObjectField) {
- TEST_DISABLED_FOR_PORTABLE();
Thread::Current()->TransitionFromSuspendedToRunnable();
LoadDex("AllFields");
runtime_->Start();
@@ -1705,6 +1714,9 @@
}
TEST_F(JniInternalTest, DeleteLocalRef) {
+ // This tests leads to warnings and errors in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
jstring s = env_->NewStringUTF("");
ASSERT_NE(s, nullptr);
env_->DeleteLocalRef(s);
@@ -1741,6 +1753,9 @@
ASSERT_EQ(JNI_OK, env_->PushLocalFrame(0));
env_->PopLocalFrame(nullptr);
+ // The following two tests will print errors to the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
// Negative capacities are not allowed.
ASSERT_EQ(JNI_ERR, env_->PushLocalFrame(-1));
@@ -1749,6 +1764,9 @@
}
TEST_F(JniInternalTest, PushLocalFrame_PopLocalFrame) {
+ // This tests leads to errors in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
jobject original = env_->NewStringUTF("");
ASSERT_NE(original, nullptr);
@@ -1813,6 +1831,9 @@
}
TEST_F(JniInternalTest, DeleteGlobalRef) {
+ // This tests leads to warnings and errors in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
jstring s = env_->NewStringUTF("");
ASSERT_NE(s, nullptr);
@@ -1863,6 +1884,9 @@
}
TEST_F(JniInternalTest, DeleteWeakGlobalRef) {
+ // This tests leads to warnings and errors in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
jstring s = env_->NewStringUTF("");
ASSERT_NE(s, nullptr);
@@ -1987,6 +2011,9 @@
}
TEST_F(JniInternalTest, MonitorEnterExit) {
+ // This will print some error messages. Suppress.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
// Create an object to torture.
jclass object_class = env_->FindClass("java/lang/Object");
ASSERT_NE(object_class, nullptr);
diff --git a/runtime/jobject_comparator.cc b/runtime/jobject_comparator.cc
index e22d75f..1f424b3 100644
--- a/runtime/jobject_comparator.cc
+++ b/runtime/jobject_comparator.cc
@@ -25,33 +25,32 @@
bool JobjectComparator::operator()(jobject jobj1, jobject jobj2) const {
// Ensure null references and cleared jweaks appear at the end.
- if (jobj1 == NULL) {
+ if (jobj1 == nullptr) {
return true;
- } else if (jobj2 == NULL) {
+ } else if (jobj2 == nullptr) {
return false;
}
ScopedObjectAccess soa(Thread::Current());
- mirror::Object* obj1 = soa.Decode<mirror::Object*>(jobj1);
- mirror::Object* obj2 = soa.Decode<mirror::Object*>(jobj2);
- if (obj1 == NULL) {
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::Object> obj1(hs.NewHandle(soa.Decode<mirror::Object*>(jobj1)));
+ Handle<mirror::Object> obj2(hs.NewHandle(soa.Decode<mirror::Object*>(jobj2)));
+ if (obj1.Get() == nullptr) {
return true;
- } else if (obj2 == NULL) {
+ } else if (obj2.Get() == nullptr) {
return false;
}
// Sort by class...
if (obj1->GetClass() != obj2->GetClass()) {
- return obj1->GetClass()->IdentityHashCode() < obj2->IdentityHashCode();
- } else {
- // ...then by size...
- size_t count1 = obj1->SizeOf();
- size_t count2 = obj2->SizeOf();
- if (count1 != count2) {
- return count1 < count2;
- } else {
- // ...and finally by identity hash code.
- return obj1->IdentityHashCode() < obj2->IdentityHashCode();
- }
+ return obj1->GetClass()->IdentityHashCode() < obj2->GetClass()->IdentityHashCode();
}
+ // ...then by size...
+ const size_t count1 = obj1->SizeOf();
+ const size_t count2 = obj2->SizeOf();
+ if (count1 != count2) {
+ return count1 < count2;
+ }
+ // ...and finally by identity hash code.
+ return obj1->IdentityHashCode() < obj2->IdentityHashCode();
}
} // namespace art
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 8303f84..a722813 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -665,6 +665,19 @@
maps_ = nullptr;
}
+void MemMap::SetSize(size_t new_size) {
+ if (new_size == base_size_) {
+ return;
+ }
+ CHECK_ALIGNED(new_size, kPageSize);
+ CHECK_EQ(base_size_, size_) << "Unsupported";
+ CHECK_LE(new_size, base_size_);
+ CHECK_EQ(munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(BaseBegin()) + new_size),
+ base_size_ - new_size), 0) << new_size << " " << base_size_;
+ base_size_ = new_size;
+ size_ = new_size;
+}
+
std::ostream& operator<<(std::ostream& os, const MemMap& mem_map) {
os << StringPrintf("[MemMap: %p-%p prot=0x%x %s]",
mem_map.BaseBegin(), mem_map.BaseEnd(), mem_map.GetProtect(),
diff --git a/runtime/mem_map.h b/runtime/mem_map.h
index 9b003aa..dc337e0 100644
--- a/runtime/mem_map.h
+++ b/runtime/mem_map.h
@@ -107,6 +107,9 @@
return size_;
}
+ // Resize the mem-map by unmapping pages at the end. Currently only supports shrinking.
+ void SetSize(size_t new_size);
+
uint8_t* End() const {
return Begin() + Size();
}
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 13f881d..048d8ba 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -19,6 +19,7 @@
#include "array.h"
+#include "base/stringprintf.h"
#include "class.h"
#include "gc/heap-inl.h"
#include "thread.h"
@@ -199,9 +200,7 @@
template<class T>
inline void PrimitiveArray<T>::VisitRoots(RootCallback* callback, void* arg) {
- if (!array_class_.IsNull()) {
- array_class_.VisitRoot(callback, arg, 0, kRootStickyClass);
- }
+ array_class_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
}
template<typename T>
diff --git a/runtime/mirror/art_field.cc b/runtime/mirror/art_field.cc
index 7e20076..5a4ebd1 100644
--- a/runtime/mirror/art_field.cc
+++ b/runtime/mirror/art_field.cc
@@ -56,9 +56,7 @@
}
void ArtField::VisitRoots(RootCallback* callback, void* arg) {
- if (!java_lang_reflect_ArtField_.IsNull()) {
- java_lang_reflect_ArtField_.VisitRoot(callback, arg, 0, kRootStickyClass);
- }
+ java_lang_reflect_ArtField_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
}
// TODO: we could speed up the search if fields are ordered by offsets.
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index c29276a..7d31148 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -24,6 +24,7 @@
#include "class_linker.h"
#include "dex_cache.h"
#include "dex_file.h"
+#include "dex_file-inl.h"
#include "object-inl.h"
#include "object_array.h"
#include "oat.h"
@@ -72,12 +73,7 @@
}
inline uint32_t ArtMethod::GetDexMethodIndex() {
-#ifdef ART_SEA_IR_MODE
- // TODO: Re-add this check for (PORTABLE + SMALL + ) SEA IR when PORTABLE IS fixed!
- // DCHECK(GetDeclaringClass()->IsLoaded() || GetDeclaringClass()->IsErroneous());
-#else
DCHECK(GetDeclaringClass()->IsLoaded() || GetDeclaringClass()->IsErroneous());
-#endif
return GetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_method_index_));
}
@@ -187,21 +183,11 @@
return PointerToLowMemUInt32(GetEntryPointFromQuickCompiledCode());
}
-inline uint32_t ArtMethod::GetPortableOatCodeOffset() {
- DCHECK(!Runtime::Current()->IsStarted());
- return PointerToLowMemUInt32(GetEntryPointFromPortableCompiledCode());
-}
-
inline void ArtMethod::SetQuickOatCodeOffset(uint32_t code_offset) {
DCHECK(!Runtime::Current()->IsStarted());
SetEntryPointFromQuickCompiledCode(reinterpret_cast<void*>(code_offset));
}
-inline void ArtMethod::SetPortableOatCodeOffset(uint32_t code_offset) {
- DCHECK(!Runtime::Current()->IsStarted());
- SetEntryPointFromPortableCompiledCode(reinterpret_cast<void*>(code_offset));
-}
-
inline const uint8_t* ArtMethod::GetMappingTable(size_t pointer_size) {
const void* code_pointer = GetQuickOatCodePointer(pointer_size);
if (code_pointer == nullptr) {
@@ -380,8 +366,7 @@
}
inline const DexFile::CodeItem* ArtMethod::GetCodeItem() {
- mirror::ArtMethod* method = GetInterfaceMethodIfProxy();
- return method->GetDexFile()->GetCodeItem(method->GetCodeItemOffset());
+ return GetDeclaringClass()->GetDexFile().GetCodeItem(GetCodeItemOffset());
}
inline bool ArtMethod::IsResolvedTypeIdx(uint16_t type_idx) {
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index 1729686..b2016dc 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -39,10 +39,9 @@
namespace art {
namespace mirror {
-extern "C" void art_portable_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*, char);
extern "C" void art_quick_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
const char*);
-#if defined(__LP64__) || defined(__arm__)
+#if defined(__LP64__) || defined(__arm__) || defined(__i386__)
extern "C" void art_quick_invoke_static_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
const char*);
#endif
@@ -61,9 +60,7 @@
void ArtMethod::VisitRoots(RootCallback* callback, void* arg) {
- if (!java_lang_reflect_ArtMethod_.IsNull()) {
- java_lang_reflect_ArtMethod_.VisitRoot(callback, arg, 0, kRootStickyClass);
- }
+ java_lang_reflect_ArtMethod_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
}
mirror::String* ArtMethod::GetNameAsString(Thread* self) {
@@ -200,11 +197,13 @@
}
uint32_t ArtMethod::ToDexPc(const uintptr_t pc, bool abort_on_failure) {
- if (IsPortableCompiled()) {
- // Portable doesn't use the machine pc, we just use dex pc instead.
- return static_cast<uint32_t>(pc);
- }
const void* entry_point = GetQuickOatEntryPoint(sizeof(void*));
+ uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(entry_point);
+ if (IsOptimized(sizeof(void*))) {
+ uint32_t ret = GetStackMap(sought_offset).GetDexPc();
+ return ret;
+ }
+
MappingTable table(entry_point != nullptr ?
GetMappingTable(EntryPointToCodePointer(entry_point), sizeof(void*)) : nullptr);
if (table.TotalSize() == 0) {
@@ -213,7 +212,6 @@
DCHECK(IsNative() || IsCalleeSaveMethod() || IsProxyMethod()) << PrettyMethod(this);
return DexFile::kDexNoIndex; // Special no mapping case
}
- uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(entry_point);
// Assume the caller wants a pc-to-dex mapping so check here first.
typedef MappingTable::PcToDexIterator It;
for (It cur = table.PcToDexBegin(), end = table.PcToDexEnd(); cur != end; ++cur) {
@@ -347,19 +345,12 @@
bool ArtMethod::IsEntrypointInterpreter() {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (!IsPortableCompiled()) { // Quick.
- const void* oat_quick_code = class_linker->GetOatMethodQuickCodeFor(this);
- return oat_quick_code == nullptr ||
- oat_quick_code != GetEntryPointFromQuickCompiledCode();
- } else { // Portable.
- const void* oat_portable_code = class_linker->GetOatMethodPortableCodeFor(this);
- return oat_portable_code == nullptr ||
- oat_portable_code != GetEntryPointFromPortableCompiledCode();
- }
+ const void* oat_quick_code = class_linker->GetOatMethodQuickCodeFor(this);
+ return oat_quick_code == nullptr || oat_quick_code != GetEntryPointFromQuickCompiledCode();
}
const void* ArtMethod::GetQuickOatEntryPoint(size_t pointer_size) {
- if (IsPortableCompiled() || IsAbstract() || IsRuntimeMethod() || IsProxyMethod()) {
+ if (IsAbstract() || IsRuntimeMethod() || IsProxyMethod()) {
return nullptr;
}
Runtime* runtime = Runtime::Current();
@@ -412,34 +403,27 @@
} else {
const bool kLogInvocationStartAndReturn = false;
bool have_quick_code = GetEntryPointFromQuickCompiledCode() != nullptr;
- bool have_portable_code = GetEntryPointFromPortableCompiledCode() != nullptr;
- if (LIKELY(have_quick_code || have_portable_code)) {
+ if (LIKELY(have_quick_code)) {
if (kLogInvocationStartAndReturn) {
- LOG(INFO) << StringPrintf("Invoking '%s' %s code=%p", PrettyMethod(this).c_str(),
- have_quick_code ? "quick" : "portable",
- have_quick_code ? GetEntryPointFromQuickCompiledCode()
- : GetEntryPointFromPortableCompiledCode());
+ LOG(INFO) << StringPrintf("Invoking '%s' quick code=%p", PrettyMethod(this).c_str(),
+ GetEntryPointFromQuickCompiledCode());
}
- // Ensure that we won't be accidentally calling quick/portable compiled code when -Xint.
+ // Ensure that we won't be accidentally calling quick compiled code when -Xint.
if (kIsDebugBuild && Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly()) {
CHECK(IsEntrypointInterpreter())
<< "Don't call compiled code when -Xint " << PrettyMethod(this);
}
- if (!IsPortableCompiled()) {
-#if defined(__LP64__) || defined(__arm__)
- if (!IsStatic()) {
- (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
- } else {
- (*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty);
- }
-#else
+#if defined(__LP64__) || defined(__arm__) || defined(__i386__)
+ if (!IsStatic()) {
(*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
-#endif
} else {
- (*art_portable_invoke_stub)(this, args, args_size, self, result, shorty[0]);
+ (*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty);
}
+#else
+ (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
+#endif
if (UNLIKELY(self->GetException(nullptr) == Thread::GetDeoptimizationException())) {
// Unusual case where we were running generated code and an
// exception was thrown to force the activations to be removed from the
@@ -451,10 +435,8 @@
interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, result);
}
if (kLogInvocationStartAndReturn) {
- LOG(INFO) << StringPrintf("Returned '%s' %s code=%p", PrettyMethod(this).c_str(),
- have_quick_code ? "quick" : "portable",
- have_quick_code ? GetEntryPointFromQuickCompiledCode()
- : GetEntryPointFromPortableCompiledCode());
+ LOG(INFO) << StringPrintf("Returned '%s' quick code=%p", PrettyMethod(this).c_str(),
+ GetEntryPointFromQuickCompiledCode());
}
} else {
LOG(INFO) << "Not invoking '" << PrettyMethod(this) << "' code=null";
@@ -484,10 +466,6 @@
}
QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo() {
- if (UNLIKELY(IsPortableCompiled())) {
- // Portable compiled dex bytecode or jni stub.
- return QuickMethodFrameInfo(kStackAlignment, 0u, 0u);
- }
Runtime* runtime = Runtime::Current();
if (UNLIKELY(IsAbstract())) {
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index 2107944..f33ca94 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -20,11 +20,13 @@
#include "dex_file.h"
#include "gc_root.h"
#include "invoke_type.h"
+#include "method_reference.h"
#include "modifiers.h"
#include "object.h"
#include "object_callbacks.h"
#include "quick/quick_method_frame_info.h"
#include "read_barrier_option.h"
+#include "stack.h"
#include "stack_map.h"
namespace art {
@@ -151,25 +153,12 @@
// Temporary solution for detecting if a method has been optimized: the compiler
// does not create a GC map. Instead, the vmap table contains the stack map
// (as in stack_map.h).
- return GetEntryPointFromQuickCompiledCodePtrSize(pointer_size) != nullptr
+ return !IsNative()
+ && GetEntryPointFromQuickCompiledCodePtrSize(pointer_size) != nullptr
&& GetQuickOatCodePointer(pointer_size) != nullptr
&& GetNativeGcMap(pointer_size) == nullptr;
}
- bool IsPortableCompiled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return (GetAccessFlags() & kAccPortableCompiled) != 0;
- }
-
- void SetIsPortableCompiled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(!IsPortableCompiled());
- SetAccessFlags(GetAccessFlags() | kAccPortableCompiled);
- }
-
- void ClearIsPortableCompiled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(IsPortableCompiled());
- SetAccessFlags(GetAccessFlags() & ~kAccPortableCompiled);
- }
-
bool CheckIncompatibleClassChange(InvokeType type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
uint16_t GetMethodIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -282,42 +271,6 @@
EntryPointFromInterpreterOffset(pointer_size), entry_point_from_interpreter, pointer_size);
}
- ALWAYS_INLINE static MemberOffset EntryPointFromPortableCompiledCodeOffset(size_t pointer_size) {
- return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
- PtrSizedFields, entry_point_from_portable_compiled_code_) / sizeof(void*) * pointer_size);
- }
-
- template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- const void* GetEntryPointFromPortableCompiledCode()
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- CheckObjectSizeEqualsMirrorSize();
- return GetEntryPointFromPortableCompiledCodePtrSize(sizeof(void*));
- }
-
- template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- ALWAYS_INLINE const void* GetEntryPointFromPortableCompiledCodePtrSize(size_t pointer_size)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetFieldPtrWithSize<const void*, kVerifyFlags>(
- EntryPointFromPortableCompiledCodeOffset(pointer_size), pointer_size);
- }
-
- template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- void SetEntryPointFromPortableCompiledCode(const void* entry_point_from_portable_compiled_code)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- CheckObjectSizeEqualsMirrorSize();
- return SetEntryPointFromPortableCompiledCodePtrSize(entry_point_from_portable_compiled_code,
- sizeof(void*));
- }
-
- template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- void SetEntryPointFromPortableCompiledCodePtrSize(
- const void* entry_point_from_portable_compiled_code, size_t pointer_size)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- SetFieldPtrWithSize<false, true, kVerifyFlags>(
- EntryPointFromPortableCompiledCodeOffset(pointer_size),
- entry_point_from_portable_compiled_code, pointer_size);
- }
-
template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
const void* GetEntryPointFromQuickCompiledCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
CheckObjectSizeEqualsMirrorSize();
@@ -374,9 +327,7 @@
bool IsEntrypointInterpreter() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
uint32_t GetQuickOatCodeOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- uint32_t GetPortableOatCodeOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetQuickOatCodeOffset(uint32_t code_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SetPortableOatCodeOffset(uint32_t code_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
ALWAYS_INLINE static const void* EntryPointToCodePointer(const void* entry_point) {
uintptr_t code = reinterpret_cast<uintptr_t>(entry_point);
@@ -440,8 +391,9 @@
}
FrameOffset GetHandleScopeOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK_LT(sizeof(void*), GetFrameSizeInBytes());
- return FrameOffset(sizeof(void*));
+ constexpr size_t handle_scope_offset = sizeof(StackReference<mirror::ArtMethod>);
+ DCHECK_LT(handle_scope_offset, GetFrameSizeInBytes());
+ return FrameOffset(handle_scope_offset);
}
void RegisterNative(const void* native_method, bool is_fast)
@@ -521,6 +473,10 @@
uintptr_t ToNativeQuickPc(const uint32_t dex_pc, bool abort_on_failure = true)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ MethodReference ToMethodReference() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return MethodReference(GetDexFile(), GetDexMethodIndex());
+ }
+
// Find the catch block for the given exception type and dex_pc. When a catch block is found,
// indicates whether the found catch block is responsible for clearing the exception or whether
// a move-exception instruction is present.
@@ -642,12 +598,8 @@
void* entry_point_from_jni_;
// Method dispatch from quick compiled code invokes this pointer which may cause bridging into
- // portable compiled code or the interpreter.
+ // the interpreter.
void* entry_point_from_quick_compiled_code_;
-
- // Method dispatch from portable compiled code invokes this pointer which may cause bridging
- // into quick compiled code or the interpreter. Last to simplify entrypoint logic.
- void* entry_point_from_portable_compiled_code_;
} ptr_sized_fields_;
static GcRoot<Class> java_lang_reflect_ArtMethod_;
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 1662ebf..495f753 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -514,7 +514,15 @@
IsErroneous<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>() ||
this == String::GetJavaLangString() ||
this == ArtField::GetJavaLangReflectArtField() ||
- this == ArtMethod::GetJavaLangReflectArtMethod());
+ this == ArtMethod::GetJavaLangReflectArtMethod())
+ << "IsIdxLoaded=" << IsIdxLoaded<kVerifyFlags>()
+ << " IsRetired=" << IsRetired<kVerifyFlags>()
+ << " IsErroneous=" <<
+ IsErroneous<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>()
+ << " IsString=" << (this == String::GetJavaLangString())
+ << " IsArtField=" << (this == ArtField::GetJavaLangReflectArtField())
+ << " IsArtMethod=" << (this == ArtMethod::GetJavaLangReflectArtMethod())
+ << " descriptor=" << PrettyDescriptor(this);
return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
}
@@ -642,7 +650,14 @@
template <bool kVisitClass, typename Visitor>
inline void Class::VisitReferences(mirror::Class* klass, const Visitor& visitor) {
VisitInstanceFieldsReferences<kVisitClass>(klass, visitor);
- if (!IsTemp() && IsResolved()) {
+ // Right after a class is allocated, but not yet loaded
+ // (kStatusNotReady, see ClassLinkder::LoadClass()), GC may find it
+ // and scan it. IsTemp() may call Class::GetAccessFlags() but may
+ // fail in the DCHECK in Class::GetAccessFlags() because the class
+ // status is kStatusNotReady. To avoid it, rely on IsResolved()
+ // only. This is fine because a temp class never goes into the
+ // kStatusResolved state.
+ if (IsResolved()) {
// Temp classes don't ever populate imt/vtable or static fields and they are not even
// allocated with the right size for those. Also, unresolved classes don't have fields
// linked yet.
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index bd3bfbf..ae684b1 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -52,9 +52,7 @@
}
void Class::VisitRoots(RootCallback* callback, void* arg) {
- if (!java_lang_Class_.IsNull()) {
- java_lang_Class_.VisitRoot(callback, arg, 0, kRootStickyClass);
- }
+ java_lang_Class_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
}
void Class::SetStatus(Status new_status, Thread* self) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 812cfd3..bd49754 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1048,6 +1048,11 @@
DISALLOW_COPY_AND_ASSIGN(InitializeClassVisitor);
};
+ // Returns true if the class loader is null, ie the class loader is the boot strap class loader.
+ bool IsBootStrapClassLoaded() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return GetClassLoader() == nullptr;
+ }
+
private:
void SetVerifyErrorClass(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index ef6fc67..53e5534 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -34,6 +34,7 @@
TEST_F(DexCacheTest, Open) {
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<1> hs(soa.Self());
+ ASSERT_TRUE(java_lang_dex_file_ != NULL);
Handle<DexCache> dex_cache(
hs.NewHandle(class_linker_->AllocDexCache(soa.Self(), *java_lang_dex_file_)));
ASSERT_TRUE(dex_cache.Get() != NULL);
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 121947d..d690163 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -154,7 +154,6 @@
}
} while (!atomic_rb_ptr->CompareExchangeWeakSequentiallyConsistent(expected_ref.reference_,
new_ref.reference_));
- DCHECK_EQ(new_ref.reference_, atomic_rb_ptr->LoadRelaxed());
return true;
#else
UNUSED(expected_rb_ptr, rb_ptr);
@@ -826,6 +825,17 @@
template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
inline bool Object::CasFieldWeakSequentiallyConsistentObject(MemberOffset field_offset,
Object* old_value, Object* new_value) {
+ bool success = CasFieldWeakSequentiallyConsistentObjectWithoutWriteBarrier<
+ kTransactionActive, kCheckTransaction, kVerifyFlags>(field_offset, old_value, new_value);
+ if (success) {
+ Runtime::Current()->GetHeap()->WriteBarrierField(this, field_offset, new_value);
+ }
+ return success;
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline bool Object::CasFieldWeakSequentiallyConsistentObjectWithoutWriteBarrier(
+ MemberOffset field_offset, Object* old_value, Object* new_value) {
if (kCheckTransaction) {
DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
}
@@ -848,7 +858,14 @@
bool success = atomic_addr->CompareExchangeWeakSequentiallyConsistent(old_ref.reference_,
new_ref.reference_);
+ return success;
+}
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline bool Object::CasFieldStrongSequentiallyConsistentObject(MemberOffset field_offset,
+ Object* old_value, Object* new_value) {
+ bool success = CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier<
+ kTransactionActive, kCheckTransaction, kVerifyFlags>(field_offset, old_value, new_value);
if (success) {
Runtime::Current()->GetHeap()->WriteBarrierField(this, field_offset, new_value);
}
@@ -856,8 +873,8 @@
}
template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
-inline bool Object::CasFieldStrongSequentiallyConsistentObject(MemberOffset field_offset,
- Object* old_value, Object* new_value) {
+inline bool Object::CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier(
+ MemberOffset field_offset, Object* old_value, Object* new_value) {
if (kCheckTransaction) {
DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
}
@@ -880,10 +897,6 @@
bool success = atomic_addr->CompareExchangeStrongSequentiallyConsistent(old_ref.reference_,
new_ref.reference_);
-
- if (success) {
- Runtime::Current()->GetHeap()->WriteBarrierField(this, field_offset, new_value);
- }
return success;
}
diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc
index 65d6ade..9262a3e 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -24,6 +24,7 @@
#include "class.h"
#include "class-inl.h"
#include "class_linker-inl.h"
+#include "dex_file-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/heap.h"
#include "iftable-inl.h"
@@ -38,6 +39,8 @@
namespace art {
namespace mirror {
+Atomic<uint32_t> Object::hash_code_seed(987654321U + std::time(nullptr));
+
class CopyReferenceFieldsWithReadBarrierVisitor {
public:
explicit CopyReferenceFieldsWithReadBarrierVisitor(Object* dest_obj)
@@ -72,7 +75,7 @@
uint8_t* dst_bytes = reinterpret_cast<uint8_t*>(dest);
size_t offset = sizeof(Object);
memcpy(dst_bytes + offset, src_bytes + offset, num_bytes - offset);
- if (kUseBakerOrBrooksReadBarrier) {
+ if (kUseReadBarrier) {
// We need a RB here. After the memcpy that covers the whole
// object above, copy references fields one by one again with a
// RB. TODO: Optimize this later?
@@ -135,16 +138,19 @@
}
uint32_t Object::GenerateIdentityHashCode() {
- static Atomic<uint32_t> seed(987654321U + std::time(nullptr));
uint32_t expected_value, new_value;
do {
- expected_value = seed.LoadRelaxed();
+ expected_value = hash_code_seed.LoadRelaxed();
new_value = expected_value * 1103515245 + 12345;
- } while ((expected_value & LockWord::kHashMask) == 0 ||
- !seed.CompareExchangeWeakRelaxed(expected_value, new_value));
+ } while (!hash_code_seed.CompareExchangeWeakRelaxed(expected_value, new_value) ||
+ (expected_value & LockWord::kHashMask) == 0);
return expected_value & LockWord::kHashMask;
}
+void Object::SetHashCodeSeed(uint32_t new_seed) {
+ hash_code_seed.StoreRelaxed(new_seed);
+}
+
int32_t Object::IdentityHashCode() const {
mirror::Object* current_this = const_cast<mirror::Object*>(this);
while (true) {
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 221feca..780c5ae 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -240,12 +240,24 @@
bool CasFieldWeakSequentiallyConsistentObject(MemberOffset field_offset, Object* old_value,
Object* new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool CasFieldWeakSequentiallyConsistentObjectWithoutWriteBarrier(MemberOffset field_offset,
+ Object* old_value,
+ Object* new_value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<bool kTransactionActive, bool kCheckTransaction = true,
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool CasFieldStrongSequentiallyConsistentObject(MemberOffset field_offset, Object* old_value,
Object* new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier(MemberOffset field_offset,
+ Object* old_value,
+ Object* new_value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
HeapReference<Object>* GetFieldObjectReferenceAddr(MemberOffset field_offset);
@@ -419,6 +431,11 @@
void VisitReferences(const Visitor& visitor, const JavaLangRefVisitor& ref_visitor)
NO_THREAD_SAFETY_ANALYSIS;
+ // Used by object_test.
+ static void SetHashCodeSeed(uint32_t new_seed);
+ // Generate an identity hash code. Public for object test.
+ static uint32_t GenerateIdentityHashCode();
+
protected:
// Accessors for non-Java type fields
template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
@@ -471,9 +488,6 @@
}
}
- // Generate an identity hash code.
- static uint32_t GenerateIdentityHashCode();
-
// A utility function that copies an object in a read barrier and
// write barrier-aware way. This is internally used by Clone() and
// Class::CopyOf().
@@ -481,6 +495,8 @@
size_t num_bytes)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static Atomic<uint32_t> hash_code_seed;
+
// The Class representing the type of the object.
HeapReference<Class> klass_;
// Monitor and hash code information.
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index fbc4f4a..96d426b 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -131,7 +131,7 @@
CHECK_EQ(sizeof(HeapReference<T>), sizeof(uint32_t));
IntArray* dstAsIntArray = reinterpret_cast<IntArray*>(this);
IntArray* srcAsIntArray = reinterpret_cast<IntArray*>(src);
- if (kUseBakerOrBrooksReadBarrier) {
+ if (kUseReadBarrier) {
// TODO: Optimize this later?
const bool copy_forward = (src != this) || (dst_pos < src_pos) || (dst_pos - src_pos >= count);
if (copy_forward) {
@@ -174,7 +174,7 @@
CHECK_EQ(sizeof(HeapReference<T>), sizeof(uint32_t));
IntArray* dstAsIntArray = reinterpret_cast<IntArray*>(this);
IntArray* srcAsIntArray = reinterpret_cast<IntArray*>(src);
- if (kUseBakerOrBrooksReadBarrier) {
+ if (kUseReadBarrier) {
// TODO: Optimize this later?
for (int i = 0; i < count; ++i) {
// We need a RB here. ObjectArray::GetWithoutChecks() contains a RB.
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index 9d789cd..fb42d28 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -77,10 +77,6 @@
TEST_F(ObjectTest, Constants) {
EXPECT_EQ(kObjectReferenceSize, sizeof(HeapReference<Object>));
EXPECT_EQ(kObjectHeaderSize, sizeof(Object));
- EXPECT_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32,
- ArtMethod::EntryPointFromPortableCompiledCodeOffset(4).Int32Value());
- EXPECT_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_64,
- ArtMethod::EntryPointFromPortableCompiledCodeOffset(8).Int32Value());
EXPECT_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32,
ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value());
EXPECT_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64,
@@ -317,7 +313,7 @@
java_lang_dex_file_->GetIndexForStringId(*string_id));
ASSERT_TRUE(type_id != NULL);
uint32_t type_idx = java_lang_dex_file_->GetIndexForTypeId(*type_id);
- Object* array = CheckAndAllocArrayFromCodeInstrumented(type_idx, sort, 3, Thread::Current(), false,
+ Object* array = CheckAndAllocArrayFromCodeInstrumented(type_idx, 3, sort, Thread::Current(), false,
Runtime::Current()->GetHeap()->GetCurrentAllocator());
EXPECT_TRUE(array->IsArrayInstance());
EXPECT_EQ(3, array->AsArray()->GetLength());
@@ -735,5 +731,14 @@
// TODO: test that interfaces trump superclasses.
}
+TEST_F(ObjectTest, IdentityHashCode) {
+ // Regression test for b/19046417 which had an infinite loop if the
+ // (seed & LockWord::kHashMask) == 0. seed 0 triggered the infinite loop since we did the check
+ // before the CAS which resulted in the same seed the next loop iteration.
+ mirror::Object::SetHashCodeSeed(0);
+ int32_t hash_code = mirror::Object::GenerateIdentityHashCode();
+ EXPECT_NE(hash_code, 0);
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/reference.cc b/runtime/mirror/reference.cc
index c36bd98..35130e8 100644
--- a/runtime/mirror/reference.cc
+++ b/runtime/mirror/reference.cc
@@ -33,9 +33,7 @@
}
void Reference::VisitRoots(RootCallback* callback, void* arg) {
- if (!java_lang_ref_Reference_.IsNull()) {
- java_lang_ref_Reference_.VisitRoot(callback, arg, 0, kRootStickyClass);
- }
+ java_lang_ref_Reference_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
}
} // namespace mirror
diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc
index 1eb20f7..c2a67e8 100644
--- a/runtime/mirror/stack_trace_element.cc
+++ b/runtime/mirror/stack_trace_element.cc
@@ -68,9 +68,7 @@
}
void StackTraceElement::VisitRoots(RootCallback* callback, void* arg) {
- if (!java_lang_StackTraceElement_.IsNull()) {
- java_lang_StackTraceElement_.VisitRoot(callback, arg, 0, kRootStickyClass);
- }
+ java_lang_StackTraceElement_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
}
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index 01599ae..e199d0e 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -224,9 +224,7 @@
}
void String::VisitRoots(RootCallback* callback, void* arg) {
- if (!java_lang_String_.IsNull()) {
- java_lang_String_.VisitRoot(callback, arg, 0, kRootStickyClass);
- }
+ java_lang_String_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
}
} // namespace mirror
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index 93ed4d4..61d85e2 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -138,9 +138,7 @@
}
void Throwable::VisitRoots(RootCallback* callback, void* arg) {
- if (!java_lang_Throwable_.IsNull()) {
- java_lang_Throwable_.VisitRoot(callback, arg, 0, kRootStickyClass);
- }
+ java_lang_Throwable_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
}
} // namespace mirror
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index 23c18f8..09dc78a 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -46,7 +46,6 @@
static constexpr uint32_t kAccPreverified = 0x00080000; // class (runtime),
// method (dex only)
static constexpr uint32_t kAccFastNative = 0x00080000; // method (dex only)
-static constexpr uint32_t kAccPortableCompiled = 0x00100000; // method (dex only)
static constexpr uint32_t kAccMiranda = 0x00200000; // method (dex only)
// Special runtime-only flags.
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 233267b..5ed8c7d 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -16,6 +16,9 @@
#include "monitor.h"
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+
+#include <cutils/trace.h>
#include <vector>
#include "base/mutex.h"
@@ -251,7 +254,12 @@
{
ScopedThreadStateChange tsc(self, kBlocked); // Change to blocked and give up mutator_lock_.
MutexLock mu2(self, monitor_lock_); // Reacquire monitor_lock_ without mutator_lock_ for Wait.
- if (owner_ != NULL) { // Did the owner_ give the lock up?
+ if (owner_ != nullptr) { // Did the owner_ give the lock up?
+ if (ATRACE_ENABLED()) {
+ std::string name;
+ owner_->GetThreadName(name);
+ ATRACE_BEGIN(("Contended on monitor with owner " + name).c_str());
+ }
monitor_contenders_.Wait(self); // Still contended so wait.
// Woken from contention.
if (log_contention) {
@@ -275,6 +283,7 @@
LogContentionEvent(self, wait_ms, sample_percent, owners_filename, owners_line_number);
}
}
+ ATRACE_END();
}
}
self->SetMonitorEnterObject(nullptr);
@@ -924,8 +933,11 @@
PrettyTypeOf(pretty_object).c_str());
} else {
// - waiting on <0x6008c468> (a java.lang.Class<java.lang.ref.ReferenceQueue>)
+ // Call PrettyTypeOf before IdentityHashCode since IdentityHashCode can cause thread
+ // suspension and move pretty_object.
+ const std::string pretty_type(PrettyTypeOf(pretty_object));
os << wait_message << StringPrintf("<0x%08x> (a %s)", pretty_object->IdentityHashCode(),
- PrettyTypeOf(pretty_object).c_str());
+ pretty_type.c_str());
}
}
// - waiting to lock <0x613f83d8> (a java.lang.Object) held by thread 5
@@ -992,14 +1004,9 @@
// the locks held in this stack frame.
std::vector<uint32_t> monitor_enter_dex_pcs;
verifier::MethodVerifier::FindLocksAtDexPc(m, dex_pc, &monitor_enter_dex_pcs);
- if (monitor_enter_dex_pcs.empty()) {
- return;
- }
-
- for (size_t i = 0; i < monitor_enter_dex_pcs.size(); ++i) {
+ for (uint32_t monitor_dex_pc : monitor_enter_dex_pcs) {
// The verifier works in terms of the dex pcs of the monitor-enter instructions.
// We want the registers used by those instructions (so we can read the values out of them).
- uint32_t monitor_dex_pc = monitor_enter_dex_pcs[i];
uint16_t monitor_enter_instruction = code_item->insns_[monitor_dex_pc];
// Quick sanity check.
@@ -1009,8 +1016,8 @@
}
uint16_t monitor_register = ((monitor_enter_instruction >> 8) & 0xff);
- mirror::Object* o = reinterpret_cast<mirror::Object*>(stack_visitor->GetVReg(m, monitor_register,
- kReferenceVReg));
+ mirror::Object* o = reinterpret_cast<mirror::Object*>(
+ stack_visitor->GetVReg(m, monitor_register, kReferenceVReg));
callback(o, callback_context);
}
}
@@ -1099,6 +1106,13 @@
monitor_add_condition_.Broadcast(self);
}
+void MonitorList::EnsureNewMonitorsDisallowed() {
+ // Lock and unlock once to ensure that no threads are still in the
+ // middle of adding new monitors.
+ MutexLock mu(Thread::Current(), monitor_list_lock_);
+ CHECK(!allow_new_monitors_);
+}
+
void MonitorList::Add(Monitor* m) {
Thread* self = Thread::Current();
MutexLock mu(self, monitor_list_lock_);
diff --git a/runtime/monitor.h b/runtime/monitor.h
index 8f97a40..0c5f8a4 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -266,12 +266,13 @@
MonitorList();
~MonitorList();
- void Add(Monitor* m);
+ void Add(Monitor* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SweepMonitorList(IsMarkedCallback* callback, void* arg)
LOCKS_EXCLUDED(monitor_list_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void DisallowNewMonitors() LOCKS_EXCLUDED(monitor_list_lock_);
void AllowNewMonitors() LOCKS_EXCLUDED(monitor_list_lock_);
+ void EnsureNewMonitorsDisallowed() LOCKS_EXCLUDED(monitor_list_lock_);
// Returns how many monitors were deflated.
size_t DeflateMonitors() LOCKS_EXCLUDED(monitor_list_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc
index adc7848..6d1e721 100644
--- a/runtime/monitor_test.cc
+++ b/runtime/monitor_test.cc
@@ -356,6 +356,8 @@
TEST_F(MonitorTest, CheckExceptionsWait1) {
// Make the CreateTask wait 10ms, the UseTask wait 10ms.
// => The use task will get the lock first and get to self == owner check.
+ // This will lead to OOM and monitor error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
CommonWaitSetup(this, class_linker_, 10, 50, false, false, 2, 50, true,
"Monitor test thread pool 1");
}
@@ -364,6 +366,8 @@
TEST_F(MonitorTest, CheckExceptionsWait2) {
// Make the CreateTask wait 0ms, the UseTask wait 10ms.
// => The create task will get the lock first and get to ms >= 0
+ // This will lead to OOM and monitor error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
CommonWaitSetup(this, class_linker_, 0, -1, true, false, 10, 50, true,
"Monitor test thread pool 2");
}
@@ -373,6 +377,8 @@
// Make the CreateTask wait 0ms, then Wait for a long time. Make the InterruptTask wait 10ms,
// after which it will interrupt the create task and then wait another 10ms.
// => The create task will get to the interrupted-exception throw.
+ // This will lead to OOM and monitor error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
CommonWaitSetup(this, class_linker_, 0, 500, true, true, 10, 50, true,
"Monitor test thread pool 3");
}
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index f37312e..037072d 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -115,7 +115,8 @@
}
ClassLinker* linker = Runtime::Current()->GetClassLinker();
- std::unique_ptr<std::vector<const DexFile*>> dex_files(new std::vector<const DexFile*>());
+ std::unique_ptr<std::vector<std::unique_ptr<const DexFile>>> dex_files(
+ new std::vector<std::unique_ptr<const DexFile>>());
std::vector<std::string> error_msgs;
bool success = linker->OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), &error_msgs,
@@ -143,9 +144,11 @@
}
}
-static std::vector<const DexFile*>* toDexFiles(jlong dex_file_address, JNIEnv* env) {
- std::vector<const DexFile*>* dex_files = reinterpret_cast<std::vector<const DexFile*>*>(
- static_cast<uintptr_t>(dex_file_address));
+static std::vector<std::unique_ptr<const DexFile>>*
+toDexFiles(jlong dex_file_address, JNIEnv* env) {
+ std::vector<std::unique_ptr<const DexFile>>* dex_files
+ = reinterpret_cast<std::vector<std::unique_ptr<const DexFile>>*>(
+ static_cast<uintptr_t>(dex_file_address));
if (UNLIKELY(dex_files == nullptr)) {
ScopedObjectAccess soa(env);
ThrowNullPointerException(NULL, "dex_file == null");
@@ -154,27 +157,29 @@
}
static void DexFile_closeDexFile(JNIEnv* env, jclass, jlong cookie) {
- std::unique_ptr<std::vector<const DexFile*>> dex_files(toDexFiles(cookie, env));
+ std::unique_ptr<std::vector<std::unique_ptr<const DexFile>>> dex_files(toDexFiles(cookie, env));
if (dex_files.get() == nullptr) {
return;
}
ScopedObjectAccess soa(env);
- size_t index = 0;
- for (const DexFile* dex_file : *dex_files) {
+ // The Runtime currently never unloads classes, which means any registered
+ // dex files must be kept around forever in case they are used. We
+ // accomplish this here by explicitly leaking those dex files that are
+ // registered.
+ //
+ // TODO: The Runtime should support unloading of classes and freeing of the
+ // dex files for those unloaded classes rather than leaking dex files here.
+ for (auto& dex_file : *dex_files) {
if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) {
- (*dex_files)[index] = nullptr;
+ dex_file.release();
}
- index++;
}
-
- STLDeleteElements(dex_files.get());
- // Unique_ptr will delete the vector itself.
}
static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader,
jlong cookie) {
- std::vector<const DexFile*>* dex_files = toDexFiles(cookie, env);
+ std::vector<std::unique_ptr<const DexFile>>* dex_files = toDexFiles(cookie, env);
if (dex_files == NULL) {
VLOG(class_linker) << "Failed to find dex_file";
return NULL;
@@ -186,7 +191,7 @@
}
const std::string descriptor(DotToDescriptor(class_name.c_str()));
const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));
- for (const DexFile* dex_file : *dex_files) {
+ for (auto& dex_file : *dex_files) {
const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str(), hash);
if (dex_class_def != nullptr) {
ScopedObjectAccess soa(env);
@@ -218,13 +223,13 @@
// Note: this can be an expensive call, as we sort out duplicates in MultiDex files.
static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jlong cookie) {
jobjectArray result = nullptr;
- std::vector<const DexFile*>* dex_files = toDexFiles(cookie, env);
+ std::vector<std::unique_ptr<const DexFile>>* dex_files = toDexFiles(cookie, env);
if (dex_files != nullptr) {
// Push all class descriptors into a set. Use set instead of unordered_set as we want to
// retrieve all in the end.
std::set<const char*, CharPointerComparator> descriptors;
- for (const DexFile* dex_file : *dex_files) {
+ for (auto& dex_file : *dex_files) {
for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
const char* descriptor = dex_file->GetClassDescriptor(class_def);
@@ -301,7 +306,10 @@
nullptr,
false, &error_msg));
if (oat_file.get() == nullptr) {
- if (kReasonLogging) {
+ // Note that even though this is kDexoptNeeded, we use
+ // kVerboseLogging instead of the usual kReasonLogging since it is
+ // the common case on first boot and very spammy.
+ if (kVerboseLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded failed to open oat file '" << oat_filename
<< "' for file location '" << filename << "': " << error_msg;
}
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index d40d64b..599d97f 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -34,6 +34,7 @@
#include "gc/heap.h"
#include "gc/space/dlmalloc_space.h"
#include "gc/space/image_space.h"
+#include "gc/task_processor.h"
#include "intern_table.h"
#include "jni_internal.h"
#include "mirror/art_method-inl.h"
@@ -133,6 +134,10 @@
Runtime::Current()->GetHeap()->ClearGrowthLimit();
}
+static void VMRuntime_clampGrowthLimit(JNIEnv*, jobject) {
+ Runtime::Current()->GetHeap()->ClampGrowthLimit();
+}
+
static jboolean VMRuntime_isDebuggerActive(JNIEnv*, jobject) {
return Dbg::IsDebuggerActive();
}
@@ -213,18 +218,38 @@
runtime->UpdateProfilerState(process_state);
}
-static void VMRuntime_trimHeap(JNIEnv*, jobject) {
- Runtime::Current()->GetHeap()->DoPendingTransitionOrTrim();
+static void VMRuntime_trimHeap(JNIEnv* env, jobject) {
+ Runtime::Current()->GetHeap()->Trim(ThreadForEnv(env));
}
static void VMRuntime_concurrentGC(JNIEnv* env, jobject) {
Runtime::Current()->GetHeap()->ConcurrentGC(ThreadForEnv(env));
}
+static void VMRuntime_requestHeapTrim(JNIEnv* env, jobject) {
+ Runtime::Current()->GetHeap()->RequestTrim(ThreadForEnv(env));
+}
+
+static void VMRuntime_requestConcurrentGC(JNIEnv* env, jobject) {
+ Runtime::Current()->GetHeap()->RequestConcurrentGC(ThreadForEnv(env));
+}
+
+static void VMRuntime_startHeapTaskProcessor(JNIEnv* env, jobject) {
+ Runtime::Current()->GetHeap()->GetTaskProcessor()->Start(ThreadForEnv(env));
+}
+
+static void VMRuntime_stopHeapTaskProcessor(JNIEnv* env, jobject) {
+ Runtime::Current()->GetHeap()->GetTaskProcessor()->Stop(ThreadForEnv(env));
+}
+
+static void VMRuntime_runHeapTasks(JNIEnv* env, jobject) {
+ Runtime::Current()->GetHeap()->GetTaskProcessor()->RunAllTasks(ThreadForEnv(env));
+}
+
typedef std::map<std::string, mirror::String*> StringTable;
static void PreloadDexCachesStringsCallback(mirror::Object** root, void* arg,
- uint32_t /*thread_id*/, RootType /*root_type*/)
+ const RootInfo& /*root_info*/)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
StringTable& table = *reinterpret_cast<StringTable*>(arg);
mirror::String* string = const_cast<mirror::Object*>(*root)->AsString();
@@ -556,6 +581,7 @@
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMRuntime, addressOf, "!(Ljava/lang/Object;)J"),
NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"),
+ NATIVE_METHOD(VMRuntime, clampGrowthLimit, "()V"),
NATIVE_METHOD(VMRuntime, classPath, "()Ljava/lang/String;"),
NATIVE_METHOD(VMRuntime, clearGrowthLimit, "()V"),
NATIVE_METHOD(VMRuntime, concurrentGC, "()V"),
@@ -569,8 +595,13 @@
NATIVE_METHOD(VMRuntime, setTargetSdkVersionNative, "(I)V"),
NATIVE_METHOD(VMRuntime, registerNativeAllocation, "(I)V"),
NATIVE_METHOD(VMRuntime, registerNativeFree, "(I)V"),
+ NATIVE_METHOD(VMRuntime, requestConcurrentGC, "()V"),
+ NATIVE_METHOD(VMRuntime, requestHeapTrim, "()V"),
+ NATIVE_METHOD(VMRuntime, runHeapTasks, "()V"),
NATIVE_METHOD(VMRuntime, updateProcessState, "(I)V"),
+ NATIVE_METHOD(VMRuntime, startHeapTaskProcessor, "()V"),
NATIVE_METHOD(VMRuntime, startJitCompilation, "()V"),
+ NATIVE_METHOD(VMRuntime, stopHeapTaskProcessor, "()V"),
NATIVE_METHOD(VMRuntime, trimHeap, "()V"),
NATIVE_METHOD(VMRuntime, vmVersion, "()Ljava/lang/String;"),
NATIVE_METHOD(VMRuntime, vmLibrary, "()Ljava/lang/String;"),
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index f1a04cb..c056adc 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -26,7 +26,7 @@
#include "ScopedUtfChars.h"
#include "thread-inl.h"
-#if defined(HAVE_PRCTL)
+#if defined(__linux__)
#include <sys/prctl.h>
#endif
@@ -35,9 +35,9 @@
namespace art {
static void EnableDebugger() {
+#if defined(__linux__)
// To let a non-privileged gdbserver attach to this
// process, we must set our dumpable flag.
-#if defined(HAVE_PRCTL)
if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
PLOG(ERROR) << "prctl(PR_SET_DUMPABLE) failed for pid " << getpid();
}
@@ -86,9 +86,15 @@
}
debug_flags &= ~DEBUG_ENABLE_DEBUGGER;
- // These two are for backwards compatibility with Dalvik.
+ if ((debug_flags & DEBUG_ENABLE_SAFEMODE) != 0) {
+ // Ensure that any (secondary) oat files will be interpreted.
+ Runtime* runtime = Runtime::Current();
+ runtime->AddCompilerOption("--compiler-filter=interpret-only");
+ debug_flags &= ~DEBUG_ENABLE_SAFEMODE;
+ }
+
+ // This is for backwards compatibility with Dalvik.
debug_flags &= ~DEBUG_ENABLE_ASSERT;
- debug_flags &= ~DEBUG_ENABLE_SAFEMODE;
if (debug_flags != 0) {
LOG(ERROR) << StringPrintf("Unknown bits set in debug_flags: %#x", debug_flags);
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index 760eb9b..e4b8db1 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -88,6 +88,7 @@
case kWaitingForSignalCatcherOutput: return kJavaWaiting;
case kWaitingInMainSignalCatcherLoop: return kJavaWaiting;
case kWaitingForMethodTracingStart: return kJavaWaiting;
+ case kWaitingForVisitObjects: return kJavaWaiting;
case kSuspended: return kJavaRunnable;
// Don't add a 'default' here so the compiler can spot incompatible enum changes.
}
diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc
index 1775468..b7f31f2 100644
--- a/runtime/native_bridge_art_interface.cc
+++ b/runtime/native_bridge_art_interface.cc
@@ -20,6 +20,7 @@
#include "base/logging.h"
#include "base/macros.h"
+#include "dex_file-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
#include "scoped_thread_state_change.h"
diff --git a/runtime/oat.cc b/runtime/oat.cc
index eab34f7..c223e2e 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -20,12 +20,13 @@
#include <zlib.h>
#include "arch/instruction_set_features.h"
+#include "base/stringprintf.h"
#include "utils.h"
namespace art {
-const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '5', '1', '\0' };
+constexpr uint8_t OatHeader::kOatMagic[4];
+constexpr uint8_t OatHeader::kOatVersion[4];
static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
size_t estimate = 0U;
@@ -67,6 +68,13 @@
uint32_t image_file_location_oat_checksum,
uint32_t image_file_location_oat_data_begin,
const SafeMap<std::string, std::string>* variable_data) {
+ // Don't want asserts in header as they would be checked in each file that includes it. But the
+ // fields are private, so we check inside a method.
+ static_assert(sizeof(magic_) == sizeof(kOatMagic),
+ "Oat magic and magic_ have different lengths.");
+ static_assert(sizeof(version_) == sizeof(kOatVersion),
+ "Oat version and version_ have different lengths.");
+
memcpy(magic_, kOatMagic, sizeof(kOatMagic));
memcpy(version_, kOatVersion, sizeof(kOatVersion));
executable_offset_ = 0;
@@ -105,9 +113,6 @@
interpreter_to_interpreter_bridge_offset_ = 0;
interpreter_to_compiled_code_bridge_offset_ = 0;
jni_dlsym_lookup_offset_ = 0;
- portable_imt_conflict_trampoline_offset_ = 0;
- portable_resolution_trampoline_offset_ = 0;
- portable_to_interpreter_bridge_offset_ = 0;
quick_generic_jni_trampoline_offset_ = 0;
quick_imt_conflict_trampoline_offset_ = 0;
quick_resolution_trampoline_offset_ = 0;
@@ -130,6 +135,28 @@
return true;
}
+std::string OatHeader::GetValidationErrorMessage() const {
+ if (memcmp(magic_, kOatMagic, sizeof(kOatMagic)) != 0) {
+ static_assert(sizeof(kOatMagic) == 4, "kOatMagic has unexpected length");
+ return StringPrintf("Invalid oat magic, expected 0x%x%x%x%x, got 0x%x%x%x%x.",
+ kOatMagic[0], kOatMagic[1], kOatMagic[2], kOatMagic[3],
+ magic_[0], magic_[1], magic_[2], magic_[3]);
+ }
+ if (memcmp(version_, kOatVersion, sizeof(kOatVersion)) != 0) {
+ static_assert(sizeof(kOatVersion) == 4, "kOatVersion has unexpected length");
+ return StringPrintf("Invalid oat version, expected 0x%x%x%x%x, got 0x%x%x%x%x.",
+ kOatVersion[0], kOatVersion[1], kOatVersion[2], kOatVersion[3],
+ version_[0], version_[1], version_[2], version_[3]);
+ }
+ if (!IsAligned<kPageSize>(executable_offset_)) {
+ return "Executable offset not page-aligned.";
+ }
+ if (!IsAligned<kPageSize>(image_patch_delta_)) {
+ return "Image patch delta not page-aligned.";
+ }
+ return "";
+}
+
const char* OatHeader::GetMagic() const {
CHECK(IsValid());
return reinterpret_cast<const char*>(magic_);
@@ -231,75 +258,18 @@
UpdateChecksum(&jni_dlsym_lookup_offset_, sizeof(offset));
}
-const void* OatHeader::GetPortableImtConflictTrampoline() const {
- return reinterpret_cast<const uint8_t*>(this) + GetPortableImtConflictTrampolineOffset();
-}
-
-uint32_t OatHeader::GetPortableImtConflictTrampolineOffset() const {
- DCHECK(IsValid());
- CHECK_GE(portable_imt_conflict_trampoline_offset_, jni_dlsym_lookup_offset_);
- return portable_imt_conflict_trampoline_offset_;
-}
-
-void OatHeader::SetPortableImtConflictTrampolineOffset(uint32_t offset) {
- CHECK(offset == 0 || offset >= jni_dlsym_lookup_offset_);
- DCHECK(IsValid());
- DCHECK_EQ(portable_imt_conflict_trampoline_offset_, 0U) << offset;
-
- portable_imt_conflict_trampoline_offset_ = offset;
- UpdateChecksum(&portable_imt_conflict_trampoline_offset_, sizeof(offset));
-}
-
-const void* OatHeader::GetPortableResolutionTrampoline() const {
- return reinterpret_cast<const uint8_t*>(this) + GetPortableResolutionTrampolineOffset();
-}
-
-uint32_t OatHeader::GetPortableResolutionTrampolineOffset() const {
- DCHECK(IsValid());
- CHECK_GE(portable_resolution_trampoline_offset_, portable_imt_conflict_trampoline_offset_);
- return portable_resolution_trampoline_offset_;
-}
-
-void OatHeader::SetPortableResolutionTrampolineOffset(uint32_t offset) {
- CHECK(offset == 0 || offset >= portable_imt_conflict_trampoline_offset_);
- DCHECK(IsValid());
- DCHECK_EQ(portable_resolution_trampoline_offset_, 0U) << offset;
-
- portable_resolution_trampoline_offset_ = offset;
- UpdateChecksum(&portable_resolution_trampoline_offset_, sizeof(offset));
-}
-
-const void* OatHeader::GetPortableToInterpreterBridge() const {
- return reinterpret_cast<const uint8_t*>(this) + GetPortableToInterpreterBridgeOffset();
-}
-
-uint32_t OatHeader::GetPortableToInterpreterBridgeOffset() const {
- DCHECK(IsValid());
- CHECK_GE(portable_to_interpreter_bridge_offset_, portable_resolution_trampoline_offset_);
- return portable_to_interpreter_bridge_offset_;
-}
-
-void OatHeader::SetPortableToInterpreterBridgeOffset(uint32_t offset) {
- CHECK(offset == 0 || offset >= portable_resolution_trampoline_offset_);
- DCHECK(IsValid());
- DCHECK_EQ(portable_to_interpreter_bridge_offset_, 0U) << offset;
-
- portable_to_interpreter_bridge_offset_ = offset;
- UpdateChecksum(&portable_to_interpreter_bridge_offset_, sizeof(offset));
-}
-
const void* OatHeader::GetQuickGenericJniTrampoline() const {
return reinterpret_cast<const uint8_t*>(this) + GetQuickGenericJniTrampolineOffset();
}
uint32_t OatHeader::GetQuickGenericJniTrampolineOffset() const {
DCHECK(IsValid());
- CHECK_GE(quick_generic_jni_trampoline_offset_, portable_to_interpreter_bridge_offset_);
+ CHECK_GE(quick_generic_jni_trampoline_offset_, jni_dlsym_lookup_offset_);
return quick_generic_jni_trampoline_offset_;
}
void OatHeader::SetQuickGenericJniTrampolineOffset(uint32_t offset) {
- CHECK(offset == 0 || offset >= portable_to_interpreter_bridge_offset_);
+ CHECK(offset == 0 || offset >= jni_dlsym_lookup_offset_);
DCHECK(IsValid());
DCHECK_EQ(quick_generic_jni_trampoline_offset_, 0U) << offset;
diff --git a/runtime/oat.h b/runtime/oat.h
index 11ed4fb..7faf33b 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -31,8 +31,8 @@
class PACKED(4) OatHeader {
public:
- static const uint8_t kOatMagic[4];
- static const uint8_t kOatVersion[4];
+ static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
+ static constexpr uint8_t kOatVersion[] = { '0', '5', '5', '\0' };
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
@@ -47,6 +47,7 @@
const SafeMap<std::string, std::string>* variable_data);
bool IsValid() const;
+ std::string GetValidationErrorMessage() const;
const char* GetMagic() const;
uint32_t GetChecksum() const;
void UpdateChecksum(const void* data, size_t length);
@@ -68,16 +69,6 @@
uint32_t GetJniDlsymLookupOffset() const;
void SetJniDlsymLookupOffset(uint32_t offset);
- const void* GetPortableResolutionTrampoline() const;
- uint32_t GetPortableResolutionTrampolineOffset() const;
- void SetPortableResolutionTrampolineOffset(uint32_t offset);
- const void* GetPortableImtConflictTrampoline() const;
- uint32_t GetPortableImtConflictTrampolineOffset() const;
- void SetPortableImtConflictTrampolineOffset(uint32_t offset);
- const void* GetPortableToInterpreterBridge() const;
- uint32_t GetPortableToInterpreterBridgeOffset() const;
- void SetPortableToInterpreterBridgeOffset(uint32_t offset);
-
const void* GetQuickGenericJniTrampoline() const;
uint32_t GetQuickGenericJniTrampolineOffset() const;
void SetQuickGenericJniTrampolineOffset(uint32_t offset);
@@ -129,9 +120,6 @@
uint32_t interpreter_to_interpreter_bridge_offset_;
uint32_t interpreter_to_compiled_code_bridge_offset_;
uint32_t jni_dlsym_lookup_offset_;
- uint32_t portable_imt_conflict_trampoline_offset_;
- uint32_t portable_resolution_trampoline_offset_;
- uint32_t portable_to_interpreter_bridge_offset_;
uint32_t quick_generic_jni_trampoline_offset_;
uint32_t quick_imt_conflict_trampoline_offset_;
uint32_t quick_resolution_trampoline_offset_;
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 91e571b..9061bb3 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -75,32 +75,26 @@
CHECK(!filename.empty()) << location;
CheckLocation(location);
std::unique_ptr<OatFile> ret;
- if (kUsePortableCompiler && executable) {
- // If we are using PORTABLE, use dlopen to deal with relocations.
- //
- // We use our own ELF loader for Quick to deal with legacy apps that
- // open a generated dex file by name, remove the file, then open
- // another generated dex file with the same name. http://b/10614658
- ret.reset(OpenDlopen(filename, location, requested_base, error_msg));
- } else {
- // If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons:
- //
- // On target, dlopen may fail when compiling due to selinux restrictions on installd.
- //
- // On host, dlopen is expected to fail when cross compiling, so fall back to OpenElfFile.
- // This won't work for portable runtime execution because it doesn't process relocations.
- std::unique_ptr<File> file(OS::OpenFileForReading(filename.c_str()));
- if (file.get() == NULL) {
- *error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno));
- return nullptr;
- }
- ret.reset(OpenElfFile(file.get(), location, requested_base, oat_file_begin, false, executable,
- error_msg));
-
- // It would be nice to unlink here. But we might have opened the file created by the
- // ScopedLock, which we better not delete to avoid races. TODO: Investigate how to fix the API
- // to allow removal when we know the ELF must be borked.
+ // If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons:
+ //
+ // On target, dlopen may fail when compiling due to selinux restrictions on installd.
+ //
+ // We use our own ELF loader for Quick to deal with legacy apps that
+ // open a generated dex file by name, remove the file, then open
+ // another generated dex file with the same name. http://b/10614658
+ //
+ // On host, dlopen is expected to fail when cross compiling, so fall back to OpenElfFile.
+ std::unique_ptr<File> file(OS::OpenFileForReading(filename.c_str()));
+ if (file.get() == NULL) {
+ *error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno));
+ return nullptr;
}
+ ret.reset(OpenElfFile(file.get(), location, requested_base, oat_file_begin, false, executable,
+ error_msg));
+
+ // It would be nice to unlink here. But we might have opened the file created by the
+ // ScopedLock, which we better not delete to avoid races. TODO: Investigate how to fix the API
+ // to allow removal when we know the ELF must be borked.
return ret.release();
}
@@ -233,7 +227,9 @@
bool OatFile::Setup(std::string* error_msg) {
if (!GetOatHeader().IsValid()) {
- *error_msg = StringPrintf("Invalid oat magic for '%s'", GetLocation().c_str());
+ std::string cause = GetOatHeader().GetValidationErrorMessage();
+ *error_msg = StringPrintf("Invalid oat header for '%s': %s", GetLocation().c_str(),
+ cause.c_str());
return false;
}
const uint8_t* oat = Begin();
@@ -458,9 +454,9 @@
return reinterpret_cast<const DexFile::Header*>(dex_file_pointer_)->file_size_;
}
-const DexFile* OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
+std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
return DexFile::Open(dex_file_pointer_, FileSize(), dex_file_location_,
- dex_file_location_checksum_, error_msg);
+ dex_file_location_checksum_, GetOatFile(), error_msg);
}
uint32_t OatFile::OatDexFile::GetOatClassOffset(uint16_t class_def_index) const {
@@ -591,7 +587,6 @@
void OatFile::OatMethod::LinkMethod(mirror::ArtMethod* method) const {
CHECK(method != NULL);
- method->SetEntryPointFromPortableCompiledCode(GetPortableCode());
method->SetEntryPointFromQuickCompiledCode(GetQuickCode());
}
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index a335c94..6ae3c3e 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -97,29 +97,8 @@
return code_offset_;
}
- const void* GetPortableCode() const {
- // TODO: encode whether code is portable/quick in flags within OatMethod.
- if (kUsePortableCompiler) {
- return GetOatPointer<const void*>(code_offset_);
- } else {
- return nullptr;
- }
- }
-
const void* GetQuickCode() const {
- if (kUsePortableCompiler) {
- return nullptr;
- } else {
- return GetOatPointer<const void*>(code_offset_);
- }
- }
-
- // Returns 0.
- uint32_t GetPortableCodeSize() const {
- // TODO: With Quick, we store the size before the code. With Portable, the code is in a .o
- // file we don't manage ourselves. ELF symbols do have a concept of size, so we could capture
- // that and store it somewhere, such as the OatMethod.
- return 0;
+ return GetOatPointer<const void*>(code_offset_);
}
// Returns size of quick code.
@@ -231,7 +210,7 @@
class OatDexFile {
public:
// Opens the DexFile referred to by this OatDexFile from within the containing OatFile.
- const DexFile* OpenDexFile(std::string* error_msg) const;
+ std::unique_ptr<const DexFile> OpenDexFile(std::string* error_msg) const;
const OatFile* GetOatFile() const {
return oat_file_;
diff --git a/runtime/object_callbacks.h b/runtime/object_callbacks.h
index 592deed..cf81cc5 100644
--- a/runtime/object_callbacks.h
+++ b/runtime/object_callbacks.h
@@ -35,34 +35,10 @@
} // namespace mirror
class StackVisitor;
-enum RootType {
- kRootUnknown = 0,
- kRootJNIGlobal,
- kRootJNILocal,
- kRootJavaFrame,
- kRootNativeStack,
- kRootStickyClass,
- kRootThreadBlock,
- kRootMonitorUsed,
- kRootThreadObject,
- kRootInternedString,
- kRootDebugger,
- kRootVMInternal,
- kRootJNIMonitor,
-};
-std::ostream& operator<<(std::ostream& os, const RootType& root_type);
-
-// Returns the new address of the object, returns root if it has not moved. tid and root_type are
-// only used by hprof.
-typedef void (RootCallback)(mirror::Object** root, void* arg, uint32_t thread_id,
- RootType root_type);
// A callback for visiting an object in the heap.
typedef void (ObjectCallback)(mirror::Object* obj, void* arg);
// A callback used for marking an object, returns the new address of the object if the object moved.
typedef mirror::Object* (MarkObjectCallback)(mirror::Object* obj, void* arg) WARN_UNUSED;
-// A callback for verifying roots.
-typedef void (VerifyRootCallback)(const mirror::Object* root, void* arg, size_t vreg,
- const StackVisitor* visitor, RootType root_type);
typedef void (MarkHeapReferenceCallback)(mirror::HeapReference<mirror::Object>* ref, void* arg);
typedef void (DelayReferenceReferentCallback)(mirror::Class* klass, mirror::Reference* ref, void* arg);
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 3e6c86b..a5df892 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -18,10 +18,6 @@
#include <sstream>
-#ifdef HAVE_ANDROID_OS
-#include "cutils/properties.h"
-#endif
-
#include "base/stringpiece.h"
#include "debugger.h"
#include "gc/heap.h"
@@ -30,234 +26,327 @@
#include "trace.h"
#include "utils.h"
+#include "cmdline_parser.h"
+#include "runtime_options.h"
+
namespace art {
+using MemoryKiB = Memory<1024>;
+
ParsedOptions::ParsedOptions()
- :
- boot_class_path_(nullptr),
- check_jni_(kIsDebugBuild), // -Xcheck:jni is off by default for regular
- // builds but on by default in debug builds.
- force_copy_(false),
- compiler_callbacks_(nullptr),
- is_zygote_(false),
- must_relocate_(kDefaultMustRelocate),
- dex2oat_enabled_(true),
- image_dex2oat_enabled_(true),
- interpreter_only_(kPoisonHeapReferences), // kPoisonHeapReferences currently works with
- // the interpreter only.
- // TODO: make it work with the compiler.
- is_explicit_gc_disabled_(false),
- use_tlab_(false),
- verify_pre_gc_heap_(false),
- verify_pre_sweeping_heap_(kIsDebugBuild), // Pre sweeping is the one that usually fails
- // if the GC corrupted the heap.
- verify_post_gc_heap_(false),
- verify_pre_gc_rosalloc_(kIsDebugBuild),
- verify_pre_sweeping_rosalloc_(false),
- verify_post_gc_rosalloc_(false),
- long_pause_log_threshold_(gc::Heap::kDefaultLongPauseLogThreshold),
- long_gc_log_threshold_(gc::Heap::kDefaultLongGCLogThreshold),
- dump_gc_performance_on_shutdown_(false),
- ignore_max_footprint_(false),
- heap_initial_size_(gc::Heap::kDefaultInitialSize),
- heap_maximum_size_(gc::Heap::kDefaultMaximumSize),
- heap_growth_limit_(0), // 0 means no growth limit.
- heap_min_free_(gc::Heap::kDefaultMinFree),
- heap_max_free_(gc::Heap::kDefaultMaxFree),
- heap_non_moving_space_capacity_(gc::Heap::kDefaultNonMovingSpaceCapacity),
- large_object_space_type_(gc::Heap::kDefaultLargeObjectSpaceType),
- large_object_threshold_(gc::Heap::kDefaultLargeObjectThreshold),
- heap_target_utilization_(gc::Heap::kDefaultTargetUtilization),
- foreground_heap_growth_multiplier_(gc::Heap::kDefaultHeapGrowthMultiplier),
- parallel_gc_threads_(1),
- conc_gc_threads_(0), // Only the main GC thread, no workers.
- collector_type_( // The default GC type is set in makefiles.
-#if ART_DEFAULT_GC_TYPE_IS_CMS
- gc::kCollectorTypeCMS),
-#elif ART_DEFAULT_GC_TYPE_IS_SS
- gc::kCollectorTypeSS),
-#elif ART_DEFAULT_GC_TYPE_IS_GSS
- gc::kCollectorTypeGSS),
-#else
- gc::kCollectorTypeCMS),
-#error "ART default GC type must be set"
-#endif
- background_collector_type_(gc::kCollectorTypeNone),
- // If background_collector_type_ is
- // kCollectorTypeNone, it defaults to the
- // collector_type_ after parsing options. If
- // you set this to kCollectorTypeHSpaceCompact
- // then we will do an hspace compaction when
- // we transition to background instead of a
- // normal collector transition.
- stack_size_(0), // 0 means default.
- max_spins_before_thin_lock_inflation_(Monitor::kDefaultMaxSpinsBeforeThinLockInflation),
- low_memory_mode_(false),
- lock_profiling_threshold_(0),
- method_trace_(false),
- method_trace_file_("/data/method-trace-file.bin"),
- method_trace_file_size_(10 * MB),
- hook_is_sensitive_thread_(nullptr),
+ : hook_is_sensitive_thread_(nullptr),
hook_vfprintf_(vfprintf),
hook_exit_(exit),
- hook_abort_(nullptr), // We don't call abort(3) by default; see
- // Runtime::Abort.
- profile_clock_source_(kDefaultTraceClockSource),
- verify_(true),
- image_isa_(kRuntimeISA),
- use_homogeneous_space_compaction_for_oom_(true), // Enable hspace compaction on OOM by default.
- min_interval_homogeneous_space_compaction_by_oom_(MsToNs(100 * 1000)) // 100s.
- {}
+ hook_abort_(nullptr) { // We don't call abort(3) by default; see
+ // Runtime::Abort
+}
-ParsedOptions* ParsedOptions::Create(const RuntimeOptions& options, bool ignore_unrecognized) {
+ParsedOptions* ParsedOptions::Create(const RuntimeOptions& options, bool ignore_unrecognized,
+ RuntimeArgumentMap* runtime_options) {
+ CHECK(runtime_options != nullptr);
+
std::unique_ptr<ParsedOptions> parsed(new ParsedOptions());
- if (parsed->Parse(options, ignore_unrecognized)) {
+ if (parsed->Parse(options, ignore_unrecognized, runtime_options)) {
return parsed.release();
}
return nullptr;
}
-// Parse a string of the form /[0-9]+[kKmMgG]?/, which is used to specify
-// memory sizes. [kK] indicates kilobytes, [mM] megabytes, and
-// [gG] gigabytes.
-//
-// "s" should point just past the "-Xm?" part of the string.
-// "div" specifies a divisor, e.g. 1024 if the value must be a multiple
-// of 1024.
-//
-// The spec says the -Xmx and -Xms options must be multiples of 1024. It
-// doesn't say anything about -Xss.
-//
-// Returns 0 (a useless size) if "s" is malformed or specifies a low or
-// non-evenly-divisible value.
-//
-size_t ParseMemoryOption(const char* s, size_t div) {
- // strtoul accepts a leading [+-], which we don't want,
- // so make sure our string starts with a decimal digit.
- if (isdigit(*s)) {
- char* s2;
- size_t val = strtoul(s, &s2, 10);
- if (s2 != s) {
- // s2 should be pointing just after the number.
- // If this is the end of the string, the user
- // has specified a number of bytes. Otherwise,
- // there should be exactly one more character
- // that specifies a multiplier.
- if (*s2 != '\0') {
- // The remainder of the string is either a single multiplier
- // character, or nothing to indicate that the value is in
- // bytes.
- char c = *s2++;
- if (*s2 == '\0') {
- size_t mul;
- if (c == '\0') {
- mul = 1;
- } else if (c == 'k' || c == 'K') {
- mul = KB;
- } else if (c == 'm' || c == 'M') {
- mul = MB;
- } else if (c == 'g' || c == 'G') {
- mul = GB;
- } else {
- // Unknown multiplier character.
- return 0;
- }
+using RuntimeParser = CmdlineParser<RuntimeArgumentMap, RuntimeArgumentMap::Key>;
- if (val <= std::numeric_limits<size_t>::max() / mul) {
- val *= mul;
- } else {
- // Clamp to a multiple of 1024.
- val = std::numeric_limits<size_t>::max() & ~(1024-1);
- }
- } else {
- // There's more than one character after the numeric part.
- return 0;
- }
- }
- // The man page says that a -Xm value must be a multiple of 1024.
- if (val % div == 0) {
- return val;
- }
- }
- }
- return 0;
+// Yes, the stack frame is huge. But we get called super early on (and just once)
+// to pass the command line arguments, so we'll probably be ok.
+// Ideas to avoid suppressing this diagnostic are welcome!
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
+
+std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognized) {
+ using M = RuntimeArgumentMap;
+
+ std::unique_ptr<RuntimeParser::Builder> parser_builder =
+ std::unique_ptr<RuntimeParser::Builder>(new RuntimeParser::Builder());
+
+ parser_builder->
+ Define("-Xzygote")
+ .IntoKey(M::Zygote)
+ .Define("-help")
+ .IntoKey(M::Help)
+ .Define("-showversion")
+ .IntoKey(M::ShowVersion)
+ .Define("-Xbootclasspath:_")
+ .WithType<std::string>()
+ .IntoKey(M::BootClassPath)
+ .Define("-Xbootclasspath-locations:_")
+ .WithType<ParseStringList<':'>>() // std::vector<std::string>, split by :
+ .IntoKey(M::BootClassPathLocations)
+ .Define({"-classpath _", "-cp _"})
+ .WithType<std::string>()
+ .IntoKey(M::ClassPath)
+ .Define("-Ximage:_")
+ .WithType<std::string>()
+ .IntoKey(M::Image)
+ .Define("-Xcheck:jni")
+ .IntoKey(M::CheckJni)
+ .Define("-Xjniopts:forcecopy")
+ .IntoKey(M::JniOptsForceCopy)
+ .Define({"-Xrunjdwp:_", "-agentlib:jdwp=_"})
+ .WithType<JDWP::JdwpOptions>()
+ .IntoKey(M::JdwpOptions)
+ .Define("-Xms_")
+ .WithType<MemoryKiB>()
+ .IntoKey(M::MemoryInitialSize)
+ .Define("-Xmx_")
+ .WithType<MemoryKiB>()
+ .IntoKey(M::MemoryMaximumSize)
+ .Define("-XX:HeapGrowthLimit=_")
+ .WithType<MemoryKiB>()
+ .IntoKey(M::HeapGrowthLimit)
+ .Define("-XX:HeapMinFree=_")
+ .WithType<MemoryKiB>()
+ .IntoKey(M::HeapMinFree)
+ .Define("-XX:HeapMaxFree=_")
+ .WithType<MemoryKiB>()
+ .IntoKey(M::HeapMaxFree)
+ .Define("-XX:NonMovingSpaceCapacity=_")
+ .WithType<MemoryKiB>()
+ .IntoKey(M::NonMovingSpaceCapacity)
+ .Define("-XX:HeapTargetUtilization=_")
+ .WithType<double>().WithRange(0.1, 0.9)
+ .IntoKey(M::HeapTargetUtilization)
+ .Define("-XX:ForegroundHeapGrowthMultiplier=_")
+ .WithType<double>().WithRange(0.1, 1.0)
+ .IntoKey(M::ForegroundHeapGrowthMultiplier)
+ .Define("-XX:ParallelGCThreads=_")
+ .WithType<unsigned int>()
+ .IntoKey(M::ParallelGCThreads)
+ .Define("-XX:ConcGCThreads=_")
+ .WithType<unsigned int>()
+ .IntoKey(M::ConcGCThreads)
+ .Define("-Xss_")
+ .WithType<Memory<1>>()
+ .IntoKey(M::StackSize)
+ .Define("-XX:MaxSpinsBeforeThinLockInflation=_")
+ .WithType<unsigned int>()
+ .IntoKey(M::MaxSpinsBeforeThinLockInflation)
+ .Define("-XX:LongPauseLogThreshold=_") // in ms
+ .WithType<MillisecondsToNanoseconds>() // store as ns
+ .IntoKey(M::LongPauseLogThreshold)
+ .Define("-XX:LongGCLogThreshold=_") // in ms
+ .WithType<MillisecondsToNanoseconds>() // store as ns
+ .IntoKey(M::LongGCLogThreshold)
+ .Define("-XX:DumpGCPerformanceOnShutdown")
+ .IntoKey(M::DumpGCPerformanceOnShutdown)
+ .Define("-XX:IgnoreMaxFootprint")
+ .IntoKey(M::IgnoreMaxFootprint)
+ .Define("-XX:LowMemoryMode")
+ .IntoKey(M::LowMemoryMode)
+ .Define("-XX:UseTLAB")
+ .IntoKey(M::UseTLAB)
+ .Define({"-XX:EnableHSpaceCompactForOOM", "-XX:DisableHSpaceCompactForOOM"})
+ .WithValues({true, false})
+ .IntoKey(M::EnableHSpaceCompactForOOM)
+ .Define("-XX:HspaceCompactForOOMMinIntervalMs=_") // in ms
+ .WithType<MillisecondsToNanoseconds>() // store as ns
+ .IntoKey(M::HSpaceCompactForOOMMinIntervalsMs)
+ .Define("-D_")
+ .WithType<std::vector<std::string>>().AppendValues()
+ .IntoKey(M::PropertiesList)
+ .Define("-Xjnitrace:_")
+ .WithType<std::string>()
+ .IntoKey(M::JniTrace)
+ .Define("-Xpatchoat:_")
+ .WithType<std::string>()
+ .IntoKey(M::PatchOat)
+ .Define({"-Xrelocate", "-Xnorelocate"})
+ .WithValues({true, false})
+ .IntoKey(M::Relocate)
+ .Define({"-Xdex2oat", "-Xnodex2oat"})
+ .WithValues({true, false})
+ .IntoKey(M::Dex2Oat)
+ .Define({"-Ximage-dex2oat", "-Xnoimage-dex2oat"})
+ .WithValues({true, false})
+ .IntoKey(M::ImageDex2Oat)
+ .Define("-Xint")
+ .WithValue(true)
+ .IntoKey(M::Interpret)
+ .Define("-Xgc:_")
+ .WithType<XGcOption>()
+ .IntoKey(M::GcOption)
+ .Define("-XX:LargeObjectSpace=_")
+ .WithType<gc::space::LargeObjectSpaceType>()
+ .WithValueMap({{"disabled", gc::space::LargeObjectSpaceType::kDisabled},
+ {"freelist", gc::space::LargeObjectSpaceType::kFreeList},
+ {"map", gc::space::LargeObjectSpaceType::kMap}})
+ .IntoKey(M::LargeObjectSpace)
+ .Define("-XX:LargeObjectThreshold=_")
+ .WithType<Memory<1>>()
+ .IntoKey(M::LargeObjectThreshold)
+ .Define("-XX:BackgroundGC=_")
+ .WithType<BackgroundGcOption>()
+ .IntoKey(M::BackgroundGc)
+ .Define("-XX:+DisableExplicitGC")
+ .IntoKey(M::DisableExplicitGC)
+ .Define("-verbose:_")
+ .WithType<LogVerbosity>()
+ .IntoKey(M::Verbose)
+ .Define("-Xlockprofthreshold:_")
+ .WithType<unsigned int>()
+ .IntoKey(M::LockProfThreshold)
+ .Define("-Xstacktracefile:_")
+ .WithType<std::string>()
+ .IntoKey(M::StackTraceFile)
+ .Define("-Xmethod-trace")
+ .IntoKey(M::MethodTrace)
+ .Define("-Xmethod-trace-file:_")
+ .WithType<std::string>()
+ .IntoKey(M::MethodTraceFile)
+ .Define("-Xmethod-trace-file-size:_")
+ .WithType<unsigned int>()
+ .IntoKey(M::MethodTraceFileSize)
+ .Define("-Xprofile:_")
+ .WithType<TraceClockSource>()
+ .WithValueMap({{"threadcpuclock", TraceClockSource::kThreadCpu},
+ {"wallclock", TraceClockSource::kWall},
+ {"dualclock", TraceClockSource::kDual}})
+ .IntoKey(M::ProfileClock)
+ .Define("-Xenable-profiler")
+ .WithType<TestProfilerOptions>()
+ .AppendValues()
+ .IntoKey(M::ProfilerOpts) // NOTE: Appends into same key as -Xprofile-*
+ .Define("-Xprofile-_") // -Xprofile-<key>:<value>
+ .WithType<TestProfilerOptions>()
+ .AppendValues()
+ .IntoKey(M::ProfilerOpts) // NOTE: Appends into same key as -Xenable-profiler
+ .Define("-Xcompiler:_")
+ .WithType<std::string>()
+ .IntoKey(M::Compiler)
+ .Define("-Xcompiler-option _")
+ .WithType<std::vector<std::string>>()
+ .AppendValues()
+ .IntoKey(M::CompilerOptions)
+ .Define("-Ximage-compiler-option _")
+ .WithType<std::vector<std::string>>()
+ .AppendValues()
+ .IntoKey(M::ImageCompilerOptions)
+ .Define("-Xverify:_")
+ .WithType<bool>()
+ .WithValueMap({{"none", false},
+ {"remote", true},
+ {"all", true}})
+ .IntoKey(M::Verify)
+ .Define("-XX:NativeBridge=_")
+ .WithType<std::string>()
+ .IntoKey(M::NativeBridge)
+ .Ignore({
+ "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
+ "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
+ "-Xdexopt:_", "-Xnoquithandler", "-Xjnigreflimit:_", "-Xgenregmap", "-Xnogenregmap",
+ "-Xverifyopt:_", "-Xcheckdexsum", "-Xincludeselectedop", "-Xjitop:_",
+ "-Xincludeselectedmethod", "-Xjitthreshold:_", "-Xjitcodecachesize:_",
+ "-Xjitblocking", "-Xjitmethod:_", "-Xjitclass:_", "-Xjitoffset:_",
+ "-Xjitconfig:_", "-Xjitcheckcg", "-Xjitverbose", "-Xjitprofile",
+ "-Xjitdisableopt", "-Xjitsuspendpoll", "-XX:mainThreadStackSize=_"})
+ .IgnoreUnrecognized(ignore_unrecognized);
+
+ // TODO: Move Usage information into this DSL.
+
+ return std::unique_ptr<RuntimeParser>(new RuntimeParser(parser_builder->Build()));
}
-static gc::CollectorType ParseCollectorType(const std::string& option) {
- if (option == "MS" || option == "nonconcurrent") {
- return gc::kCollectorTypeMS;
- } else if (option == "CMS" || option == "concurrent") {
- return gc::kCollectorTypeCMS;
- } else if (option == "SS") {
- return gc::kCollectorTypeSS;
- } else if (option == "GSS") {
- return gc::kCollectorTypeGSS;
- } else if (option == "CC") {
- return gc::kCollectorTypeCC;
- } else if (option == "MC") {
- return gc::kCollectorTypeMC;
- } else {
- return gc::kCollectorTypeNone;
- }
-}
+#pragma GCC diagnostic pop
-bool ParsedOptions::ParseXGcOption(const std::string& option) {
- std::vector<std::string> gc_options;
- Split(option.substr(strlen("-Xgc:")), ',', &gc_options);
- for (const std::string& gc_option : gc_options) {
- gc::CollectorType collector_type = ParseCollectorType(gc_option);
- if (collector_type != gc::kCollectorTypeNone) {
- collector_type_ = collector_type;
- } else if (gc_option == "preverify") {
- verify_pre_gc_heap_ = true;
- } else if (gc_option == "nopreverify") {
- verify_pre_gc_heap_ = false;
- } else if (gc_option == "presweepingverify") {
- verify_pre_sweeping_heap_ = true;
- } else if (gc_option == "nopresweepingverify") {
- verify_pre_sweeping_heap_ = false;
- } else if (gc_option == "postverify") {
- verify_post_gc_heap_ = true;
- } else if (gc_option == "nopostverify") {
- verify_post_gc_heap_ = false;
- } else if (gc_option == "preverify_rosalloc") {
- verify_pre_gc_rosalloc_ = true;
- } else if (gc_option == "nopreverify_rosalloc") {
- verify_pre_gc_rosalloc_ = false;
- } else if (gc_option == "presweepingverify_rosalloc") {
- verify_pre_sweeping_rosalloc_ = true;
- } else if (gc_option == "nopresweepingverify_rosalloc") {
- verify_pre_sweeping_rosalloc_ = false;
- } else if (gc_option == "postverify_rosalloc") {
- verify_post_gc_rosalloc_ = true;
- } else if (gc_option == "nopostverify_rosalloc") {
- verify_post_gc_rosalloc_ = false;
- } else if ((gc_option == "precise") ||
- (gc_option == "noprecise") ||
- (gc_option == "verifycardtable") ||
- (gc_option == "noverifycardtable")) {
- // Ignored for backwards compatibility.
+// Remove all the special options that have something in the void* part of the option.
+// If runtime_options is not null, put the options in there.
+// As a side-effect, populate the hooks from options.
+bool ParsedOptions::ProcessSpecialOptions(const RuntimeOptions& options,
+ RuntimeArgumentMap* runtime_options,
+ std::vector<std::string>* out_options) {
+ using M = RuntimeArgumentMap;
+
+ // TODO: Move the below loop into JNI
+ // Handle special options that set up hooks
+ for (size_t i = 0; i < options.size(); ++i) {
+ const std::string option(options[i].first);
+ // TODO: support -Djava.class.path
+ if (option == "bootclasspath") {
+ auto boot_class_path
+ = reinterpret_cast<const std::vector<const DexFile*>*>(options[i].second);
+
+ if (runtime_options != nullptr) {
+ runtime_options->Set(M::BootClassPathDexList, boot_class_path);
+ }
+ } else if (option == "compilercallbacks") {
+ CompilerCallbacks* compiler_callbacks =
+ reinterpret_cast<CompilerCallbacks*>(const_cast<void*>(options[i].second));
+ if (runtime_options != nullptr) {
+ runtime_options->Set(M::CompilerCallbacksPtr, compiler_callbacks);
+ }
+ } else if (option == "imageinstructionset") {
+ const char* isa_str = reinterpret_cast<const char*>(options[i].second);
+ auto&& image_isa = GetInstructionSetFromString(isa_str);
+ if (image_isa == kNone) {
+ Usage("%s is not a valid instruction set.", isa_str);
+ return false;
+ }
+ if (runtime_options != nullptr) {
+ runtime_options->Set(M::ImageInstructionSet, image_isa);
+ }
+ } else if (option == "sensitiveThread") {
+ const void* hook = options[i].second;
+ bool (*hook_is_sensitive_thread)() = reinterpret_cast<bool (*)()>(const_cast<void*>(hook));
+
+ if (runtime_options != nullptr) {
+ runtime_options->Set(M::HookIsSensitiveThread, hook_is_sensitive_thread);
+ }
+ } else if (option == "vfprintf") {
+ const void* hook = options[i].second;
+ if (hook == nullptr) {
+ Usage("vfprintf argument was NULL");
+ return false;
+ }
+ int (*hook_vfprintf)(FILE *, const char*, va_list) =
+ reinterpret_cast<int (*)(FILE *, const char*, va_list)>(const_cast<void*>(hook));
+
+ if (runtime_options != nullptr) {
+ runtime_options->Set(M::HookVfprintf, hook_vfprintf);
+ }
+ hook_vfprintf_ = hook_vfprintf;
+ } else if (option == "exit") {
+ const void* hook = options[i].second;
+ if (hook == nullptr) {
+ Usage("exit argument was NULL");
+ return false;
+ }
+ void(*hook_exit)(jint) = reinterpret_cast<void(*)(jint)>(const_cast<void*>(hook));
+ if (runtime_options != nullptr) {
+ runtime_options->Set(M::HookExit, hook_exit);
+ }
+ hook_exit_ = hook_exit;
+ } else if (option == "abort") {
+ const void* hook = options[i].second;
+ if (hook == nullptr) {
+ Usage("abort was NULL\n");
+ return false;
+ }
+ void(*hook_abort)() = reinterpret_cast<void(*)()>(const_cast<void*>(hook));
+ if (runtime_options != nullptr) {
+ runtime_options->Set(M::HookAbort, hook_abort);
+ }
+ hook_abort_ = hook_abort;
} else {
- Usage("Unknown -Xgc option %s\n", gc_option.c_str());
- return false;
+ // It is a regular option, that doesn't have a known 'second' value.
+ // Push it on to the regular options which will be parsed by our parser.
+ if (out_options != nullptr) {
+ out_options->push_back(option);
+ }
}
}
+
return true;
}
-bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognized) {
- const char* boot_class_path_string = getenv("BOOTCLASSPATH");
- if (boot_class_path_string != NULL) {
- boot_class_path_string_ = boot_class_path_string;
- }
- const char* class_path_string = getenv("CLASSPATH");
- if (class_path_string != NULL) {
- class_path_string_ = class_path_string;
- }
-
- // Default to number of processors minus one since the main GC thread also does work.
- parallel_gc_threads_ = sysconf(_SC_NPROCESSORS_CONF) - 1;
-
+bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognized,
+ RuntimeArgumentMap* runtime_options) {
// gLogVerbosity.class_linker = true; // TODO: don't check this in!
// gLogVerbosity.compiler = true; // TODO: don't check this in!
// gLogVerbosity.gc = true; // TODO: don't check this in!
@@ -277,425 +366,104 @@
LOG(INFO) << "option[" << i << "]=" << options[i].first;
}
}
- for (size_t i = 0; i < options.size(); ++i) {
- const std::string option(options[i].first);
- if (StartsWith(option, "-help")) {
- Usage(nullptr);
- return false;
- } else if (StartsWith(option, "-showversion")) {
- UsageMessage(stdout, "ART version %s\n", Runtime::GetVersion());
+
+ auto parser = MakeParser(ignore_unrecognized);
+
+ // Convert to a simple string list (without the magic pointer options)
+ std::vector<std::string> argv_list;
+ if (!ProcessSpecialOptions(options, nullptr, &argv_list)) {
+ return false;
+ }
+
+ CmdlineResult parse_result = parser->Parse(argv_list);
+
+ // Handle parse errors by displaying the usage and potentially exiting.
+ if (parse_result.IsError()) {
+ if (parse_result.GetStatus() == CmdlineResult::kUsage) {
+ UsageMessage(stdout, "%s\n", parse_result.GetMessage().c_str());
Exit(0);
- } else if (StartsWith(option, "-Xbootclasspath:")) {
- boot_class_path_string_ = option.substr(strlen("-Xbootclasspath:")).data();
- LOG(INFO) << "setting boot class path to " << boot_class_path_string_;
- } else if (option == "-classpath" || option == "-cp") {
- // TODO: support -Djava.class.path
- i++;
- if (i == options.size()) {
- Usage("Missing required class path value for %s\n", option.c_str());
- return false;
- }
- const StringPiece& value = options[i].first;
- class_path_string_ = value.data();
- } else if (option == "bootclasspath") {
- boot_class_path_
- = reinterpret_cast<const std::vector<const DexFile*>*>(options[i].second);
- } else if (StartsWith(option, "-Ximage:")) {
- if (!ParseStringAfterChar(option, ':', &image_)) {
- return false;
- }
- } else if (StartsWith(option, "-Xcheck:jni")) {
- check_jni_ = true;
- } else if (StartsWith(option, "-Xjniopts:forcecopy")) {
- force_copy_ = true;
- } else if (StartsWith(option, "-Xrunjdwp:") || StartsWith(option, "-agentlib:jdwp=")) {
- std::string tail(option.substr(option[1] == 'X' ? 10 : 15));
- // TODO: move parsing logic out of Dbg
- if (tail == "help" || !Dbg::ParseJdwpOptions(tail)) {
- if (tail != "help") {
- UsageMessage(stderr, "Failed to parse JDWP option %s\n", tail.c_str());
- }
- Usage("Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n"
- "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n");
- return false;
- }
- } else if (StartsWith(option, "-Xms")) {
- size_t size = ParseMemoryOption(option.substr(strlen("-Xms")).c_str(), 1024);
- if (size == 0) {
- Usage("Failed to parse memory option %s\n", option.c_str());
- return false;
- }
- heap_initial_size_ = size;
- } else if (StartsWith(option, "-Xmx")) {
- size_t size = ParseMemoryOption(option.substr(strlen("-Xmx")).c_str(), 1024);
- if (size == 0) {
- Usage("Failed to parse memory option %s\n", option.c_str());
- return false;
- }
- heap_maximum_size_ = size;
- } else if (StartsWith(option, "-XX:HeapGrowthLimit=")) {
- size_t size = ParseMemoryOption(option.substr(strlen("-XX:HeapGrowthLimit=")).c_str(), 1024);
- if (size == 0) {
- Usage("Failed to parse memory option %s\n", option.c_str());
- return false;
- }
- heap_growth_limit_ = size;
- } else if (StartsWith(option, "-XX:HeapMinFree=")) {
- size_t size = ParseMemoryOption(option.substr(strlen("-XX:HeapMinFree=")).c_str(), 1024);
- if (size == 0) {
- Usage("Failed to parse memory option %s\n", option.c_str());
- return false;
- }
- heap_min_free_ = size;
- } else if (StartsWith(option, "-XX:HeapMaxFree=")) {
- size_t size = ParseMemoryOption(option.substr(strlen("-XX:HeapMaxFree=")).c_str(), 1024);
- if (size == 0) {
- Usage("Failed to parse memory option %s\n", option.c_str());
- return false;
- }
- heap_max_free_ = size;
- } else if (StartsWith(option, "-XX:NonMovingSpaceCapacity=")) {
- size_t size = ParseMemoryOption(
- option.substr(strlen("-XX:NonMovingSpaceCapacity=")).c_str(), 1024);
- if (size == 0) {
- Usage("Failed to parse memory option %s\n", option.c_str());
- return false;
- }
- heap_non_moving_space_capacity_ = size;
- } else if (StartsWith(option, "-XX:HeapTargetUtilization=")) {
- if (!ParseDouble(option, '=', 0.1, 0.9, &heap_target_utilization_)) {
- return false;
- }
- } else if (StartsWith(option, "-XX:ForegroundHeapGrowthMultiplier=")) {
- if (!ParseDouble(option, '=', 0.1, 10.0, &foreground_heap_growth_multiplier_)) {
- return false;
- }
- } else if (StartsWith(option, "-XX:ParallelGCThreads=")) {
- if (!ParseUnsignedInteger(option, '=', ¶llel_gc_threads_)) {
- return false;
- }
- } else if (StartsWith(option, "-XX:ConcGCThreads=")) {
- if (!ParseUnsignedInteger(option, '=', &conc_gc_threads_)) {
- return false;
- }
- } else if (StartsWith(option, "-Xss")) {
- size_t size = ParseMemoryOption(option.substr(strlen("-Xss")).c_str(), 1);
- if (size == 0) {
- Usage("Failed to parse memory option %s\n", option.c_str());
- return false;
- }
- stack_size_ = size;
- } else if (StartsWith(option, "-XX:MaxSpinsBeforeThinLockInflation=")) {
- if (!ParseUnsignedInteger(option, '=', &max_spins_before_thin_lock_inflation_)) {
- return false;
- }
- } else if (StartsWith(option, "-XX:LongPauseLogThreshold=")) {
- unsigned int value;
- if (!ParseUnsignedInteger(option, '=', &value)) {
- return false;
- }
- long_pause_log_threshold_ = MsToNs(value);
- } else if (StartsWith(option, "-XX:LongGCLogThreshold=")) {
- unsigned int value;
- if (!ParseUnsignedInteger(option, '=', &value)) {
- return false;
- }
- long_gc_log_threshold_ = MsToNs(value);
- } else if (option == "-XX:DumpGCPerformanceOnShutdown") {
- dump_gc_performance_on_shutdown_ = true;
- } else if (option == "-XX:IgnoreMaxFootprint") {
- ignore_max_footprint_ = true;
- } else if (option == "-XX:LowMemoryMode") {
- low_memory_mode_ = true;
- // TODO Might want to turn off must_relocate here.
- } else if (option == "-XX:UseTLAB") {
- use_tlab_ = true;
- } else if (option == "-XX:EnableHSpaceCompactForOOM") {
- use_homogeneous_space_compaction_for_oom_ = true;
- } else if (option == "-XX:DisableHSpaceCompactForOOM") {
- use_homogeneous_space_compaction_for_oom_ = false;
- } else if (StartsWith(option, "-D")) {
- properties_.push_back(option.substr(strlen("-D")));
- } else if (StartsWith(option, "-Xjnitrace:")) {
- jni_trace_ = option.substr(strlen("-Xjnitrace:"));
- } else if (option == "compilercallbacks") {
- compiler_callbacks_ =
- reinterpret_cast<CompilerCallbacks*>(const_cast<void*>(options[i].second));
- } else if (option == "imageinstructionset") {
- const char* isa_str = reinterpret_cast<const char*>(options[i].second);
- image_isa_ = GetInstructionSetFromString(isa_str);
- if (image_isa_ == kNone) {
- Usage("%s is not a valid instruction set.", isa_str);
- return false;
- }
- } else if (option == "-Xzygote") {
- is_zygote_ = true;
- } else if (StartsWith(option, "-Xpatchoat:")) {
- if (!ParseStringAfterChar(option, ':', &patchoat_executable_)) {
- return false;
- }
- } else if (option == "-Xrelocate") {
- must_relocate_ = true;
- } else if (option == "-Xnorelocate") {
- must_relocate_ = false;
- } else if (option == "-Xnodex2oat") {
- dex2oat_enabled_ = false;
- } else if (option == "-Xdex2oat") {
- dex2oat_enabled_ = true;
- } else if (option == "-Xnoimage-dex2oat") {
- image_dex2oat_enabled_ = false;
- } else if (option == "-Ximage-dex2oat") {
- image_dex2oat_enabled_ = true;
- } else if (option == "-Xint") {
- interpreter_only_ = true;
- } else if (StartsWith(option, "-Xgc:")) {
- if (!ParseXGcOption(option)) {
- return false;
- }
- } else if (StartsWith(option, "-XX:LargeObjectSpace=")) {
- std::string substring;
- if (!ParseStringAfterChar(option, '=', &substring)) {
- return false;
- }
- if (substring == "disabled") {
- large_object_space_type_ = gc::space::kLargeObjectSpaceTypeDisabled;
- } else if (substring == "freelist") {
- large_object_space_type_ = gc::space::kLargeObjectSpaceTypeFreeList;
- } else if (substring == "map") {
- large_object_space_type_ = gc::space::kLargeObjectSpaceTypeMap;
- } else {
- Usage("Unknown -XX:LargeObjectSpace= option %s\n", substring.c_str());
- return false;
- }
- } else if (StartsWith(option, "-XX:LargeObjectThreshold=")) {
- std::string substring;
- if (!ParseStringAfterChar(option, '=', &substring)) {
- return false;
- }
- size_t size = ParseMemoryOption(substring.c_str(), 1);
- if (size == 0) {
- Usage("Failed to parse memory option %s\n", option.c_str());
- return false;
- }
- large_object_threshold_ = size;
- } else if (StartsWith(option, "-XX:BackgroundGC=")) {
- std::string substring;
- if (!ParseStringAfterChar(option, '=', &substring)) {
- return false;
- }
- // Special handling for HSpaceCompact since this is only valid as a background GC type.
- if (substring == "HSpaceCompact") {
- background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact;
- } else {
- gc::CollectorType collector_type = ParseCollectorType(substring);
- if (collector_type != gc::kCollectorTypeNone) {
- background_collector_type_ = collector_type;
- } else {
- Usage("Unknown -XX:BackgroundGC option %s\n", substring.c_str());
- return false;
- }
- }
- } else if (option == "-XX:+DisableExplicitGC") {
- is_explicit_gc_disabled_ = true;
- } else if (StartsWith(option, "-verbose:")) {
- std::vector<std::string> verbose_options;
- Split(option.substr(strlen("-verbose:")), ',', &verbose_options);
- for (size_t j = 0; j < verbose_options.size(); ++j) {
- if (verbose_options[j] == "class") {
- gLogVerbosity.class_linker = true;
- } else if (verbose_options[j] == "compiler") {
- gLogVerbosity.compiler = true;
- } else if (verbose_options[j] == "gc") {
- gLogVerbosity.gc = true;
- } else if (verbose_options[j] == "heap") {
- gLogVerbosity.heap = true;
- } else if (verbose_options[j] == "jdwp") {
- gLogVerbosity.jdwp = true;
- } else if (verbose_options[j] == "jni") {
- gLogVerbosity.jni = true;
- } else if (verbose_options[j] == "monitor") {
- gLogVerbosity.monitor = true;
- } else if (verbose_options[j] == "profiler") {
- gLogVerbosity.profiler = true;
- } else if (verbose_options[j] == "signals") {
- gLogVerbosity.signals = true;
- } else if (verbose_options[j] == "startup") {
- gLogVerbosity.startup = true;
- } else if (verbose_options[j] == "third-party-jni") {
- gLogVerbosity.third_party_jni = true;
- } else if (verbose_options[j] == "threads") {
- gLogVerbosity.threads = true;
- } else if (verbose_options[j] == "verifier") {
- gLogVerbosity.verifier = true;
- } else {
- Usage("Unknown -verbose option %s\n", verbose_options[j].c_str());
- return false;
- }
- }
- } else if (StartsWith(option, "-Xlockprofthreshold:")) {
- if (!ParseUnsignedInteger(option, ':', &lock_profiling_threshold_)) {
- return false;
- }
- } else if (StartsWith(option, "-Xstacktracefile:")) {
- if (!ParseStringAfterChar(option, ':', &stack_trace_file_)) {
- return false;
- }
- } else if (option == "sensitiveThread") {
- const void* hook = options[i].second;
- hook_is_sensitive_thread_ = reinterpret_cast<bool (*)()>(const_cast<void*>(hook));
- } else if (option == "vfprintf") {
- const void* hook = options[i].second;
- if (hook == nullptr) {
- Usage("vfprintf argument was NULL");
- return false;
- }
- hook_vfprintf_ =
- reinterpret_cast<int (*)(FILE *, const char*, va_list)>(const_cast<void*>(hook));
- } else if (option == "exit") {
- const void* hook = options[i].second;
- if (hook == nullptr) {
- Usage("exit argument was NULL");
- return false;
- }
- hook_exit_ = reinterpret_cast<void(*)(jint)>(const_cast<void*>(hook));
- } else if (option == "abort") {
- const void* hook = options[i].second;
- if (hook == nullptr) {
- Usage("abort was NULL\n");
- return false;
- }
- hook_abort_ = reinterpret_cast<void(*)()>(const_cast<void*>(hook));
- } else if (option == "-Xmethod-trace") {
- method_trace_ = true;
- } else if (StartsWith(option, "-Xmethod-trace-file:")) {
- method_trace_file_ = option.substr(strlen("-Xmethod-trace-file:"));
- } else if (StartsWith(option, "-Xmethod-trace-file-size:")) {
- if (!ParseUnsignedInteger(option, ':', &method_trace_file_size_)) {
- return false;
- }
- } else if (option == "-Xprofile:threadcpuclock") {
- Trace::SetDefaultClockSource(kTraceClockSourceThreadCpu);
- } else if (option == "-Xprofile:wallclock") {
- Trace::SetDefaultClockSource(kTraceClockSourceWall);
- } else if (option == "-Xprofile:dualclock") {
- Trace::SetDefaultClockSource(kTraceClockSourceDual);
- } else if (option == "-Xenable-profiler") {
- profiler_options_.enabled_ = true;
- } else if (StartsWith(option, "-Xprofile-filename:")) {
- if (!ParseStringAfterChar(option, ':', &profile_output_filename_)) {
- return false;
- }
- } else if (StartsWith(option, "-Xprofile-period:")) {
- if (!ParseUnsignedInteger(option, ':', &profiler_options_.period_s_)) {
- return false;
- }
- } else if (StartsWith(option, "-Xprofile-duration:")) {
- if (!ParseUnsignedInteger(option, ':', &profiler_options_.duration_s_)) {
- return false;
- }
- } else if (StartsWith(option, "-Xprofile-interval:")) {
- if (!ParseUnsignedInteger(option, ':', &profiler_options_.interval_us_)) {
- return false;
- }
- } else if (StartsWith(option, "-Xprofile-backoff:")) {
- if (!ParseDouble(option, ':', 1.0, 10.0, &profiler_options_.backoff_coefficient_)) {
- return false;
- }
- } else if (option == "-Xprofile-start-immediately") {
- profiler_options_.start_immediately_ = true;
- } else if (StartsWith(option, "-Xprofile-top-k-threshold:")) {
- if (!ParseDouble(option, ':', 0.0, 100.0, &profiler_options_.top_k_threshold_)) {
- return false;
- }
- } else if (StartsWith(option, "-Xprofile-top-k-change-threshold:")) {
- if (!ParseDouble(option, ':', 0.0, 100.0, &profiler_options_.top_k_change_threshold_)) {
- return false;
- }
- } else if (option == "-Xprofile-type:method") {
- profiler_options_.profile_type_ = kProfilerMethod;
- } else if (option == "-Xprofile-type:stack") {
- profiler_options_.profile_type_ = kProfilerBoundedStack;
- } else if (StartsWith(option, "-Xprofile-max-stack-depth:")) {
- if (!ParseUnsignedInteger(option, ':', &profiler_options_.max_stack_depth_)) {
- return false;
- }
- } else if (StartsWith(option, "-Xcompiler:")) {
- if (!ParseStringAfterChar(option, ':', &compiler_executable_)) {
- return false;
- }
- } else if (option == "-Xcompiler-option") {
- i++;
- if (i == options.size()) {
- Usage("Missing required compiler option for %s\n", option.c_str());
- return false;
- }
- compiler_options_.push_back(options[i].first);
- } else if (option == "-Ximage-compiler-option") {
- i++;
- if (i == options.size()) {
- Usage("Missing required compiler option for %s\n", option.c_str());
- return false;
- }
- image_compiler_options_.push_back(options[i].first);
- } else if (StartsWith(option, "-Xverify:")) {
- std::string verify_mode = option.substr(strlen("-Xverify:"));
- if (verify_mode == "none") {
- verify_ = false;
- } else if (verify_mode == "remote" || verify_mode == "all") {
- verify_ = true;
- } else {
- Usage("Unknown -Xverify option %s\n", verify_mode.c_str());
- return false;
- }
- } else if (StartsWith(option, "-XX:NativeBridge=")) {
- if (!ParseStringAfterChar(option, '=', &native_bridge_library_filename_)) {
- return false;
- }
- } else if (StartsWith(option, "-ea") ||
- StartsWith(option, "-da") ||
- StartsWith(option, "-enableassertions") ||
- StartsWith(option, "-disableassertions") ||
- (option == "--runtime-arg") ||
- (option == "-esa") ||
- (option == "-dsa") ||
- (option == "-enablesystemassertions") ||
- (option == "-disablesystemassertions") ||
- (option == "-Xrs") ||
- StartsWith(option, "-Xint:") ||
- StartsWith(option, "-Xdexopt:") ||
- (option == "-Xnoquithandler") ||
- StartsWith(option, "-Xjnigreflimit:") ||
- (option == "-Xgenregmap") ||
- (option == "-Xnogenregmap") ||
- StartsWith(option, "-Xverifyopt:") ||
- (option == "-Xcheckdexsum") ||
- (option == "-Xincludeselectedop") ||
- StartsWith(option, "-Xjitop:") ||
- (option == "-Xincludeselectedmethod") ||
- StartsWith(option, "-Xjitthreshold:") ||
- StartsWith(option, "-Xjitcodecachesize:") ||
- (option == "-Xjitblocking") ||
- StartsWith(option, "-Xjitmethod:") ||
- StartsWith(option, "-Xjitclass:") ||
- StartsWith(option, "-Xjitoffset:") ||
- StartsWith(option, "-Xjitconfig:") ||
- (option == "-Xjitcheckcg") ||
- (option == "-Xjitverbose") ||
- (option == "-Xjitprofile") ||
- (option == "-Xjitdisableopt") ||
- (option == "-Xjitsuspendpoll") ||
- StartsWith(option, "-XX:mainThreadStackSize=")) {
- // Ignored for backwards compatibility.
- } else if (!ignore_unrecognized) {
- Usage("Unrecognized option %s\n", option.c_str());
+ } else if (parse_result.GetStatus() == CmdlineResult::kUnknown && !ignore_unrecognized) {
+ Usage("%s\n", parse_result.GetMessage().c_str());
return false;
+ } else {
+ Usage("%s\n", parse_result.GetMessage().c_str());
+ Exit(0);
+ }
+
+ UNREACHABLE();
+ return false;
+ }
+
+ using M = RuntimeArgumentMap;
+ RuntimeArgumentMap args = parser->ReleaseArgumentsMap();
+
+ // -help, -showversion, etc.
+ if (args.Exists(M::Help)) {
+ Usage(nullptr);
+ return false;
+ } else if (args.Exists(M::ShowVersion)) {
+ UsageMessage(stdout, "ART version %s\n", Runtime::GetVersion());
+ Exit(0);
+ } else if (args.Exists(M::BootClassPath)) {
+ LOG(INFO) << "setting boot class path to " << *args.Get(M::BootClassPath);
+ }
+
+ // Set a default boot class path if we didn't get an explicit one via command line.
+ if (getenv("BOOTCLASSPATH") != nullptr) {
+ args.SetIfMissing(M::BootClassPath, std::string(getenv("BOOTCLASSPATH")));
+ }
+
+ // Set a default class path if we didn't get an explicit one via command line.
+ if (getenv("CLASSPATH") != nullptr) {
+ args.SetIfMissing(M::ClassPath, std::string(getenv("CLASSPATH")));
+ }
+
+ // Default to number of processors minus one since the main GC thread also does work.
+ args.SetIfMissing(M::ParallelGCThreads,
+ static_cast<unsigned int>(sysconf(_SC_NPROCESSORS_CONF) - 1u));
+
+ // -Xverbose:
+ {
+ LogVerbosity *log_verbosity = args.Get(M::Verbose);
+ if (log_verbosity != nullptr) {
+ gLogVerbosity = *log_verbosity;
}
}
- // If not set, background collector type defaults to homogeneous compaction
- // if not low memory mode, semispace otherwise.
- if (background_collector_type_ == gc::kCollectorTypeNone) {
- background_collector_type_ = low_memory_mode_ ?
- gc::kCollectorTypeSS : gc::kCollectorTypeHomogeneousSpaceCompact;
+
+ // -Xprofile:
+ Trace::SetDefaultClockSource(args.GetOrDefault(M::ProfileClock));
+
+ if (!ProcessSpecialOptions(options, &args, nullptr)) {
+ return false;
+ }
+
+ {
+ // If not set, background collector type defaults to homogeneous compaction.
+ // If foreground is GSS, use GSS as background collector.
+ // If not low memory mode, semispace otherwise.
+
+ gc::CollectorType background_collector_type_;
+ gc::CollectorType collector_type_ = (XGcOption{}).collector_type_; // NOLINT [whitespace/braces] [5]
+ bool low_memory_mode_ = args.Exists(M::LowMemoryMode);
+
+ background_collector_type_ = args.GetOrDefault(M::BackgroundGc);
+ {
+ XGcOption* xgc = args.Get(M::GcOption);
+ if (xgc != nullptr && xgc->collector_type_ != gc::kCollectorTypeNone) {
+ collector_type_ = xgc->collector_type_;
+ }
+ }
+
+ if (background_collector_type_ == gc::kCollectorTypeNone) {
+ if (collector_type_ != gc::kCollectorTypeGSS) {
+ background_collector_type_ = low_memory_mode_ ?
+ gc::kCollectorTypeSS : gc::kCollectorTypeHomogeneousSpaceCompact;
+ } else {
+ background_collector_type_ = collector_type_;
+ }
+ }
+
+ args.Set(M::BackgroundGc, BackgroundGcOption { background_collector_type_ });
}
// If a reference to the dalvik core.jar snuck in, replace it with
@@ -710,23 +478,45 @@
std::string core_jar("/core-hostdex.jar");
std::string core_libart_jar("/core-libart-hostdex.jar");
#endif
- size_t core_jar_pos = boot_class_path_string_.find(core_jar);
+ auto boot_class_path_string = args.GetOrDefault(M::BootClassPath);
+
+ size_t core_jar_pos = boot_class_path_string.find(core_jar);
if (core_jar_pos != std::string::npos) {
- boot_class_path_string_.replace(core_jar_pos, core_jar.size(), core_libart_jar);
+ boot_class_path_string.replace(core_jar_pos, core_jar.size(), core_libart_jar);
+ args.Set(M::BootClassPath, boot_class_path_string);
}
- if (compiler_callbacks_ == nullptr && image_.empty()) {
- image_ += GetAndroidRoot();
- image_ += "/framework/boot.art";
+ {
+ auto&& boot_class_path = args.GetOrDefault(M::BootClassPath);
+ auto&& boot_class_path_locations = args.GetOrDefault(M::BootClassPathLocations);
+ if (args.Exists(M::BootClassPathLocations)) {
+ size_t boot_class_path_count = ParseStringList<':'>::Split(boot_class_path).Size();
+
+ if (boot_class_path_count != boot_class_path_locations.Size()) {
+ Usage("The number of boot class path files does not match"
+ " the number of boot class path locations given\n"
+ " boot class path files (%zu): %s\n"
+ " boot class path locations (%zu): %s\n",
+ boot_class_path.size(), boot_class_path_string.c_str(),
+ boot_class_path_locations.Size(), boot_class_path_locations.Join().c_str());
+ return false;
+ }
+ }
}
- if (heap_growth_limit_ == 0) {
- heap_growth_limit_ = heap_maximum_size_;
+
+ if (!args.Exists(M::CompilerCallbacksPtr) && !args.Exists(M::Image)) {
+ std::string image = GetAndroidRoot();
+ image += "/framework/boot.art";
+ args.Set(M::Image, image);
}
- if (background_collector_type_ == gc::kCollectorTypeNone) {
- background_collector_type_ = collector_type_;
+
+ if (args.GetOrDefault(M::HeapGrowthLimit) == 0u) { // 0 means no growth limit
+ args.Set(M::HeapGrowthLimit, args.GetOrDefault(M::MemoryMaximumSize));
}
+
+ *runtime_options = std::move(args);
return true;
-} // NOLINT(readability/fn_size)
+}
void ParsedOptions::Exit(int status) {
hook_exit_(status);
@@ -802,6 +592,8 @@
UsageMessage(stream, " -Xgc:[no]postverify_rosalloc\n");
UsageMessage(stream, " -Xgc:[no]presweepingverify\n");
UsageMessage(stream, " -Ximage:filename\n");
+ UsageMessage(stream, " -Xbootclasspath-locations:bootclasspath\n"
+ " (override the dex locations of the -Xbootclasspath files)\n");
UsageMessage(stream, " -XX:+DisableExplicitGC\n");
UsageMessage(stream, " -XX:ParallelGCThreads=integervalue\n");
UsageMessage(stream, " -XX:ConcGCThreads=integervalue\n");
@@ -877,73 +669,4 @@
Exit((error) ? 1 : 0);
}
-bool ParsedOptions::ParseStringAfterChar(const std::string& s, char c, std::string* parsed_value) {
- std::string::size_type colon = s.find(c);
- if (colon == std::string::npos) {
- Usage("Missing char %c in option %s\n", c, s.c_str());
- return false;
- }
- // Add one to remove the char we were trimming until.
- *parsed_value = s.substr(colon + 1);
- return true;
-}
-
-bool ParsedOptions::ParseInteger(const std::string& s, char after_char, int* parsed_value) {
- std::string::size_type colon = s.find(after_char);
- if (colon == std::string::npos) {
- Usage("Missing char %c in option %s\n", after_char, s.c_str());
- return false;
- }
- const char* begin = &s[colon + 1];
- char* end;
- size_t result = strtoul(begin, &end, 10);
- if (begin == end || *end != '\0') {
- Usage("Failed to parse integer from %s\n", s.c_str());
- return false;
- }
- *parsed_value = result;
- return true;
-}
-
-bool ParsedOptions::ParseUnsignedInteger(const std::string& s, char after_char,
- unsigned int* parsed_value) {
- int i;
- if (!ParseInteger(s, after_char, &i)) {
- return false;
- }
- if (i < 0) {
- Usage("Negative value %d passed for unsigned option %s\n", i, s.c_str());
- return false;
- }
- *parsed_value = i;
- return true;
-}
-
-bool ParsedOptions::ParseDouble(const std::string& option, char after_char,
- double min, double max, double* parsed_value) {
- std::string substring;
- if (!ParseStringAfterChar(option, after_char, &substring)) {
- return false;
- }
- bool sane_val = true;
- double value;
- if ((false)) {
- // TODO: this doesn't seem to work on the emulator. b/15114595
- std::stringstream iss(substring);
- iss >> value;
- // Ensure that we have a value, there was no cruft after it and it satisfies a sensible range.
- sane_val = iss.eof() && (value >= min) && (value <= max);
- } else {
- char* end = nullptr;
- value = strtod(substring.c_str(), &end);
- sane_val = *end == '\0' && value >= min && value <= max;
- }
- if (!sane_val) {
- Usage("Invalid double value %s for option %s\n", substring.c_str(), option.c_str());
- return false;
- }
- *parsed_value = value;
- return true;
-}
-
} // namespace art
diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h
index 9294868..529dd5c 100644
--- a/runtime/parsed_options.h
+++ b/runtime/parsed_options.h
@@ -27,92 +27,44 @@
#include "gc/space/large_object_space.h"
#include "arch/instruction_set.h"
#include "profiler_options.h"
+#include "runtime_options.h"
namespace art {
class CompilerCallbacks;
class DexFile;
+struct RuntimeArgumentMap;
typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions;
+template <typename TVariantMap,
+ template <typename TKeyValue> class TVariantMapKey>
+struct CmdlineParser;
+
class ParsedOptions {
public:
- // returns null if problem parsing and ignore_unrecognized is false
- static ParsedOptions* Create(const RuntimeOptions& options, bool ignore_unrecognized);
+ using RuntimeParser = CmdlineParser<RuntimeArgumentMap, RuntimeArgumentMap::Key>;
+ // Create a parser that can turn user-defined input into a RuntimeArgumentMap.
+ // This visibility is effectively for testing-only, and normal code does not
+ // need to create its own parser.
+ static std::unique_ptr<RuntimeParser> MakeParser(bool ignore_unrecognized);
- const std::vector<const DexFile*>* boot_class_path_;
- std::string boot_class_path_string_;
- std::string class_path_string_;
- std::string image_;
- bool check_jni_;
- bool force_copy_;
- std::string jni_trace_;
- std::string native_bridge_library_filename_;
- CompilerCallbacks* compiler_callbacks_;
- bool is_zygote_;
- bool must_relocate_;
- bool dex2oat_enabled_;
- bool image_dex2oat_enabled_;
- std::string patchoat_executable_;
- bool interpreter_only_;
- bool is_explicit_gc_disabled_;
- bool use_tlab_;
- bool verify_pre_gc_heap_;
- bool verify_pre_sweeping_heap_;
- bool verify_post_gc_heap_;
- bool verify_pre_gc_rosalloc_;
- bool verify_pre_sweeping_rosalloc_;
- bool verify_post_gc_rosalloc_;
- unsigned int long_pause_log_threshold_;
- unsigned int long_gc_log_threshold_;
- bool dump_gc_performance_on_shutdown_;
- bool ignore_max_footprint_;
- size_t heap_initial_size_;
- size_t heap_maximum_size_;
- size_t heap_growth_limit_;
- size_t heap_min_free_;
- size_t heap_max_free_;
- size_t heap_non_moving_space_capacity_;
- gc::space::LargeObjectSpaceType large_object_space_type_;
- size_t large_object_threshold_;
- double heap_target_utilization_;
- double foreground_heap_growth_multiplier_;
- unsigned int parallel_gc_threads_;
- unsigned int conc_gc_threads_;
- gc::CollectorType collector_type_;
- gc::CollectorType background_collector_type_;
- size_t stack_size_;
- unsigned int max_spins_before_thin_lock_inflation_;
- bool low_memory_mode_;
- unsigned int lock_profiling_threshold_;
- std::string stack_trace_file_;
- bool method_trace_;
- std::string method_trace_file_;
- unsigned int method_trace_file_size_;
+ // returns true if parsing succeeds, and stores the resulting options into runtime_options
+ static ParsedOptions* Create(const RuntimeOptions& options, bool ignore_unrecognized,
+ RuntimeArgumentMap* runtime_options);
+
bool (*hook_is_sensitive_thread_)();
jint (*hook_vfprintf_)(FILE* stream, const char* format, va_list ap);
void (*hook_exit_)(jint status);
void (*hook_abort_)();
- std::vector<std::string> properties_;
- std::string compiler_executable_;
- std::vector<std::string> compiler_options_;
- std::vector<std::string> image_compiler_options_;
- ProfilerOptions profiler_options_;
- std::string profile_output_filename_;
- TraceClockSource profile_clock_source_;
- bool verify_;
- InstructionSet image_isa_;
-
- // Whether or not we use homogeneous space compaction to avoid OOM errors. If enabled,
- // the heap will attempt to create an extra space which enables compacting from a malloc space to
- // another malloc space when we are about to throw OOM.
- bool use_homogeneous_space_compaction_for_oom_;
- // Minimal interval allowed between two homogeneous space compactions caused by OOM.
- uint64_t min_interval_homogeneous_space_compaction_by_oom_;
private:
ParsedOptions();
+ bool ProcessSpecialOptions(const RuntimeOptions& options,
+ RuntimeArgumentMap* runtime_options,
+ std::vector<std::string>* out_options);
+
void Usage(const char* fmt, ...);
void UsageMessage(FILE* stream, const char* fmt, ...);
void UsageMessageV(FILE* stream, const char* fmt, va_list ap);
@@ -120,13 +72,8 @@
void Exit(int status);
void Abort();
- bool Parse(const RuntimeOptions& options, bool ignore_unrecognized);
- bool ParseXGcOption(const std::string& option);
- bool ParseStringAfterChar(const std::string& option, char after_char, std::string* parsed_value);
- bool ParseInteger(const std::string& option, char after_char, int* parsed_value);
- bool ParseUnsignedInteger(const std::string& option, char after_char, unsigned int* parsed_value);
- bool ParseDouble(const std::string& option, char after_char, double min, double max,
- double* parsed_value);
+ bool Parse(const RuntimeOptions& options, bool ignore_unrecognized,
+ RuntimeArgumentMap* runtime_options);
};
} // namespace art
diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc
index 61481b1..79dc2fa 100644
--- a/runtime/parsed_options_test.cc
+++ b/runtime/parsed_options_test.cc
@@ -22,7 +22,12 @@
namespace art {
-class ParsedOptionsTest : public CommonRuntimeTest {};
+class ParsedOptionsTest : public ::testing::Test {
+ public:
+ static void SetUpTestCase() {
+ CommonRuntimeTest::SetUpAndroidRoot();
+ }
+};
TEST_F(ParsedOptionsTest, ParsedOptions) {
void* test_vfprintf = reinterpret_cast<void*>(0xa);
@@ -30,7 +35,7 @@
void* test_exit = reinterpret_cast<void*>(0xc);
void* null = reinterpret_cast<void*>(NULL);
- std::string lib_core(GetLibCoreDexFileName());
+ std::string lib_core(CommonRuntimeTest::GetLibCoreDexFileName());
std::string boot_class_path;
boot_class_path += "-Xbootclasspath:";
@@ -54,20 +59,28 @@
options.push_back(std::make_pair("vfprintf", test_vfprintf));
options.push_back(std::make_pair("abort", test_abort));
options.push_back(std::make_pair("exit", test_exit));
- std::unique_ptr<ParsedOptions> parsed(ParsedOptions::Create(options, false));
- ASSERT_TRUE(parsed.get() != NULL);
- EXPECT_EQ(lib_core, parsed->boot_class_path_string_);
- EXPECT_EQ(lib_core, parsed->class_path_string_);
- EXPECT_EQ(std::string("boot_image"), parsed->image_);
- EXPECT_EQ(true, parsed->check_jni_);
- EXPECT_EQ(2048U, parsed->heap_initial_size_);
- EXPECT_EQ(4 * KB, parsed->heap_maximum_size_);
- EXPECT_EQ(1 * MB, parsed->stack_size_);
- EXPECT_DOUBLE_EQ(0.75, parsed->heap_target_utilization_);
- EXPECT_TRUE(test_vfprintf == parsed->hook_vfprintf_);
- EXPECT_TRUE(test_exit == parsed->hook_exit_);
- EXPECT_TRUE(test_abort == parsed->hook_abort_);
+ RuntimeArgumentMap map;
+ std::unique_ptr<ParsedOptions> parsed(ParsedOptions::Create(options, false, &map));
+ ASSERT_TRUE(parsed.get() != NULL);
+ ASSERT_NE(0u, map.Size());
+
+ using Opt = RuntimeArgumentMap;
+
+#define EXPECT_PARSED_EQ(expected, actual_key) EXPECT_EQ(expected, map.GetOrDefault(actual_key))
+#define EXPECT_PARSED_EXISTS(actual_key) EXPECT_TRUE(map.Exists(actual_key))
+
+ EXPECT_PARSED_EQ(lib_core, Opt::BootClassPath);
+ EXPECT_PARSED_EQ(lib_core, Opt::ClassPath);
+ EXPECT_PARSED_EQ(std::string("boot_image"), Opt::Image);
+ EXPECT_PARSED_EXISTS(Opt::CheckJni);
+ EXPECT_PARSED_EQ(2048U, Opt::MemoryInitialSize);
+ EXPECT_PARSED_EQ(4 * KB, Opt::MemoryMaximumSize);
+ EXPECT_PARSED_EQ(1 * MB, Opt::StackSize);
+ EXPECT_DOUBLE_EQ(0.75, map.GetOrDefault(Opt::HeapTargetUtilization));
+ EXPECT_TRUE(test_vfprintf == map.GetOrDefault(Opt::HookVfprintf));
+ EXPECT_TRUE(test_exit == map.GetOrDefault(Opt::HookExit));
+ EXPECT_TRUE(test_abort == map.GetOrDefault(Opt::HookAbort));
EXPECT_TRUE(VLOG_IS_ON(class_linker));
EXPECT_FALSE(VLOG_IS_ON(compiler));
EXPECT_FALSE(VLOG_IS_ON(heap));
@@ -78,9 +91,11 @@
EXPECT_FALSE(VLOG_IS_ON(startup));
EXPECT_FALSE(VLOG_IS_ON(third_party_jni));
EXPECT_FALSE(VLOG_IS_ON(threads));
- ASSERT_EQ(2U, parsed->properties_.size());
- EXPECT_EQ("foo=bar", parsed->properties_[0]);
- EXPECT_EQ("baz=qux", parsed->properties_[1]);
+
+ auto&& properties_list = map.GetOrDefault(Opt::PropertiesList);
+ ASSERT_EQ(2U, properties_list.size());
+ EXPECT_EQ("foo=bar", properties_list[0]);
+ EXPECT_EQ("baz=qux", properties_list[1]);
}
} // namespace art
diff --git a/runtime/primitive.cc b/runtime/primitive.cc
index a639f93..d29a060 100644
--- a/runtime/primitive.cc
+++ b/runtime/primitive.cc
@@ -31,6 +31,11 @@
"PrimVoid",
};
+const char* Primitive::PrettyDescriptor(Primitive::Type type) {
+ CHECK(Primitive::kPrimNot <= type && type <= Primitive::kPrimVoid) << static_cast<int>(type);
+ return kTypeNames[type];
+}
+
std::ostream& operator<<(std::ostream& os, const Primitive::Type& type) {
int32_t int_type = static_cast<int32_t>(type);
if (type >= Primitive::kPrimNot && type <= Primitive::kPrimVoid) {
diff --git a/runtime/primitive.h b/runtime/primitive.h
index afcc64d..9dda144 100644
--- a/runtime/primitive.h
+++ b/runtime/primitive.h
@@ -146,6 +146,29 @@
}
}
+ static const char* PrettyDescriptor(Type type);
+
+ static bool IsFloatingPointType(Type type) {
+ return type == kPrimFloat || type == kPrimDouble;
+ }
+
+ static bool IsIntegralType(Type type) {
+ switch (type) {
+ case kPrimByte:
+ case kPrimChar:
+ case kPrimShort:
+ case kPrimInt:
+ case kPrimLong:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static bool Is64BitType(Type type) {
+ return type == kPrimLong || type == kPrimDouble;
+ }
+
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Primitive);
};
diff --git a/runtime/profiler.cc b/runtime/profiler.cc
index e399195..c3bdcb1 100644
--- a/runtime/profiler.cc
+++ b/runtime/profiler.cc
@@ -40,13 +40,7 @@
#include "thread.h"
#include "thread_list.h"
-#ifdef HAVE_ANDROID_OS
-#include "cutils/properties.h"
-#endif
-
-#if !defined(ART_USE_PORTABLE_COMPILER)
#include "entrypoints/quick/quick_entrypoints.h"
-#endif
namespace art {
diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc
index 3415e8f..d65b2d5 100644
--- a/runtime/quick/inline_method_analyser.cc
+++ b/runtime/quick/inline_method_analyser.cc
@@ -15,6 +15,7 @@
*/
#include "inline_method_analyser.h"
+#include "dex_file-inl.h"
#include "dex_instruction.h"
#include "dex_instruction-inl.h"
#include "mirror/art_field.h"
diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h
index 72b696b..3463025 100644
--- a/runtime/quick/inline_method_analyser.h
+++ b/runtime/quick/inline_method_analyser.h
@@ -103,6 +103,9 @@
kIntrinsicFlagIsObject = 4,
// kIntrinsicUnsafePut
kIntrinsicFlagIsOrdered = 8,
+
+ // kIntrinsicDoubleCvt, kIntrinsicFloatCvt.
+ kIntrinsicFlagToFloatingPoint = kIntrinsicFlagMin,
};
struct InlineIGetIPutData {
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 90c9fe7..34f6713 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -204,9 +204,7 @@
CHECK(code_item != nullptr);
uint16_t num_regs = code_item->registers_size_;
uint32_t dex_pc = GetDexPc();
- const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
- uint32_t new_dex_pc = dex_pc + inst->SizeInCodeUnits();
- ShadowFrame* new_frame = ShadowFrame::Create(num_regs, nullptr, m, new_dex_pc);
+ ShadowFrame* new_frame = ShadowFrame::Create(num_regs, nullptr, m, dex_pc);
StackHandleScope<3> hs(self_);
mirror::Class* declaring_class = m->GetDeclaringClass();
Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
@@ -214,7 +212,7 @@
Handle<mirror::ArtMethod> h_method(hs.NewHandle(m));
verifier::MethodVerifier verifier(self_, h_dex_cache->GetDexFile(), h_dex_cache, h_class_loader,
&m->GetClassDef(), code_item, m->GetDexMethodIndex(),
- h_method, m->GetAccessFlags(), false, true, true);
+ h_method, m->GetAccessFlags(), false, true, true, true);
verifier.Verify();
const std::vector<int32_t> kinds(verifier.DescribeVRegs(dex_pc));
for (uint16_t reg = 0; reg < num_regs; ++reg) {
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index cf1ecbf..31622de 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -32,7 +32,7 @@
class ThrowLocation;
class ShadowFrame;
-// Manages exception delivery for Quick backend. Not used by Portable backend.
+// Manages exception delivery for Quick backend.
class QuickExceptionHandler {
public:
QuickExceptionHandler(Thread* self, bool is_deoptimization)
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index 0dc31e7..c74fded 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -19,43 +19,147 @@
#include "read_barrier.h"
+#include "gc/collector/concurrent_copying.h"
+#include "gc/heap.h"
#include "mirror/object_reference.h"
+#include "mirror/reference.h"
+#include "runtime.h"
namespace art {
-template <typename MirrorType, ReadBarrierOption kReadBarrierOption>
+template <typename MirrorType, ReadBarrierOption kReadBarrierOption, bool kMaybeDuringStartup>
inline MirrorType* ReadBarrier::Barrier(
mirror::Object* obj, MemberOffset offset, mirror::HeapReference<MirrorType>* ref_addr) {
- // Unused for now.
- UNUSED(obj, offset, ref_addr);
const bool with_read_barrier = kReadBarrierOption == kWithReadBarrier;
if (with_read_barrier && kUseBakerReadBarrier) {
- // To be implemented.
- return ref_addr->AsMirrorPtr();
+ // The higher bits of the rb ptr, rb_ptr_high_bits (must be zero)
+ // is used to create artificial data dependency from the is_gray
+ // load to the ref field (ptr) load to avoid needing a load-load
+ // barrier between the two.
+ uintptr_t rb_ptr_high_bits;
+ bool is_gray = HasGrayReadBarrierPointer(obj, &rb_ptr_high_bits);
+ ref_addr = reinterpret_cast<mirror::HeapReference<MirrorType>*>(
+ rb_ptr_high_bits | reinterpret_cast<uintptr_t>(ref_addr));
+ MirrorType* ref = ref_addr->AsMirrorPtr();
+ if (is_gray) {
+ // Slow-path.
+ ref = reinterpret_cast<MirrorType*>(Mark(ref));
+ }
+ if (kEnableReadBarrierInvariantChecks) {
+ CHECK_EQ(rb_ptr_high_bits, 0U) << obj << " rb_ptr=" << obj->GetReadBarrierPointer();
+ }
+ AssertToSpaceInvariant(obj, offset, ref);
+ return ref;
} else if (with_read_barrier && kUseBrooksReadBarrier) {
// To be implemented.
return ref_addr->AsMirrorPtr();
+ } else if (with_read_barrier && kUseTableLookupReadBarrier) {
+ MirrorType* ref = ref_addr->AsMirrorPtr();
+ MirrorType* old_ref = ref;
+ // The heap or the collector can be null at startup. TODO: avoid the need for this null check.
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ if (heap != nullptr && heap->GetReadBarrierTable()->IsSet(old_ref)) {
+ ref = reinterpret_cast<MirrorType*>(Mark(old_ref));
+ // Update the field atomically. This may fail if mutator updates before us, but it's ok.
+ obj->CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier<false, false>(
+ offset, old_ref, ref);
+ }
+ AssertToSpaceInvariant(obj, offset, ref);
+ return ref;
} else {
// No read barrier.
return ref_addr->AsMirrorPtr();
}
}
-template <typename MirrorType, ReadBarrierOption kReadBarrierOption>
+template <typename MirrorType, ReadBarrierOption kReadBarrierOption, bool kMaybeDuringStartup>
inline MirrorType* ReadBarrier::BarrierForRoot(MirrorType** root) {
MirrorType* ref = *root;
const bool with_read_barrier = kReadBarrierOption == kWithReadBarrier;
if (with_read_barrier && kUseBakerReadBarrier) {
- // To be implemented.
+ if (kMaybeDuringStartup && IsDuringStartup()) {
+ // During startup, the heap may not be initialized yet. Just
+ // return the given ref.
+ return ref;
+ }
+ // TODO: separate the read barrier code from the collector code more.
+ if (Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->IsMarking()) {
+ ref = reinterpret_cast<MirrorType*>(Mark(ref));
+ }
+ AssertToSpaceInvariant(nullptr, MemberOffset(0), ref);
return ref;
} else if (with_read_barrier && kUseBrooksReadBarrier) {
// To be implemented.
return ref;
+ } else if (with_read_barrier && kUseTableLookupReadBarrier) {
+ if (kMaybeDuringStartup && IsDuringStartup()) {
+ // During startup, the heap may not be initialized yet. Just
+ // return the given ref.
+ return ref;
+ }
+ if (Runtime::Current()->GetHeap()->GetReadBarrierTable()->IsSet(ref)) {
+ MirrorType* old_ref = ref;
+ ref = reinterpret_cast<MirrorType*>(Mark(old_ref));
+ // Update the field atomically. This may fail if mutator updates before us, but it's ok.
+ Atomic<mirror::Object*>* atomic_root = reinterpret_cast<Atomic<mirror::Object*>*>(root);
+ atomic_root->CompareExchangeStrongSequentiallyConsistent(old_ref, ref);
+ }
+ AssertToSpaceInvariant(nullptr, MemberOffset(0), ref);
+ return ref;
} else {
return ref;
}
}
+inline bool ReadBarrier::IsDuringStartup() {
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ if (heap == nullptr) {
+ // During startup, the heap can be null.
+ return true;
+ }
+ if (heap->CurrentCollectorType() != gc::kCollectorTypeCC) {
+ // CC isn't running.
+ return true;
+ }
+ gc::collector::ConcurrentCopying* collector = heap->ConcurrentCopyingCollector();
+ if (collector == nullptr) {
+ // During startup, the collector can be null.
+ return true;
+ }
+ return false;
+}
+
+inline void ReadBarrier::AssertToSpaceInvariant(mirror::Object* obj, MemberOffset offset,
+ mirror::Object* ref) {
+ if (kEnableToSpaceInvariantChecks || kIsDebugBuild) {
+ if (ref == nullptr || IsDuringStartup()) {
+ return;
+ }
+ Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->
+ AssertToSpaceInvariant(obj, offset, ref);
+ }
+}
+
+inline mirror::Object* ReadBarrier::Mark(mirror::Object* obj) {
+ return Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->Mark(obj);
+}
+
+inline bool ReadBarrier::HasGrayReadBarrierPointer(mirror::Object* obj,
+ uintptr_t* out_rb_ptr_high_bits) {
+ mirror::Object* rb_ptr = obj->GetReadBarrierPointer();
+ uintptr_t rb_ptr_bits = reinterpret_cast<uintptr_t>(rb_ptr);
+ uintptr_t rb_ptr_low_bits = rb_ptr_bits & rb_ptr_mask_;
+ if (kEnableReadBarrierInvariantChecks) {
+ CHECK(rb_ptr_low_bits == white_ptr_ || rb_ptr_low_bits == gray_ptr_ ||
+ rb_ptr_low_bits == black_ptr_)
+ << "obj=" << obj << " rb_ptr=" << rb_ptr << " " << PrettyTypeOf(obj);
+ }
+ bool is_gray = rb_ptr_low_bits == gray_ptr_;
+ // The high bits are supposed to be zero. We check this on the caller side.
+ *out_rb_ptr_high_bits = rb_ptr_bits & ~rb_ptr_mask_;
+ return is_gray;
+}
+
} // namespace art
#endif // ART_RUNTIME_READ_BARRIER_INL_H_
diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h
index ed5db4e..474b46f 100644
--- a/runtime/read_barrier.h
+++ b/runtime/read_barrier.h
@@ -19,6 +19,7 @@
#include "base/mutex.h"
#include "base/macros.h"
+#include "jni.h"
#include "offsets.h"
#include "read_barrier_c.h"
@@ -26,25 +27,70 @@
// which needs to be a C header file for asm_support.h.
namespace art {
+
namespace mirror {
+ class ArtField;
+ class ArtMethod;
class Object;
template<typename MirrorType> class HeapReference;
} // namespace mirror
class ReadBarrier {
public:
+ // TODO: disable thse flags for production use.
+ // Enable the to-space invariant checks.
+ static constexpr bool kEnableToSpaceInvariantChecks = true;
+ // Enable the read barrier checks.
+ static constexpr bool kEnableReadBarrierInvariantChecks = true;
+
// It's up to the implementation whether the given field gets
// updated whereas the return value must be an updated reference.
- template <typename MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ template <typename MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier,
+ bool kMaybeDuringStartup = false>
ALWAYS_INLINE static MirrorType* Barrier(
mirror::Object* obj, MemberOffset offset, mirror::HeapReference<MirrorType>* ref_addr)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// It's up to the implementation whether the given root gets updated
// whereas the return value must be an updated reference.
- template <typename MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ template <typename MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier,
+ bool kMaybeDuringStartup = false>
ALWAYS_INLINE static MirrorType* BarrierForRoot(MirrorType** root)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ static bool IsDuringStartup();
+
+ // Without the holder object.
+ static void AssertToSpaceInvariant(mirror::Object* ref)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ AssertToSpaceInvariant(nullptr, MemberOffset(0), ref);
+ }
+ // With the holder object.
+ static void AssertToSpaceInvariant(mirror::Object* obj, MemberOffset offset,
+ mirror::Object* ref)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ static mirror::Object* Mark(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ static mirror::Object* WhitePtr() {
+ return reinterpret_cast<mirror::Object*>(white_ptr_);
+ }
+ static mirror::Object* GrayPtr() {
+ return reinterpret_cast<mirror::Object*>(gray_ptr_);
+ }
+ static mirror::Object* BlackPtr() {
+ return reinterpret_cast<mirror::Object*>(black_ptr_);
+ }
+
+ ALWAYS_INLINE static bool HasGrayReadBarrierPointer(mirror::Object* obj,
+ uintptr_t* out_rb_ptr_high_bits)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Note: These couldn't be constexpr pointers as reinterpret_cast isn't compatible with them.
+ static constexpr uintptr_t white_ptr_ = 0x0; // Not marked.
+ static constexpr uintptr_t gray_ptr_ = 0x1; // Marked, but not marked through. On mark stack.
+ static constexpr uintptr_t black_ptr_ = 0x2; // Marked through. Used for non-moving objects.
+ static constexpr uintptr_t rb_ptr_mask_ = 0x3; // The low 2 bits for white|gray|black.
};
} // namespace art
diff --git a/runtime/read_barrier_c.h b/runtime/read_barrier_c.h
index 1385c60..49efaa2 100644
--- a/runtime/read_barrier_c.h
+++ b/runtime/read_barrier_c.h
@@ -22,10 +22,14 @@
// include globals.h.
// Uncomment one of the following two and the two fields in
-// Object.java (libcore) to enable baker or brooks pointers.
+// Object.java (libcore) to enable baker, brooks (unimplemented), or
+// table-lookup read barriers.
+#ifdef ART_USE_READ_BARRIER
// #define USE_BAKER_READ_BARRIER
// #define USE_BROOKS_READ_BARRIER
+#define USE_TABLE_LOOKUP_READ_BARRIER
+#endif
#if defined(USE_BAKER_READ_BARRIER) || defined(USE_BROOKS_READ_BARRIER)
#define USE_BAKER_OR_BROOKS_READ_BARRIER
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index 01c5070..357d454 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -71,43 +71,6 @@
return obj->AsArray()->GetLength();
}
-struct ObjectComparator {
- bool operator()(GcRoot<mirror::Object> root1, GcRoot<mirror::Object> root2)
- // TODO: enable analysis when analysis can work with the STL.
- NO_THREAD_SAFETY_ANALYSIS {
- Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
- mirror::Object* obj1 = root1.Read<kWithoutReadBarrier>();
- mirror::Object* obj2 = root2.Read<kWithoutReadBarrier>();
- // Ensure null references and cleared jweaks appear at the end.
- if (obj1 == NULL) {
- return true;
- } else if (obj2 == NULL) {
- return false;
- }
- Runtime* runtime = Runtime::Current();
- if (runtime->IsClearedJniWeakGlobal(obj1)) {
- return true;
- } else if (runtime->IsClearedJniWeakGlobal(obj2)) {
- return false;
- }
-
- // Sort by class...
- if (obj1->GetClass() != obj2->GetClass()) {
- return obj1->GetClass()->IdentityHashCode() < obj2->IdentityHashCode();
- } else {
- // ...then by size...
- size_t count1 = obj1->SizeOf();
- size_t count2 = obj2->SizeOf();
- if (count1 != count2) {
- return count1 < count2;
- } else {
- // ...and finally by identity hash code.
- return obj1->IdentityHashCode() < obj2->IdentityHashCode();
- }
- }
- }
-};
-
// Log an object with some additional info.
//
// Pass in the number of elements in the array (or 0 if this is not an
@@ -153,6 +116,38 @@
}
void ReferenceTable::Dump(std::ostream& os, Table& entries) {
+ // Compare GC roots, first by class, then size, then address.
+ struct GcRootComparator {
+ bool operator()(GcRoot<mirror::Object> root1, GcRoot<mirror::Object> root2) const
+ // TODO: enable analysis when analysis can work with the STL.
+ NO_THREAD_SAFETY_ANALYSIS {
+ Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+ // These GC roots are already forwarded in ReferenceTable::Dump. We sort by class since there
+ // are no suspend points which can happen during the sorting process. This works since
+ // we are guaranteed that the addresses of obj1, obj2, obj1->GetClass, obj2->GetClass wont
+ // change during the sorting process. The classes are forwarded by ref->GetClass().
+ mirror::Object* obj1 = root1.Read<kWithoutReadBarrier>();
+ mirror::Object* obj2 = root2.Read<kWithoutReadBarrier>();
+ DCHECK(obj1 != nullptr);
+ DCHECK(obj2 != nullptr);
+ Runtime* runtime = Runtime::Current();
+ DCHECK(!runtime->IsClearedJniWeakGlobal(obj1));
+ DCHECK(!runtime->IsClearedJniWeakGlobal(obj2));
+ // Sort by class...
+ if (obj1->GetClass() != obj2->GetClass()) {
+ return obj1->GetClass() < obj2->GetClass();
+ }
+ // ...then by size...
+ const size_t size1 = obj1->SizeOf();
+ const size_t size2 = obj2->SizeOf();
+ if (size1 != size2) {
+ return size1 < size2;
+ }
+ // ...and finally by address.
+ return obj1 < obj2;
+ }
+ };
+
if (entries.empty()) {
os << " (empty)\n";
return;
@@ -166,16 +161,17 @@
first = 0;
}
os << " Last " << (count - first) << " entries (of " << count << "):\n";
+ Runtime* runtime = Runtime::Current();
for (int idx = count - 1; idx >= first; --idx) {
mirror::Object* ref = entries[idx].Read();
- if (ref == NULL) {
+ if (ref == nullptr) {
continue;
}
- if (Runtime::Current()->IsClearedJniWeakGlobal(ref)) {
+ if (runtime->IsClearedJniWeakGlobal(ref)) {
os << StringPrintf(" %5d: cleared jweak\n", idx);
continue;
}
- if (ref->GetClass() == NULL) {
+ if (ref->GetClass() == nullptr) {
// should only be possible right after a plain dvmMalloc().
size_t size = ref->SizeOf();
os << StringPrintf(" %5d: %p (raw) (%zd bytes)\n", idx, ref, size);
@@ -189,7 +185,7 @@
if (element_count != 0) {
StringAppendF(&extras, " (%zd elements)", element_count);
} else if (ref->GetClass()->IsStringClass()) {
- mirror::String* s = const_cast<mirror::Object*>(ref)->AsString();
+ mirror::String* s = ref->AsString();
std::string utf8(s->ToModifiedUtf8());
if (s->GetLength() <= 16) {
StringAppendF(&extras, " \"%s\"", utf8.c_str());
@@ -200,57 +196,50 @@
os << StringPrintf(" %5d: ", idx) << ref << " " << className << extras << "\n";
}
- // Make a copy of the table and sort it.
+ // Make a copy of the table and sort it, only adding non null and not cleared elements.
Table sorted_entries;
- for (size_t i = 0; i < entries.size(); ++i) {
- mirror::Object* entry = entries[i].Read();
- sorted_entries.push_back(GcRoot<mirror::Object>(entry));
- }
- std::sort(sorted_entries.begin(), sorted_entries.end(), ObjectComparator());
-
- // Remove any uninteresting stuff from the list. The sort moved them all to the end.
- while (!sorted_entries.empty() && sorted_entries.back().IsNull()) {
- sorted_entries.pop_back();
- }
- while (!sorted_entries.empty() &&
- Runtime::Current()->IsClearedJniWeakGlobal(
- sorted_entries.back().Read<kWithoutReadBarrier>())) {
- sorted_entries.pop_back();
+ for (GcRoot<mirror::Object>& root : entries) {
+ if (!root.IsNull() && !runtime->IsClearedJniWeakGlobal(root.Read())) {
+ sorted_entries.push_back(root);
+ }
}
if (sorted_entries.empty()) {
return;
}
+ std::sort(sorted_entries.begin(), sorted_entries.end(), GcRootComparator());
// Dump a summary of the whole table.
os << " Summary:\n";
size_t equiv = 0;
size_t identical = 0;
- for (size_t idx = 1; idx < count; idx++) {
- mirror::Object* prev = sorted_entries[idx-1].Read<kWithoutReadBarrier>();
- mirror::Object* current = sorted_entries[idx].Read<kWithoutReadBarrier>();
- size_t element_count = GetElementCount(prev);
- if (current == prev) {
- // Same reference, added more than once.
- identical++;
- } else if (current->GetClass() == prev->GetClass() && GetElementCount(current) == element_count) {
- // Same class / element count, different object.
- equiv++;
- } else {
- // Different class.
- DumpSummaryLine(os, prev, element_count, identical, equiv);
- equiv = identical = 0;
+ mirror::Object* prev = nullptr;
+ for (GcRoot<mirror::Object>& root : sorted_entries) {
+ mirror::Object* current = root.Read<kWithoutReadBarrier>();
+ if (prev != nullptr) {
+ const size_t element_count = GetElementCount(prev);
+ if (current == prev) {
+ // Same reference, added more than once.
+ ++identical;
+ } else if (current->GetClass() == prev->GetClass() &&
+ GetElementCount(current) == element_count) {
+ // Same class / element count, different object.
+ ++equiv;
+ } else {
+ // Different class.
+ DumpSummaryLine(os, prev, element_count, identical, equiv);
+ equiv = 0;
+ identical = 0;
+ }
}
+ prev = current;
}
// Handle the last entry.
- DumpSummaryLine(os, sorted_entries.back().Read<kWithoutReadBarrier>(),
- GetElementCount(sorted_entries.back().Read<kWithoutReadBarrier>()),
- identical, equiv);
+ DumpSummaryLine(os, prev, GetElementCount(prev), identical, equiv);
}
-void ReferenceTable::VisitRoots(RootCallback* visitor, void* arg, uint32_t tid,
- RootType root_type) {
+void ReferenceTable::VisitRoots(RootCallback* visitor, void* arg, const RootInfo& root_info) {
for (GcRoot<mirror::Object>& root : entries_) {
- root.VisitRoot(visitor, arg, tid, root_type);
+ root.VisitRoot(visitor, arg, root_info);
}
}
diff --git a/runtime/reference_table.h b/runtime/reference_table.h
index 6cffa85..22cf1cd 100644
--- a/runtime/reference_table.h
+++ b/runtime/reference_table.h
@@ -49,7 +49,7 @@
void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VisitRoots(RootCallback* visitor, void* arg, uint32_t tid, RootType root_type);
+ void VisitRoots(RootCallback* visitor, void* arg, const RootInfo& root_info);
private:
typedef std::vector<GcRoot<mirror::Object>,
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 85f9938..2aeb92d 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -77,12 +77,6 @@
}
void AppendWide(uint64_t value) {
- // For ARM and MIPS portable, align wide values to 8 bytes (ArgArray starts at offset of 4).
-#if defined(ART_USE_PORTABLE_COMPILER) && (defined(__arm__) || defined(__mips__))
- if (num_bytes_ % 8 == 0) {
- num_bytes_ += 4;
- }
-#endif
arg_array_[num_bytes_ / 4] = value;
arg_array_[(num_bytes_ / 4) + 1] = value >> 32;
num_bytes_ += 8;
diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc
index eca1800..7aefdaa 100644
--- a/runtime/reflection_test.cc
+++ b/runtime/reflection_test.cc
@@ -493,7 +493,6 @@
};
TEST_F(ReflectionTest, StaticMainMethod) {
- TEST_DISABLED_FOR_PORTABLE();
ScopedObjectAccess soa(Thread::Current());
jobject jclass_loader = LoadDex("Main");
StackHandleScope<1> hs(soa.Self());
@@ -518,122 +517,98 @@
}
TEST_F(ReflectionTest, StaticNopMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeNopMethod(true);
}
TEST_F(ReflectionTest, NonStaticNopMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeNopMethod(false);
}
TEST_F(ReflectionTest, StaticIdentityByteMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeIdentityByteMethod(true);
}
TEST_F(ReflectionTest, NonStaticIdentityByteMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeIdentityByteMethod(false);
}
TEST_F(ReflectionTest, StaticIdentityIntMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeIdentityIntMethod(true);
}
TEST_F(ReflectionTest, NonStaticIdentityIntMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeIdentityIntMethod(false);
}
TEST_F(ReflectionTest, StaticIdentityDoubleMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeIdentityDoubleMethod(true);
}
TEST_F(ReflectionTest, NonStaticIdentityDoubleMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeIdentityDoubleMethod(false);
}
TEST_F(ReflectionTest, StaticSumIntIntMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeSumIntIntMethod(true);
}
TEST_F(ReflectionTest, NonStaticSumIntIntMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeSumIntIntMethod(false);
}
TEST_F(ReflectionTest, StaticSumIntIntIntMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeSumIntIntIntMethod(true);
}
TEST_F(ReflectionTest, NonStaticSumIntIntIntMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeSumIntIntIntMethod(false);
}
TEST_F(ReflectionTest, StaticSumIntIntIntIntMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeSumIntIntIntIntMethod(true);
}
TEST_F(ReflectionTest, NonStaticSumIntIntIntIntMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeSumIntIntIntIntMethod(false);
}
TEST_F(ReflectionTest, StaticSumIntIntIntIntIntMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeSumIntIntIntIntIntMethod(true);
}
TEST_F(ReflectionTest, NonStaticSumIntIntIntIntIntMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeSumIntIntIntIntIntMethod(false);
}
TEST_F(ReflectionTest, StaticSumDoubleDoubleMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeSumDoubleDoubleMethod(true);
}
TEST_F(ReflectionTest, NonStaticSumDoubleDoubleMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeSumDoubleDoubleMethod(false);
}
TEST_F(ReflectionTest, StaticSumDoubleDoubleDoubleMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeSumDoubleDoubleDoubleMethod(true);
}
TEST_F(ReflectionTest, NonStaticSumDoubleDoubleDoubleMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeSumDoubleDoubleDoubleMethod(false);
}
TEST_F(ReflectionTest, StaticSumDoubleDoubleDoubleDoubleMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeSumDoubleDoubleDoubleDoubleMethod(true);
}
TEST_F(ReflectionTest, NonStaticSumDoubleDoubleDoubleDoubleMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeSumDoubleDoubleDoubleDoubleMethod(false);
}
TEST_F(ReflectionTest, StaticSumDoubleDoubleDoubleDoubleDoubleMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeSumDoubleDoubleDoubleDoubleDoubleMethod(true);
}
TEST_F(ReflectionTest, NonStaticSumDoubleDoubleDoubleDoubleDoubleMethod) {
- TEST_DISABLED_FOR_PORTABLE();
InvokeSumDoubleDoubleDoubleDoubleDoubleMethod(false);
}
diff --git a/runtime/runtime-inl.h b/runtime/runtime-inl.h
index cdf8d54..a82bc85 100644
--- a/runtime/runtime-inl.h
+++ b/runtime/runtime-inl.h
@@ -19,6 +19,7 @@
#include "runtime.h"
+#include "mirror/art_method.h"
#include "read_barrier-inl.h"
namespace art {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 078e7d2..6704963 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -29,10 +29,12 @@
#include <cstdio>
#include <cstdlib>
#include <limits>
-#include <memory>
+#include <memory_representation.h>
#include <vector>
#include <fcntl.h>
+#include "JniConstants.h"
+#include "ScopedLocalRef.h"
#include "arch/arm/quick_method_frame_info_arm.h"
#include "arch/arm/registers_arm.h"
#include "arch/arm64/quick_method_frame_info_arm64.h"
@@ -40,6 +42,8 @@
#include "arch/instruction_set_features.h"
#include "arch/mips/quick_method_frame_info_mips.h"
#include "arch/mips/registers_mips.h"
+#include "arch/mips64/quick_method_frame_info_mips64.h"
+#include "arch/mips64/registers_mips64.h"
#include "arch/x86/quick_method_frame_info_x86.h"
#include "arch/x86/registers_x86.h"
#include "arch/x86_64/quick_method_frame_info_x86_64.h"
@@ -57,19 +61,19 @@
#include "gc/heap.h"
#include "gc/space/image_space.h"
#include "gc/space/space.h"
+#include "handle_scope-inl.h"
#include "image.h"
#include "instrumentation.h"
#include "intern_table.h"
#include "jni_internal.h"
+#include "mirror/array.h"
#include "mirror/art_field-inl.h"
#include "mirror/art_method-inl.h"
-#include "mirror/array.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/stack_trace_element.h"
#include "mirror/throwable.h"
#include "monitor.h"
-#include "native_bridge_art_interface.h"
#include "native/dalvik_system_DexFile.h"
#include "native/dalvik_system_VMDebug.h"
#include "native/dalvik_system_VMRuntime.h"
@@ -78,48 +82,43 @@
#include "native/java_lang_Class.h"
#include "native/java_lang_DexCache.h"
#include "native/java_lang_Object.h"
-#include "native/java_lang_ref_FinalizerReference.h"
-#include "native/java_lang_reflect_Array.h"
-#include "native/java_lang_reflect_Constructor.h"
-#include "native/java_lang_reflect_Field.h"
-#include "native/java_lang_reflect_Method.h"
-#include "native/java_lang_reflect_Proxy.h"
-#include "native/java_lang_ref_Reference.h"
#include "native/java_lang_Runtime.h"
#include "native/java_lang_String.h"
#include "native/java_lang_System.h"
#include "native/java_lang_Thread.h"
#include "native/java_lang_Throwable.h"
#include "native/java_lang_VMClassLoader.h"
+#include "native/java_lang_ref_FinalizerReference.h"
+#include "native/java_lang_ref_Reference.h"
+#include "native/java_lang_reflect_Array.h"
+#include "native/java_lang_reflect_Constructor.h"
+#include "native/java_lang_reflect_Field.h"
+#include "native/java_lang_reflect_Method.h"
+#include "native/java_lang_reflect_Proxy.h"
#include "native/java_util_concurrent_atomic_AtomicLong.h"
#include "native/org_apache_harmony_dalvik_ddmc_DdmServer.h"
#include "native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.h"
#include "native/sun_misc_Unsafe.h"
-#include "parsed_options.h"
+#include "native_bridge_art_interface.h"
#include "oat_file.h"
#include "os.h"
+#include "parsed_options.h"
+#include "profiler.h"
#include "quick/quick_method_frame_info.h"
#include "reflection.h"
+#include "runtime_options.h"
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change.h"
#include "sigchain.h"
#include "signal_catcher.h"
#include "signal_set.h"
-#include "handle_scope-inl.h"
#include "thread.h"
#include "thread_list.h"
#include "trace.h"
#include "transaction.h"
-#include "profiler.h"
#include "verifier/method_verifier.h"
#include "well_known_classes.h"
-#include "JniConstants.h" // Last to avoid LOG redefinition in ics-mr1-plus-art.
-
-#ifdef HAVE_ANDROID_OS
-#include "cutils/properties.h"
-#endif
-
namespace art {
// If a signal isn't handled properly, enable a handler that attempts to dump the Java stack.
@@ -174,7 +173,8 @@
implicit_null_checks_(false),
implicit_so_checks_(false),
implicit_suspend_checks_(false),
- is_native_bridge_loaded_(false) {
+ is_native_bridge_loaded_(false),
+ jdwp_options_(nullptr) {
CheckAsmSupportOffsetsAndSizes();
}
@@ -190,6 +190,13 @@
}
Thread* self = Thread::Current();
+ if (self == nullptr) {
+ CHECK(AttachCurrentThread("Shutdown thread", false, nullptr, false));
+ self = Thread::Current();
+ } else {
+ LOG(WARNING) << "Current thread not detached in Runtime shutdown";
+ }
+
{
MutexLock mu(self, *Locks::runtime_shutdown_lock_);
shutting_down_started_ = true;
@@ -198,6 +205,16 @@
}
shutting_down_ = true;
}
+ // Shutdown and wait for the daemons.
+ CHECK(self != nullptr);
+ if (IsFinishedStarting()) {
+ self->ClearException();
+ self->GetJniEnv()->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons,
+ WellKnownClasses::java_lang_Daemons_stop);
+ }
+ DetachCurrentThread();
+ self = nullptr;
+
// Shut down background profiler before the runtime exits.
if (profiler_started_) {
BackgroundMethodSamplingProfiler::Shutdown();
@@ -211,6 +228,10 @@
// Make sure our internal threads are dead before we start tearing down things they're using.
Dbg::StopJdwp();
+ if (jdwp_options_ != nullptr) {
+ delete jdwp_options_;
+ }
+
delete signal_catcher_;
// Make sure all other non-daemon threads have terminated, and all daemon threads are suspended.
@@ -574,7 +595,7 @@
// Start the JDWP thread. If the command-line debugger flags specified "suspend=y",
// this will pause the runtime, so we probably want this to come last.
- Dbg::StartJdwp();
+ Dbg::StartJdwp(jdwp_options_);
}
void Runtime::StartSignalCatcher() {
@@ -608,8 +629,9 @@
}
static bool OpenDexFilesFromImage(const std::string& image_location,
- std::vector<const DexFile*>& dex_files,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files,
size_t* failures) {
+ DCHECK(dex_files != nullptr) << "OpenDexFilesFromImage: out-param is NULL";
std::string system_filename;
bool has_system = false;
std::string cache_filename_unused;
@@ -653,11 +675,11 @@
*failures += 1;
continue;
}
- const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg);
- if (dex_file == nullptr) {
+ std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
+ if (dex_file.get() == nullptr) {
*failures += 1;
} else {
- dex_files.push_back(dex_file);
+ dex_files->push_back(std::move(dex_file));
}
}
Runtime::Current()->GetClassLinker()->RegisterOatFile(oat_file.release());
@@ -666,8 +688,10 @@
static size_t OpenDexFiles(const std::vector<std::string>& dex_filenames,
+ const std::vector<std::string>& dex_locations,
const std::string& image_location,
- std::vector<const DexFile*>& dex_files) {
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+ DCHECK(dex_files != nullptr) << "OpenDexFiles: out-param is NULL";
size_t failure_count = 0;
if (!image_location.empty() && OpenDexFilesFromImage(image_location, dex_files, &failure_count)) {
return failure_count;
@@ -675,12 +699,13 @@
failure_count = 0;
for (size_t i = 0; i < dex_filenames.size(); i++) {
const char* dex_filename = dex_filenames[i].c_str();
+ const char* dex_location = dex_locations[i].c_str();
std::string error_msg;
if (!OS::FileExists(dex_filename)) {
LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'";
continue;
}
- if (!DexFile::Open(dex_filename, dex_filename, &error_msg, &dex_files)) {
+ if (!DexFile::Open(dex_filename, dex_location, &error_msg, dex_files)) {
LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg;
++failure_count;
}
@@ -693,8 +718,11 @@
MemMap::Init();
- std::unique_ptr<ParsedOptions> options(ParsedOptions::Create(raw_options, ignore_unrecognized));
- if (options.get() == nullptr) {
+ using Opt = RuntimeArgumentMap;
+ RuntimeArgumentMap runtime_options;
+ std::unique_ptr<ParsedOptions> parsed_options(
+ ParsedOptions::Create(raw_options, ignore_unrecognized, &runtime_options));
+ if (parsed_options.get() == nullptr) {
LOG(ERROR) << "Failed to parse options";
return false;
}
@@ -702,76 +730,84 @@
QuasiAtomic::Startup();
- Monitor::Init(options->lock_profiling_threshold_, options->hook_is_sensitive_thread_);
+ Monitor::Init(runtime_options.GetOrDefault(Opt::LockProfThreshold),
+ runtime_options.GetOrDefault(Opt::HookIsSensitiveThread));
- boot_class_path_string_ = options->boot_class_path_string_;
- class_path_string_ = options->class_path_string_;
- properties_ = options->properties_;
+ boot_class_path_string_ = runtime_options.ReleaseOrDefault(Opt::BootClassPath);
+ class_path_string_ = runtime_options.ReleaseOrDefault(Opt::ClassPath);
+ properties_ = runtime_options.ReleaseOrDefault(Opt::PropertiesList);
- compiler_callbacks_ = options->compiler_callbacks_;
- patchoat_executable_ = options->patchoat_executable_;
- must_relocate_ = options->must_relocate_;
- is_zygote_ = options->is_zygote_;
- is_explicit_gc_disabled_ = options->is_explicit_gc_disabled_;
- dex2oat_enabled_ = options->dex2oat_enabled_;
- image_dex2oat_enabled_ = options->image_dex2oat_enabled_;
+ compiler_callbacks_ = runtime_options.GetOrDefault(Opt::CompilerCallbacksPtr);
+ patchoat_executable_ = runtime_options.ReleaseOrDefault(Opt::PatchOat);
+ must_relocate_ = runtime_options.GetOrDefault(Opt::Relocate);
+ is_zygote_ = runtime_options.Exists(Opt::Zygote);
+ is_explicit_gc_disabled_ = runtime_options.Exists(Opt::DisableExplicitGC);
+ dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::Dex2Oat);
+ image_dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::ImageDex2Oat);
- vfprintf_ = options->hook_vfprintf_;
- exit_ = options->hook_exit_;
- abort_ = options->hook_abort_;
+ vfprintf_ = runtime_options.GetOrDefault(Opt::HookVfprintf);
+ exit_ = runtime_options.GetOrDefault(Opt::HookExit);
+ abort_ = runtime_options.GetOrDefault(Opt::HookAbort);
- default_stack_size_ = options->stack_size_;
- stack_trace_file_ = options->stack_trace_file_;
+ default_stack_size_ = runtime_options.GetOrDefault(Opt::StackSize);
+ stack_trace_file_ = runtime_options.ReleaseOrDefault(Opt::StackTraceFile);
- compiler_executable_ = options->compiler_executable_;
- compiler_options_ = options->compiler_options_;
- image_compiler_options_ = options->image_compiler_options_;
- image_location_ = options->image_;
+ compiler_executable_ = runtime_options.ReleaseOrDefault(Opt::Compiler);
+ compiler_options_ = runtime_options.ReleaseOrDefault(Opt::CompilerOptions);
+ image_compiler_options_ = runtime_options.ReleaseOrDefault(Opt::ImageCompilerOptions);
+ image_location_ = runtime_options.GetOrDefault(Opt::Image);
- max_spins_before_thin_lock_inflation_ = options->max_spins_before_thin_lock_inflation_;
+ max_spins_before_thin_lock_inflation_ =
+ runtime_options.GetOrDefault(Opt::MaxSpinsBeforeThinLockInflation);
monitor_list_ = new MonitorList;
monitor_pool_ = MonitorPool::Create();
thread_list_ = new ThreadList;
intern_table_ = new InternTable;
- verify_ = options->verify_;
+ verify_ = runtime_options.GetOrDefault(Opt::Verify);
- if (options->interpreter_only_) {
+ if (runtime_options.GetOrDefault(Opt::Interpret)) {
GetInstrumentation()->ForceInterpretOnly();
}
- heap_ = new gc::Heap(options->heap_initial_size_,
- options->heap_growth_limit_,
- options->heap_min_free_,
- options->heap_max_free_,
- options->heap_target_utilization_,
- options->foreground_heap_growth_multiplier_,
- options->heap_maximum_size_,
- options->heap_non_moving_space_capacity_,
- options->image_,
- options->image_isa_,
- options->collector_type_,
- options->background_collector_type_,
- options->large_object_space_type_,
- options->large_object_threshold_,
- options->parallel_gc_threads_,
- options->conc_gc_threads_,
- options->low_memory_mode_,
- options->long_pause_log_threshold_,
- options->long_gc_log_threshold_,
- options->ignore_max_footprint_,
- options->use_tlab_,
- options->verify_pre_gc_heap_,
- options->verify_pre_sweeping_heap_,
- options->verify_post_gc_heap_,
- options->verify_pre_gc_rosalloc_,
- options->verify_pre_sweeping_rosalloc_,
- options->verify_post_gc_rosalloc_,
- options->use_homogeneous_space_compaction_for_oom_,
- options->min_interval_homogeneous_space_compaction_by_oom_);
+ XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption);
+ heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),
+ runtime_options.GetOrDefault(Opt::HeapGrowthLimit),
+ runtime_options.GetOrDefault(Opt::HeapMinFree),
+ runtime_options.GetOrDefault(Opt::HeapMaxFree),
+ runtime_options.GetOrDefault(Opt::HeapTargetUtilization),
+ runtime_options.GetOrDefault(Opt::ForegroundHeapGrowthMultiplier),
+ runtime_options.GetOrDefault(Opt::MemoryMaximumSize),
+ runtime_options.GetOrDefault(Opt::NonMovingSpaceCapacity),
+ runtime_options.GetOrDefault(Opt::Image),
+ runtime_options.GetOrDefault(Opt::ImageInstructionSet),
+ xgc_option.collector_type_,
+ runtime_options.GetOrDefault(Opt::BackgroundGc),
+ runtime_options.GetOrDefault(Opt::LargeObjectSpace),
+ runtime_options.GetOrDefault(Opt::LargeObjectThreshold),
+ runtime_options.GetOrDefault(Opt::ParallelGCThreads),
+ runtime_options.GetOrDefault(Opt::ConcGCThreads),
+ runtime_options.Exists(Opt::LowMemoryMode),
+ runtime_options.GetOrDefault(Opt::LongPauseLogThreshold),
+ runtime_options.GetOrDefault(Opt::LongGCLogThreshold),
+ runtime_options.Exists(Opt::IgnoreMaxFootprint),
+ runtime_options.Exists(Opt::UseTLAB),
+ xgc_option.verify_pre_gc_heap_,
+ xgc_option.verify_pre_sweeping_heap_,
+ xgc_option.verify_post_gc_heap_,
+ xgc_option.verify_pre_gc_rosalloc_,
+ xgc_option.verify_pre_sweeping_rosalloc_,
+ xgc_option.verify_post_gc_rosalloc_,
+ runtime_options.GetOrDefault(Opt::EnableHSpaceCompactForOOM),
+ runtime_options.GetOrDefault(Opt::HSpaceCompactForOOMMinIntervalsMs));
- dump_gc_performance_on_shutdown_ = options->dump_gc_performance_on_shutdown_;
+ dump_gc_performance_on_shutdown_ = runtime_options.Exists(Opt::DumpGCPerformanceOnShutdown);
+
+ if (runtime_options.Exists(Opt::JdwpOptions)) {
+ JDWP::JdwpOptions options = runtime_options.GetOrDefault(Opt::JdwpOptions);
+ jdwp_options_ = new JDWP::JdwpOptions(options);
+ }
BlockSignals();
InitPlatformSignalHandlers();
@@ -822,7 +858,7 @@
}
}
- java_vm_ = new JavaVMExt(this, options.get());
+ java_vm_ = new JavaVMExt(this, runtime_options);
Thread::Startup();
@@ -846,25 +882,46 @@
if (kIsDebugBuild) {
GetHeap()->GetImageSpace()->VerifyImageAllocations();
}
- } else if (!IsCompiler() || !image_dex2oat_enabled_) {
+ if (boot_class_path_string_.empty()) {
+ // The bootclasspath is not explicitly specified: construct it from the loaded dex files.
+ const std::vector<const DexFile*>& boot_class_path = GetClassLinker()->GetBootClassPath();
+ std::vector<std::string> dex_locations;
+ dex_locations.reserve(boot_class_path.size());
+ for (const DexFile* dex_file : boot_class_path) {
+ dex_locations.push_back(dex_file->GetLocation());
+ }
+ boot_class_path_string_ = Join(dex_locations, ':');
+ }
+ } else {
std::vector<std::string> dex_filenames;
Split(boot_class_path_string_, ':', &dex_filenames);
- std::vector<const DexFile*> boot_class_path;
- OpenDexFiles(dex_filenames, options->image_, boot_class_path);
- class_linker_->InitWithoutImage(boot_class_path);
+
+ std::vector<std::string> dex_locations;
+ if (!runtime_options.Exists(Opt::BootClassPathLocations)) {
+ dex_locations = dex_filenames;
+ } else {
+ dex_locations = runtime_options.GetOrDefault(Opt::BootClassPathLocations);
+ CHECK_EQ(dex_filenames.size(), dex_locations.size());
+ }
+
+ std::vector<std::unique_ptr<const DexFile>> boot_class_path;
+ OpenDexFiles(dex_filenames,
+ dex_locations,
+ runtime_options.GetOrDefault(Opt::Image),
+ &boot_class_path);
+ instruction_set_ = runtime_options.GetOrDefault(Opt::ImageInstructionSet);
+ class_linker_->InitWithoutImage(std::move(boot_class_path));
+
// TODO: Should we move the following to InitWithoutImage?
- SetInstructionSet(kRuntimeISA);
+ SetInstructionSet(instruction_set_);
for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
if (!HasCalleeSaveMethod(type)) {
SetCalleeSaveMethod(CreateCalleeSaveMethod(), type);
}
}
- } else {
- CHECK(options->boot_class_path_ != nullptr);
- CHECK_NE(options->boot_class_path_->size(), 0U);
- class_linker_->InitWithoutImage(*options->boot_class_path_);
}
+
CHECK(class_linker_ != nullptr);
// Initialize the special sentinel_ value early.
@@ -873,20 +930,42 @@
verifier::MethodVerifier::Init();
- method_trace_ = options->method_trace_;
- method_trace_file_ = options->method_trace_file_;
- method_trace_file_size_ = options->method_trace_file_size_;
+ method_trace_ = runtime_options.Exists(Opt::MethodTrace);
+ method_trace_file_ = runtime_options.ReleaseOrDefault(Opt::MethodTraceFile);
+ method_trace_file_size_ = runtime_options.ReleaseOrDefault(Opt::MethodTraceFileSize);
- profile_output_filename_ = options->profile_output_filename_;
- profiler_options_ = options->profiler_options_;
+ {
+ auto&& profiler_options = runtime_options.ReleaseOrDefault(Opt::ProfilerOpts);
+ profile_output_filename_ = profiler_options.output_file_name_;
+
+ // TODO: Don't do this, just change ProfilerOptions to include the output file name?
+ ProfilerOptions other_options(
+ profiler_options.enabled_,
+ profiler_options.period_s_,
+ profiler_options.duration_s_,
+ profiler_options.interval_us_,
+ profiler_options.backoff_coefficient_,
+ profiler_options.start_immediately_,
+ profiler_options.top_k_threshold_,
+ profiler_options.top_k_change_threshold_,
+ profiler_options.profile_type_,
+ profiler_options.max_stack_depth_);
+
+ profiler_options_ = other_options;
+ }
// TODO: move this to just be an Trace::Start argument
- Trace::SetDefaultClockSource(options->profile_clock_source_);
+ Trace::SetDefaultClockSource(runtime_options.GetOrDefault(Opt::ProfileClock));
- if (options->method_trace_) {
+ if (method_trace_) {
ScopedThreadStateChange tsc(self, kWaitingForMethodTracingStart);
- Trace::Start(options->method_trace_file_.c_str(), -1, options->method_trace_file_size_, 0,
- false, false, 0);
+ Trace::Start(method_trace_file_.c_str(),
+ -1,
+ static_cast<int>(method_trace_file_size_),
+ 0,
+ false,
+ false,
+ 0);
}
// Pre-allocate an OutOfMemoryError for the double-OOME case.
@@ -930,7 +1009,10 @@
// Runtime::Start():
// DidForkFromZygote(kInitialize) -> try to initialize any native bridge given.
// No-op wrt native bridge.
- is_native_bridge_loaded_ = LoadNativeBridge(options->native_bridge_library_filename_);
+ {
+ std::string native_bridge_file_name = runtime_options.ReleaseOrDefault(Opt::NativeBridge);
+ is_native_bridge_loaded_ = LoadNativeBridge(native_bridge_file_name);
+ }
VLOG(startup) << "Runtime::Init exiting";
return true;
@@ -956,10 +1038,9 @@
// Most JNI libraries can just use System.loadLibrary, but libcore can't because it's
// the library that implements System.loadLibrary!
{
- std::string mapped_name(StringPrintf(OS_SHARED_LIB_FORMAT_STR, "javacore"));
std::string reason;
- if (!java_vm_->LoadNativeLibrary(env, mapped_name, nullptr, &reason)) {
- LOG(FATAL) << "LoadNativeLibrary failed for \"" << mapped_name << "\": " << reason;
+ if (!java_vm_->LoadNativeLibrary(env, "libjavacore.so", nullptr, &reason)) {
+ LOG(FATAL) << "LoadNativeLibrary failed for \"libjavacore.so\": " << reason;
}
}
@@ -1181,35 +1262,23 @@
}
}
+void Runtime::VisitTransactionRoots(RootCallback* callback, void* arg) {
+ if (preinitialization_transaction_ != nullptr) {
+ preinitialization_transaction_->VisitRoots(callback, arg);
+ }
+}
+
void Runtime::VisitNonThreadRoots(RootCallback* callback, void* arg) {
java_vm_->VisitRoots(callback, arg);
- if (!sentinel_.IsNull()) {
- sentinel_.VisitRoot(callback, arg, 0, kRootVMInternal);
- DCHECK(!sentinel_.IsNull());
- }
- if (!pre_allocated_OutOfMemoryError_.IsNull()) {
- pre_allocated_OutOfMemoryError_.VisitRoot(callback, arg, 0, kRootVMInternal);
- DCHECK(!pre_allocated_OutOfMemoryError_.IsNull());
- }
- resolution_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
- DCHECK(!resolution_method_.IsNull());
- if (!pre_allocated_NoClassDefFoundError_.IsNull()) {
- pre_allocated_NoClassDefFoundError_.VisitRoot(callback, arg, 0, kRootVMInternal);
- DCHECK(!pre_allocated_NoClassDefFoundError_.IsNull());
- }
- if (HasImtConflictMethod()) {
- imt_conflict_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
- }
- if (!imt_unimplemented_method_.IsNull()) {
- imt_unimplemented_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
- }
- if (HasDefaultImt()) {
- default_imt_.VisitRoot(callback, arg, 0, kRootVMInternal);
- }
+ sentinel_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+ pre_allocated_OutOfMemoryError_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+ resolution_method_.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
+ pre_allocated_NoClassDefFoundError_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+ imt_conflict_method_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+ imt_unimplemented_method_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+ default_imt_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
- if (!callee_save_methods_[i].IsNull()) {
- callee_save_methods_[i].VisitRoot(callback, arg, 0, kRootVMInternal);
- }
+ callee_save_methods_[i].VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
}
verifier::MethodVerifier::VisitStaticRoots(callback, arg);
{
@@ -1218,9 +1287,7 @@
verifier->VisitRoots(callback, arg);
}
}
- if (preinitialization_transaction_ != nullptr) {
- preinitialization_transaction_->VisitRoots(callback, arg);
- }
+ VisitTransactionRoots(callback, arg);
instrumentation_.VisitRoots(callback, arg);
}
@@ -1229,6 +1296,15 @@
VisitNonThreadRoots(callback, arg);
}
+void Runtime::VisitThreadRoots(RootCallback* callback, void* arg) {
+ thread_list_->VisitRoots(callback, arg);
+}
+
+size_t Runtime::FlipThreadRoots(Closure* thread_flip_visitor, Closure* flip_callback,
+ gc::collector::GarbageCollector* collector) {
+ return thread_list_->FlipThreadRoots(thread_flip_visitor, flip_callback, collector);
+}
+
void Runtime::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags) {
VisitNonConcurrentRoots(callback, arg);
VisitConcurrentRoots(callback, arg, flags);
@@ -1257,10 +1333,9 @@
method->SetDexMethodIndex(DexFile::kDexNoIndex);
// When compiling, the code pointer will get set later when the image is loaded.
if (runtime->IsCompiler()) {
- method->SetEntryPointFromPortableCompiledCode(nullptr);
- method->SetEntryPointFromQuickCompiledCode(nullptr);
+ size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
+ method->SetEntryPointFromQuickCompiledCodePtrSize(nullptr, pointer_size);
} else {
- method->SetEntryPointFromPortableCompiledCode(GetPortableImtConflictStub());
method->SetEntryPointFromQuickCompiledCode(GetQuickImtConflictStub());
}
return method.Get();
@@ -1277,10 +1352,9 @@
method->SetDexMethodIndex(DexFile::kDexNoIndex);
// When compiling, the code pointer will get set later when the image is loaded.
if (runtime->IsCompiler()) {
- method->SetEntryPointFromPortableCompiledCode(nullptr);
- method->SetEntryPointFromQuickCompiledCode(nullptr);
+ size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
+ method->SetEntryPointFromQuickCompiledCodePtrSize(nullptr, pointer_size);
} else {
- method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionStub());
method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());
}
return method.Get();
@@ -1295,8 +1369,8 @@
method->SetDeclaringClass(mirror::ArtMethod::GetJavaLangReflectArtMethod());
// TODO: use a special method for callee saves
method->SetDexMethodIndex(DexFile::kDexNoIndex);
- method->SetEntryPointFromPortableCompiledCode(nullptr);
- method->SetEntryPointFromQuickCompiledCode(nullptr);
+ size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
+ method->SetEntryPointFromQuickCompiledCodePtrSize(nullptr, pointer_size);
DCHECK_NE(instruction_set_, kNone);
return method.Get();
}
@@ -1313,6 +1387,14 @@
java_vm_->AllowNewWeakGlobals();
}
+void Runtime::EnsureNewSystemWeaksDisallowed() {
+ // Lock and unlock the system weak locks once to ensure that no
+ // threads are still in the middle of adding new system weaks.
+ monitor_list_->EnsureNewMonitorsDisallowed();
+ intern_table_->EnsureNewInternsDisallowed();
+ java_vm_->EnsureNewWeakGlobalsDisallowed();
+}
+
void Runtime::SetInstructionSet(InstructionSet instruction_set) {
instruction_set_ = instruction_set;
if ((instruction_set_ == kThumb2) || (instruction_set_ == kArm)) {
@@ -1325,6 +1407,11 @@
CalleeSaveType type = static_cast<CalleeSaveType>(i);
callee_save_method_frame_infos_[i] = mips::MipsCalleeSaveMethodFrameInfo(type);
}
+ } else if (instruction_set_ == kMips64) {
+ for (int i = 0; i != kLastCalleeSaveType; ++i) {
+ CalleeSaveType type = static_cast<CalleeSaveType>(i);
+ callee_save_method_frame_infos_[i] = mips64::Mips64CalleeSaveMethodFrameInfo(type);
+ }
} else if (instruction_set_ == kX86) {
for (int i = 0; i != kLastCalleeSaveType; ++i) {
CalleeSaveType type = static_cast<CalleeSaveType>(i);
@@ -1369,12 +1456,18 @@
void Runtime::AddMethodVerifier(verifier::MethodVerifier* verifier) {
DCHECK(verifier != nullptr);
+ if (gAborting) {
+ return;
+ }
MutexLock mu(Thread::Current(), method_verifier_lock_);
method_verifiers_.insert(verifier);
}
void Runtime::RemoveMethodVerifier(verifier::MethodVerifier* verifier) {
DCHECK(verifier != nullptr);
+ if (gAborting) {
+ return;
+ }
MutexLock mu(Thread::Current(), method_verifier_lock_);
auto it = method_verifiers_.find(verifier);
CHECK(it != method_verifiers_.end());
@@ -1401,6 +1494,31 @@
preinitialization_transaction_ = nullptr;
}
+
+bool Runtime::IsTransactionAborted() const {
+ if (!IsActiveTransaction()) {
+ return false;
+ } else {
+ DCHECK(IsCompiler());
+ return preinitialization_transaction_->IsAborted();
+ }
+}
+
+void Runtime::AbortTransactionAndThrowInternalError(Thread* self,
+ const std::string& abort_message) {
+ DCHECK(IsCompiler());
+ DCHECK(IsActiveTransaction());
+ preinitialization_transaction_->Abort(abort_message);
+ ThrowInternalErrorForAbortedTransaction(self);
+}
+
+void Runtime::ThrowInternalErrorForAbortedTransaction(Thread* self) {
+ DCHECK(IsCompiler());
+ DCHECK(IsActiveTransaction());
+ DCHECK(IsTransactionAborted());
+ preinitialization_transaction_->ThrowInternalError(self);
+}
+
void Runtime::RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset,
uint8_t value, bool is_volatile) const {
DCHECK(IsCompiler());
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 39fd910..d98eb8d 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -43,7 +43,13 @@
namespace gc {
class Heap;
+ namespace collector {
+ class GarbageCollector;
+ } // namespace collector
} // namespace gc
+namespace JDWP {
+ struct JdwpOptions;
+} // namespace JDWP
namespace mirror {
class ArtMethod;
class ClassLoader;
@@ -58,6 +64,7 @@
class MethodVerifier;
} // namespace verifier
class ClassLinker;
+class Closure;
class DexFile;
class InternTable;
class JavaVMExt;
@@ -133,6 +140,10 @@
return compiler_options_;
}
+ void AddCompilerOption(std::string option) {
+ compiler_options_.push_back(option);
+ }
+
const std::vector<std::string>& GetImageCompilerOptions() const {
return image_compiler_options_;
}
@@ -266,8 +277,9 @@
return "2.1.0";
}
- void DisallowNewSystemWeaks() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void DisallowNewSystemWeaks() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void AllowNewSystemWeaks() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void EnsureNewSystemWeaksDisallowed() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Visit all the roots. If only_dirty is true then non-dirty roots won't be visited. If
// clean_dirty is true then dirty roots will be marked as non-dirty after visiting.
@@ -283,6 +295,17 @@
void VisitNonThreadRoots(RootCallback* visitor, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void VisitTransactionRoots(RootCallback* visitor, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Visit all of the thread roots.
+ void VisitThreadRoots(RootCallback* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Flip thread roots from from-space refs to to-space refs.
+ size_t FlipThreadRoots(Closure* thread_flip_visitor, Closure* flip_callback,
+ gc::collector::GarbageCollector* collector)
+ LOCKS_EXCLUDED(Locks::mutator_lock_);
+
// Visit all other roots which must be done with mutators suspended.
void VisitNonConcurrentRoots(RootCallback* visitor, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -421,6 +444,9 @@
LOCKS_EXCLUDED(method_verifier_lock_);
const std::vector<const DexFile*>& GetCompileTimeClassPath(jobject class_loader);
+
+ // The caller is responsible for ensuring the class_path DexFiles remain
+ // valid as long as the Runtime object remains valid.
void SetCompileTimeClassPath(jobject class_loader, std::vector<const DexFile*>& class_path);
void StartProfiler(const char* profile_output_filename);
@@ -432,6 +458,13 @@
}
void EnterTransactionMode(Transaction* transaction);
void ExitTransactionMode();
+ bool IsTransactionAborted() const;
+
+ void AbortTransactionAndThrowInternalError(Thread* self, const std::string& abort_message)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void ThrowInternalErrorForAbortedTransaction(Thread* self)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
void RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset, uint8_t value,
bool is_volatile) const;
void RecordWriteFieldByte(mirror::Object* obj, MemberOffset field_offset, int8_t value,
@@ -652,6 +685,9 @@
// that there's no native bridge.
bool is_native_bridge_loaded_;
+ // JDWP options for debugging.
+ const JDWP::JdwpOptions* jdwp_options_;
+
DISALLOW_COPY_AND_ASSIGN(Runtime);
};
std::ostream& operator<<(std::ostream& os, const Runtime::CalleeSaveType& rhs);
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index 1de035c..35d944f 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -25,19 +25,29 @@
#include "base/dumpable.h"
#include "base/logging.h"
+#include "base/macros.h"
#include "base/mutex.h"
#include "base/stringprintf.h"
#include "thread-inl.h"
+#include "thread_list.h"
#include "utils.h"
namespace art {
static constexpr bool kDumpHeapObjectOnSigsevg = false;
+static constexpr bool kUseSigRTTimeout = true;
struct Backtrace {
+ public:
+ explicit Backtrace(void* raw_context) : raw_context_(raw_context) {}
void Dump(std::ostream& os) const {
- DumpNativeStack(os, GetTid(), "\t");
+ DumpNativeStack(os, GetTid(), "\t", nullptr, raw_context_);
}
+ private:
+ // Stores the context of the signal that was unexpected and will terminate the runtime. The
+ // DumpNativeStack code will take care of casting it to the expected type. This is required
+ // as our signal handler runs on an alternate stack.
+ void* raw_context_;
};
struct OsInfo {
@@ -276,10 +286,29 @@
mcontext_t& context;
};
+// Return the signal number we recognize as timeout. -1 means not active/supported.
+static int GetTimeoutSignal() {
+#if defined(__APPLE__)
+ // Mac does not support realtime signals.
+ UNUSED(kUseSigRTTimeout);
+ return -1;
+#else
+ return kUseSigRTTimeout ? (SIGRTMIN + 2) : -1;
+#endif
+}
+
+static bool IsTimeoutSignal(int signal_number) {
+ return signal_number == GetTimeoutSignal();
+}
+
void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context) {
static bool handlingUnexpectedSignal = false;
if (handlingUnexpectedSignal) {
LogMessage::LogLine(__FILE__, __LINE__, INTERNAL_FATAL, "HandleUnexpectedSignal reentered\n");
+ if (IsTimeoutSignal(signal_number)) {
+ // Ignore a recursive timeout.
+ return;
+ }
_exit(1);
}
handlingUnexpectedSignal = true;
@@ -298,7 +327,7 @@
pid_t tid = GetTid();
std::string thread_name(GetThreadName(tid));
UContext thread_context(raw_context);
- Backtrace thread_backtrace;
+ Backtrace thread_backtrace(raw_context);
LOG(INTERNAL_FATAL) << "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"
<< StringPrintf("Fatal signal %d (%s), code %d (%s)",
@@ -313,6 +342,10 @@
<< "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace);
Runtime* runtime = Runtime::Current();
if (runtime != nullptr) {
+ if (IsTimeoutSignal(signal_number)) {
+ // Special timeout signal. Try to dump all threads.
+ runtime->GetThreadList()->DumpForSigQuit(LOG(INTERNAL_FATAL));
+ }
gc::Heap* heap = runtime->GetHeap();
LOG(INTERNAL_FATAL) << "Fault message: " << runtime->GetFaultMessage();
if (kDumpHeapObjectOnSigsevg && heap != nullptr && info != nullptr) {
@@ -367,6 +400,10 @@
rc += sigaction(SIGSTKFLT, &action, NULL);
#endif
rc += sigaction(SIGTRAP, &action, NULL);
+ // Special dump-all timeout.
+ if (GetTimeoutSignal() != -1) {
+ rc += sigaction(GetTimeoutSignal(), &action, NULL);
+ }
CHECK_EQ(rc, 0);
}
diff --git a/runtime/runtime_options.cc b/runtime/runtime_options.cc
new file mode 100644
index 0000000..c54461e
--- /dev/null
+++ b/runtime/runtime_options.cc
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+#include "runtime_options.h"
+
+#include "gc/heap.h"
+#include "monitor.h"
+#include "runtime.h"
+#include "trace.h"
+#include "utils.h"
+#include "debugger.h"
+
+namespace art {
+
+// Specify storage for the RuntimeOptions keys.
+
+#define RUNTIME_OPTIONS_KEY(Type, Name, ...) const RuntimeArgumentMap::Key<Type> RuntimeArgumentMap::Name {__VA_ARGS__}; // NOLINT [readability/braces] [4]
+#include "runtime_options.def"
+
+} // namespace art
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
new file mode 100644
index 0000000..022a2a5
--- /dev/null
+++ b/runtime/runtime_options.def
@@ -0,0 +1,119 @@
+/*
+ * 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 RUNTIME_OPTIONS_KEY
+#error "Please #define RUNTIME_OPTIONS_KEY before #including this file"
+#define RUNTIME_OPTIONS_KEY(...) // Don't display errors in this file in IDEs.
+#endif
+
+// This file defines the list of keys for RuntimeOptions.
+// These can be used with RuntimeOptions.Get/Set/etc, for example:
+// RuntimeOptions opt; bool* dex2oat_enabled = opt.Get(RuntimeOptions::Dex2Oat);
+//
+// Column Descriptions:
+// <<Type>> <<Key Name>> <<Default Value>>
+//
+// Default values are only used by Map::GetOrDefault(K<T>).
+// If a default value is omitted here, T{} is used as the default value, which is
+// almost-always the value of the type as if it was memset to all 0.
+//
+
+// Parse-able keys from the command line.
+RUNTIME_OPTIONS_KEY (Unit, Zygote)
+RUNTIME_OPTIONS_KEY (Unit, Help)
+RUNTIME_OPTIONS_KEY (Unit, ShowVersion)
+RUNTIME_OPTIONS_KEY (std::string, BootClassPath)
+RUNTIME_OPTIONS_KEY (ParseStringList<':'>,BootClassPathLocations) // std::vector<std::string>
+RUNTIME_OPTIONS_KEY (std::string, ClassPath)
+RUNTIME_OPTIONS_KEY (std::string, Image)
+RUNTIME_OPTIONS_KEY (Unit, CheckJni)
+RUNTIME_OPTIONS_KEY (Unit, JniOptsForceCopy)
+RUNTIME_OPTIONS_KEY (JDWP::JdwpOptions, JdwpOptions)
+RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryMaximumSize, gc::Heap::kDefaultMaximumSize) // -Xmx
+RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryInitialSize, gc::Heap::kDefaultInitialSize) // -Xms
+RUNTIME_OPTIONS_KEY (MemoryKiB, HeapGrowthLimit) // Default is 0 for unlimited
+RUNTIME_OPTIONS_KEY (MemoryKiB, HeapMinFree, gc::Heap::kDefaultMinFree)
+RUNTIME_OPTIONS_KEY (MemoryKiB, HeapMaxFree, gc::Heap::kDefaultMaxFree)
+RUNTIME_OPTIONS_KEY (MemoryKiB, NonMovingSpaceCapacity, gc::Heap::kDefaultNonMovingSpaceCapacity)
+RUNTIME_OPTIONS_KEY (double, HeapTargetUtilization, gc::Heap::kDefaultTargetUtilization)
+RUNTIME_OPTIONS_KEY (double, ForegroundHeapGrowthMultiplier, gc::Heap::kDefaultHeapGrowthMultiplier)
+RUNTIME_OPTIONS_KEY (unsigned int, ParallelGCThreads, 1u)
+RUNTIME_OPTIONS_KEY (unsigned int, ConcGCThreads)
+RUNTIME_OPTIONS_KEY (Memory<1>, StackSize) // -Xss
+RUNTIME_OPTIONS_KEY (unsigned int, MaxSpinsBeforeThinLockInflation,Monitor::kDefaultMaxSpinsBeforeThinLockInflation)
+RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
+ LongPauseLogThreshold, gc::Heap::kDefaultLongPauseLogThreshold)
+RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
+ LongGCLogThreshold, gc::Heap::kDefaultLongGCLogThreshold)
+RUNTIME_OPTIONS_KEY (Unit, DumpGCPerformanceOnShutdown)
+RUNTIME_OPTIONS_KEY (Unit, IgnoreMaxFootprint)
+RUNTIME_OPTIONS_KEY (Unit, LowMemoryMode)
+RUNTIME_OPTIONS_KEY (Unit, UseTLAB)
+RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true)
+RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
+ HSpaceCompactForOOMMinIntervalsMs,\
+ MsToNs(100 * 1000)) // 100s
+RUNTIME_OPTIONS_KEY (std::vector<std::string>, \
+ PropertiesList) // -D<whatever> -D<whatever> ...
+RUNTIME_OPTIONS_KEY (std::string, JniTrace)
+RUNTIME_OPTIONS_KEY (std::string, PatchOat)
+RUNTIME_OPTIONS_KEY (bool, Relocate, kDefaultMustRelocate)
+RUNTIME_OPTIONS_KEY (bool, Dex2Oat, true)
+RUNTIME_OPTIONS_KEY (bool, ImageDex2Oat, true)
+ // kPoisonHeapReferences currently works with
+ // the interpreter only.
+ // TODO: make it work with the compiler.
+RUNTIME_OPTIONS_KEY (bool, Interpret, (kPoisonHeapReferences || kUseReadBarrier)) // -Xint
+ // Disable the compiler for CC (for now).
+RUNTIME_OPTIONS_KEY (XGcOption, GcOption) // -Xgc:
+RUNTIME_OPTIONS_KEY (gc::space::LargeObjectSpaceType, \
+ LargeObjectSpace, gc::Heap::kDefaultLargeObjectSpaceType)
+RUNTIME_OPTIONS_KEY (Memory<1>, LargeObjectThreshold, gc::Heap::kDefaultLargeObjectThreshold)
+RUNTIME_OPTIONS_KEY (BackgroundGcOption, BackgroundGc)
+
+RUNTIME_OPTIONS_KEY (Unit, DisableExplicitGC)
+RUNTIME_OPTIONS_KEY (LogVerbosity, Verbose)
+RUNTIME_OPTIONS_KEY (unsigned int, LockProfThreshold)
+RUNTIME_OPTIONS_KEY (std::string, StackTraceFile)
+RUNTIME_OPTIONS_KEY (Unit, MethodTrace)
+RUNTIME_OPTIONS_KEY (std::string, MethodTraceFile, "/data/method-trace-file.bin")
+RUNTIME_OPTIONS_KEY (unsigned int, MethodTraceFileSize, 10 * MB)
+RUNTIME_OPTIONS_KEY (TraceClockSource, ProfileClock, kDefaultTraceClockSource) // -Xprofile:
+RUNTIME_OPTIONS_KEY (TestProfilerOptions, ProfilerOpts) // -Xenable-profiler, -Xprofile-*
+RUNTIME_OPTIONS_KEY (std::string, Compiler)
+RUNTIME_OPTIONS_KEY (std::vector<std::string>, \
+ CompilerOptions) // -Xcompiler-option ...
+RUNTIME_OPTIONS_KEY (std::vector<std::string>, \
+ ImageCompilerOptions) // -Ximage-compiler-option ...
+RUNTIME_OPTIONS_KEY (bool, Verify, true)
+RUNTIME_OPTIONS_KEY (std::string, NativeBridge)
+
+// Not parse-able from command line, but can be provided explicitly.
+RUNTIME_OPTIONS_KEY (const std::vector<const DexFile*>*, \
+ BootClassPathDexList) // TODO: make unique_ptr
+RUNTIME_OPTIONS_KEY (InstructionSet, ImageInstructionSet, kRuntimeISA)
+RUNTIME_OPTIONS_KEY (CompilerCallbacks*, CompilerCallbacksPtr) // TDOO: make unique_ptr
+RUNTIME_OPTIONS_KEY (bool (*)(), HookIsSensitiveThread)
+RUNTIME_OPTIONS_KEY (int32_t (*)(FILE* stream, const char* format, va_list ap), \
+ HookVfprintf, vfprintf)
+RUNTIME_OPTIONS_KEY (void (*)(int32_t status), \
+ HookExit, exit)
+ // We don't call abort(3) by default; see
+ // Runtime::Abort.
+RUNTIME_OPTIONS_KEY (void (*)(), HookAbort, nullptr)
+
+
+#undef RUNTIME_OPTIONS_KEY
diff --git a/runtime/runtime_options.h b/runtime/runtime_options.h
new file mode 100644
index 0000000..ebd52d7
--- /dev/null
+++ b/runtime/runtime_options.h
@@ -0,0 +1,83 @@
+/*
+ * 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_RUNTIME_RUNTIME_OPTIONS_H_
+#define ART_RUNTIME_RUNTIME_OPTIONS_H_
+
+#include "runtime/base/variant_map.h"
+#include "cmdline/cmdline_types.h" // TODO: don't need to include this file here
+
+// Map keys
+#include <vector>
+#include <string>
+#include "runtime/base/logging.h"
+#include "cmdline/unit.h"
+#include "jdwp/jdwp.h"
+#include "gc/collector_type.h"
+#include "gc/space/large_object_space.h"
+#include "profiler_options.h"
+#include "arch/instruction_set.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+namespace art {
+
+class CompilerCallbacks;
+class DexFile;
+struct XGcOption;
+struct BackgroundGcOption;
+struct TestProfilerOptions;
+
+#define DECLARE_KEY(Type, Name) static const Key<Type> Name
+
+ // Define a key that is usable with a RuntimeArgumentMap.
+ // This key will *not* work with other subtypes of VariantMap.
+ template <typename TValue>
+ struct RuntimeArgumentMapKey : VariantMapKey<TValue> {
+ RuntimeArgumentMapKey() {}
+ explicit RuntimeArgumentMapKey(TValue default_value)
+ : VariantMapKey<TValue>(std::move(default_value)) {}
+ // Don't ODR-use constexpr default values, which means that Struct::Fields
+ // that are declared 'static constexpr T Name = Value' don't need to have a matching definition.
+ };
+
+ // Defines a type-safe heterogeneous key->value map.
+ // Use the VariantMap interface to look up or to store a RuntimeArgumentMapKey,Value pair.
+ //
+ // Example:
+ // auto map = RuntimeArgumentMap();
+ // map.Set(RuntimeArgumentMap::HeapTargetUtilization, 5.0);
+ // double *target_utilization = map.Get(RuntimeArgumentMap);
+ //
+ struct RuntimeArgumentMap : VariantMap<RuntimeArgumentMap, RuntimeArgumentMapKey> {
+ // This 'using' line is necessary to inherit the variadic constructor.
+ using VariantMap<RuntimeArgumentMap, RuntimeArgumentMapKey>::VariantMap;
+
+ // Make the next many usages of Key slightly shorter to type.
+ template <typename TValue>
+ using Key = RuntimeArgumentMapKey<TValue>;
+
+ // List of key declarations, shorthand for 'static const Key<T> Name'
+#define RUNTIME_OPTIONS_KEY(Type, Name, ...) static const Key<Type> Name;
+#include "runtime_options.def"
+ };
+
+#undef DECLARE_KEY
+
+ // using RuntimeOptions = RuntimeArgumentMap;
+} // namespace art
+
+#endif // ART_RUNTIME_RUNTIME_OPTIONS_H_
diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h
index ae3eaf2..adf3480 100644
--- a/runtime/scoped_thread_state_change.h
+++ b/runtime/scoped_thread_state_change.h
@@ -20,6 +20,7 @@
#include "base/casts.h"
#include "java_vm_ext.h"
#include "jni_env_ext-inl.h"
+#include "mirror/art_field.h"
#include "read_barrier.h"
#include "thread-inl.h"
#include "verify_object.h"
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 43714b9..b39aebf 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -113,6 +113,9 @@
}
}
+extern "C" mirror::Object* artQuickGetProxyThisObject(StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
mirror::Object* StackVisitor::GetThisObject() const {
mirror::ArtMethod* m = GetMethod();
if (m->IsStatic()) {
@@ -125,7 +128,14 @@
} else {
return cur_shadow_frame_->GetVRegReference(0);
}
- } else if (m->IsOptimized(sizeof(void*))) {
+ } else if (m->IsProxyMethod()) {
+ if (cur_quick_frame_ != nullptr) {
+ return artQuickGetProxyThisObject(cur_quick_frame_);
+ } else {
+ return cur_shadow_frame_->GetVRegReference(0);
+ }
+ } else if (m->IsOptimized(GetInstructionSetPointerSize(
+ Runtime::Current()->GetInstructionSet()))) {
// TODO: Implement, currently only used for exceptions when jdwp is enabled.
UNIMPLEMENTED(WARNING)
<< "StackVisitor::GetThisObject is unimplemented with the optimizing compiler";
@@ -163,11 +173,10 @@
bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
uint32_t reg = vmap_table.ComputeRegister(spill_mask, vmap_offset, kind);
- uintptr_t ptr_val;
- bool success = is_float ? GetFPR(reg, &ptr_val) : GetGPR(reg, &ptr_val);
- if (!success) {
+ if (!IsAccessibleRegister(reg, is_float)) {
return false;
}
+ uintptr_t ptr_val = GetRegister(reg, is_float);
bool target64 = Is64BitInstructionSet(kRuntimeISA);
if (target64) {
bool wide_lo = (kind == kLongLoVReg) || (kind == kDoubleLoVReg);
@@ -185,11 +194,14 @@
const DexFile::CodeItem* code_item = m->GetCodeItem();
DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be NULL or how would we compile
// its instructions?
- *val = *GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
- frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
+ uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
+ frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
+ DCHECK(addr != nullptr);
+ *val = *addr;
return true;
}
} else {
+ DCHECK(cur_shadow_frame_ != nullptr);
*val = cur_shadow_frame_->GetVReg(vreg);
return true;
}
@@ -219,12 +231,11 @@
uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
uint32_t reg_lo = vmap_table.ComputeRegister(spill_mask, vmap_offset_lo, kind_lo);
uint32_t reg_hi = vmap_table.ComputeRegister(spill_mask, vmap_offset_hi, kind_hi);
- uintptr_t ptr_val_lo, ptr_val_hi;
- bool success = is_float ? GetFPR(reg_lo, &ptr_val_lo) : GetGPR(reg_lo, &ptr_val_lo);
- success &= is_float ? GetFPR(reg_hi, &ptr_val_hi) : GetGPR(reg_hi, &ptr_val_hi);
- if (!success) {
+ if (!IsAccessibleRegister(reg_lo, is_float) || !IsAccessibleRegister(reg_hi, is_float)) {
return false;
}
+ uintptr_t ptr_val_lo = GetRegister(reg_lo, is_float);
+ uintptr_t ptr_val_hi = GetRegister(reg_hi, is_float);
bool target64 = Is64BitInstructionSet(kRuntimeISA);
if (target64) {
int64_t value_long_lo = static_cast<int64_t>(ptr_val_lo);
@@ -240,10 +251,12 @@
// its instructions?
uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
+ DCHECK(addr != nullptr);
*val = *reinterpret_cast<uint64_t*>(addr);
return true;
}
} else {
+ DCHECK(cur_shadow_frame_ != nullptr);
*val = cur_shadow_frame_->GetVRegLong(vreg);
return true;
}
@@ -264,17 +277,16 @@
bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
const uint32_t reg = vmap_table.ComputeRegister(spill_mask, vmap_offset, kind);
+ if (!IsAccessibleRegister(reg, is_float)) {
+ return false;
+ }
bool target64 = Is64BitInstructionSet(kRuntimeISA);
// Deal with 32 or 64-bit wide registers in a way that builds on all targets.
if (target64) {
bool wide_lo = (kind == kLongLoVReg) || (kind == kDoubleLoVReg);
bool wide_hi = (kind == kLongHiVReg) || (kind == kDoubleHiVReg);
if (wide_lo || wide_hi) {
- uintptr_t old_reg_val;
- bool success = is_float ? GetFPR(reg, &old_reg_val) : GetGPR(reg, &old_reg_val);
- if (!success) {
- return false;
- }
+ uintptr_t old_reg_val = GetRegister(reg, is_float);
uint64_t new_vreg_portion = static_cast<uint64_t>(new_value);
uint64_t old_reg_val_as_wide = static_cast<uint64_t>(old_reg_val);
uint64_t mask = 0xffffffff;
@@ -286,21 +298,20 @@
new_value = static_cast<uintptr_t>((old_reg_val_as_wide & mask) | new_vreg_portion);
}
}
- if (is_float) {
- return SetFPR(reg, new_value);
- } else {
- return SetGPR(reg, new_value);
- }
+ SetRegister(reg, new_value, is_float);
+ return true;
} else {
const DexFile::CodeItem* code_item = m->GetCodeItem();
DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be NULL or how would we compile
// its instructions?
uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
+ DCHECK(addr != nullptr);
*addr = new_value;
return true;
}
} else {
+ DCHECK(cur_shadow_frame_ != nullptr);
cur_shadow_frame_->SetVReg(vreg, new_value);
return true;
}
@@ -330,17 +341,16 @@
uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
uint32_t reg_lo = vmap_table.ComputeRegister(spill_mask, vmap_offset_lo, kind_lo);
uint32_t reg_hi = vmap_table.ComputeRegister(spill_mask, vmap_offset_hi, kind_hi);
+ if (!IsAccessibleRegister(reg_lo, is_float) || !IsAccessibleRegister(reg_hi, is_float)) {
+ return false;
+ }
uintptr_t new_value_lo = static_cast<uintptr_t>(new_value & 0xFFFFFFFF);
uintptr_t new_value_hi = static_cast<uintptr_t>(new_value >> 32);
bool target64 = Is64BitInstructionSet(kRuntimeISA);
// Deal with 32 or 64-bit wide registers in a way that builds on all targets.
if (target64) {
- uintptr_t old_reg_val_lo, old_reg_val_hi;
- bool success = is_float ? GetFPR(reg_lo, &old_reg_val_lo) : GetGPR(reg_lo, &old_reg_val_lo);
- success &= is_float ? GetFPR(reg_hi, &old_reg_val_hi) : GetGPR(reg_hi, &old_reg_val_hi);
- if (!success) {
- return false;
- }
+ uintptr_t old_reg_val_lo = GetRegister(reg_lo, is_float);
+ uintptr_t old_reg_val_hi = GetRegister(reg_hi, is_float);
uint64_t new_vreg_portion_lo = static_cast<uint64_t>(new_value_lo);
uint64_t new_vreg_portion_hi = static_cast<uint64_t>(new_value_hi) << 32;
uint64_t old_reg_val_lo_as_wide = static_cast<uint64_t>(old_reg_val_lo);
@@ -350,47 +360,64 @@
new_value_lo = static_cast<uintptr_t>((old_reg_val_lo_as_wide & mask_lo) | new_vreg_portion_lo);
new_value_hi = static_cast<uintptr_t>((old_reg_val_hi_as_wide & mask_hi) | new_vreg_portion_hi);
}
- bool success = is_float ? SetFPR(reg_lo, new_value_lo) : SetGPR(reg_lo, new_value_lo);
- success &= is_float ? SetFPR(reg_hi, new_value_hi) : SetGPR(reg_hi, new_value_hi);
- return success;
+ SetRegister(reg_lo, new_value_lo, is_float);
+ SetRegister(reg_hi, new_value_hi, is_float);
+ return true;
} else {
const DexFile::CodeItem* code_item = m->GetCodeItem();
DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be NULL or how would we compile
// its instructions?
uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
+ DCHECK(addr != nullptr);
*reinterpret_cast<uint64_t*>(addr) = new_value;
return true;
}
} else {
+ DCHECK(cur_shadow_frame_ != nullptr);
cur_shadow_frame_->SetVRegLong(vreg, new_value);
return true;
}
}
+bool StackVisitor::IsAccessibleGPR(uint32_t reg) const {
+ DCHECK(context_ != nullptr);
+ return context_->IsAccessibleGPR(reg);
+}
+
uintptr_t* StackVisitor::GetGPRAddress(uint32_t reg) const {
- DCHECK(cur_quick_frame_ != NULL) << "This is a quick frame routine";
+ DCHECK(cur_quick_frame_ != nullptr) << "This is a quick frame routine";
+ DCHECK(context_ != nullptr);
return context_->GetGPRAddress(reg);
}
-bool StackVisitor::GetGPR(uint32_t reg, uintptr_t* val) const {
- DCHECK(cur_quick_frame_ != NULL) << "This is a quick frame routine";
- return context_->GetGPR(reg, val);
+uintptr_t StackVisitor::GetGPR(uint32_t reg) const {
+ DCHECK(cur_quick_frame_ != nullptr) << "This is a quick frame routine";
+ DCHECK(context_ != nullptr);
+ return context_->GetGPR(reg);
}
-bool StackVisitor::SetGPR(uint32_t reg, uintptr_t value) {
- DCHECK(cur_quick_frame_ != NULL) << "This is a quick frame routine";
- return context_->SetGPR(reg, value);
+void StackVisitor::SetGPR(uint32_t reg, uintptr_t value) {
+ DCHECK(cur_quick_frame_ != nullptr) << "This is a quick frame routine";
+ DCHECK(context_ != nullptr);
+ context_->SetGPR(reg, value);
}
-bool StackVisitor::GetFPR(uint32_t reg, uintptr_t* val) const {
- DCHECK(cur_quick_frame_ != NULL) << "This is a quick frame routine";
- return context_->GetFPR(reg, val);
+bool StackVisitor::IsAccessibleFPR(uint32_t reg) const {
+ DCHECK(context_ != nullptr);
+ return context_->IsAccessibleFPR(reg);
}
-bool StackVisitor::SetFPR(uint32_t reg, uintptr_t value) {
- DCHECK(cur_quick_frame_ != NULL) << "This is a quick frame routine";
- return context_->SetFPR(reg, value);
+uintptr_t StackVisitor::GetFPR(uint32_t reg) const {
+ DCHECK(cur_quick_frame_ != nullptr) << "This is a quick frame routine";
+ DCHECK(context_ != nullptr);
+ return context_->GetFPR(reg);
+}
+
+void StackVisitor::SetFPR(uint32_t reg, uintptr_t value) {
+ DCHECK(cur_quick_frame_ != nullptr) << "This is a quick frame routine";
+ DCHECK(context_ != nullptr);
+ context_->SetFPR(reg, value);
}
uintptr_t StackVisitor::GetReturnPc() const {
@@ -605,4 +632,11 @@
}
}
+void JavaFrameRootInfo::Describe(std::ostream& os) const {
+ const StackVisitor* visitor = stack_visitor_;
+ CHECK(visitor != nullptr);
+ os << "Type=" << GetType() << " thread_id=" << GetThreadId() << " location=" <<
+ visitor->DescribeLocation() << " vreg=" << vreg_;
+}
+
} // namespace art
diff --git a/runtime/stack.h b/runtime/stack.h
index 1d772e6..5a86ca1 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -22,7 +22,9 @@
#include "arch/instruction_set.h"
#include "dex_file.h"
+#include "gc_root.h"
#include "mirror/object_reference.h"
+#include "read_barrier.h"
#include "throw_location.h"
#include "utils.h"
#include "verify_object.h"
@@ -72,8 +74,7 @@
: mirror::ObjectReference<false, MirrorType>(p) {}
};
-// ShadowFrame has 3 possible layouts:
-// - portable - a unified array of VRegs and references. Precise references need GC maps.
+// ShadowFrame has 2 possible layouts:
// - interpreter - separate VRegs and reference arrays. References are in the reference array.
// - JNI - just VRegs, but where every VReg holds a reference.
class ShadowFrame {
@@ -100,28 +101,11 @@
~ShadowFrame() {}
bool HasReferenceArray() const {
-#if defined(ART_USE_PORTABLE_COMPILER)
- return (number_of_vregs_ & kHasReferenceArray) != 0;
-#else
return true;
-#endif
}
uint32_t NumberOfVRegs() const {
-#if defined(ART_USE_PORTABLE_COMPILER)
- return number_of_vregs_ & ~kHasReferenceArray;
-#else
return number_of_vregs_;
-#endif
- }
-
- void SetNumberOfVRegs(uint32_t number_of_vregs) {
-#if defined(ART_USE_PORTABLE_COMPILER)
- number_of_vregs_ = number_of_vregs | (number_of_vregs_ & kHasReferenceArray);
-#else
- UNUSED(number_of_vregs);
- UNIMPLEMENTED(FATAL) << "Should only be called when portable is enabled";
-#endif
}
uint32_t GetDexPC() const {
@@ -180,6 +164,9 @@
const uint32_t* vreg_ptr = &vregs_[i];
ref = reinterpret_cast<const StackReference<mirror::Object>*>(vreg_ptr)->AsMirrorPtr();
}
+ if (kUseReadBarrier) {
+ ReadBarrier::AssertToSpaceInvariant(ref);
+ }
if (kVerifyFlags & kVerifyReads) {
VerifyObject(ref);
}
@@ -247,6 +234,9 @@
if (kVerifyFlags & kVerifyWrites) {
VerifyObject(val);
}
+ if (kUseReadBarrier) {
+ ReadBarrier::AssertToSpaceInvariant(val);
+ }
uint32_t* vreg = &vregs_[i];
reinterpret_cast<StackReference<mirror::Object>*>(vreg)->Assign(val);
if (HasReferenceArray()) {
@@ -270,16 +260,6 @@
ThrowLocation GetCurrentLocationForThrow() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SetMethod(mirror::ArtMethod* method) {
-#if defined(ART_USE_PORTABLE_COMPILER)
- DCHECK(method != nullptr);
- method_ = method;
-#else
- UNUSED(method);
- UNIMPLEMENTED(FATAL) << "Should only be called when portable is enabled";
-#endif
- }
-
bool Contains(StackReference<mirror::Object>* shadow_frame_entry_obj) const {
if (HasReferenceArray()) {
return ((&References()[0] <= shadow_frame_entry_obj) &&
@@ -316,10 +296,6 @@
uint32_t dex_pc, bool has_reference_array)
: number_of_vregs_(num_vregs), link_(link), method_(method), dex_pc_(dex_pc) {
if (has_reference_array) {
-#if defined(ART_USE_PORTABLE_COMPILER)
- CHECK_LT(num_vregs, static_cast<uint32_t>(kHasReferenceArray));
- number_of_vregs_ |= kHasReferenceArray;
-#endif
memset(vregs_, 0, num_vregs * (sizeof(uint32_t) + sizeof(StackReference<mirror::Object>)));
} else {
memset(vregs_, 0, num_vregs * sizeof(uint32_t));
@@ -336,13 +312,7 @@
return const_cast<StackReference<mirror::Object>*>(const_cast<const ShadowFrame*>(this)->References());
}
-#if defined(ART_USE_PORTABLE_COMPILER)
- constexpr uint32_t kHasReferenceArray = 1ul << 31;
- // TODO: make const in the portable case.
- uint32_t number_of_vregs_;
-#else
const uint32_t number_of_vregs_;
-#endif
// Link to previous shadow frame or NULL.
ShadowFrame* link_;
mirror::ArtMethod* method_;
@@ -352,6 +322,19 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(ShadowFrame);
};
+class JavaFrameRootInfo : public RootInfo {
+ public:
+ JavaFrameRootInfo(uint32_t thread_id, const StackVisitor* stack_visitor, size_t vreg)
+ : RootInfo(kRootJavaFrame, thread_id), stack_visitor_(stack_visitor), vreg_(vreg) {
+ }
+ virtual void Describe(std::ostream& os) const OVERRIDE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ private:
+ const StackVisitor* const stack_visitor_;
+ const size_t vreg_;
+};
+
// The managed stack is used to record fragments of managed code stacks. Managed code stacks
// may either be shadow frames or lists of frames using fixed frame sizes. Transition records are
// necessary for transitions between code using different frame layouts and transitions into native
@@ -666,10 +649,29 @@
StackVisitor(Thread* thread, Context* context, size_t num_frames)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool GetGPR(uint32_t reg, uintptr_t* val) const;
- bool SetGPR(uint32_t reg, uintptr_t value);
- bool GetFPR(uint32_t reg, uintptr_t* val) const;
- bool SetFPR(uint32_t reg, uintptr_t value);
+ bool IsAccessibleRegister(uint32_t reg, bool is_float) const {
+ return is_float ? IsAccessibleFPR(reg) : IsAccessibleGPR(reg);
+ }
+ uintptr_t GetRegister(uint32_t reg, bool is_float) const {
+ DCHECK(IsAccessibleRegister(reg, is_float));
+ return is_float ? GetFPR(reg) : GetGPR(reg);
+ }
+ void SetRegister(uint32_t reg, uintptr_t value, bool is_float) {
+ DCHECK(IsAccessibleRegister(reg, is_float));
+ if (is_float) {
+ SetFPR(reg, value);
+ } else {
+ SetGPR(reg, value);
+ }
+ }
+
+ bool IsAccessibleGPR(uint32_t reg) const;
+ uintptr_t GetGPR(uint32_t reg) const;
+ void SetGPR(uint32_t reg, uintptr_t value);
+
+ bool IsAccessibleFPR(uint32_t reg) const;
+ uintptr_t GetFPR(uint32_t reg) const;
+ void SetFPR(uint32_t reg, uintptr_t value);
void SanityCheckFrame() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index a58ecab..7cc3e57 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -19,6 +19,7 @@
#include "base/bit_vector.h"
#include "memory_region.h"
+#include "utils.h"
namespace art {
@@ -199,6 +200,11 @@
&& region_.size() == other.region_.size();
}
+ static size_t ComputeAlignedStackMapSize(size_t stack_mask_size) {
+ // On ARM, the stack maps must be 4-byte aligned.
+ return RoundUp(StackMap::kFixedSize + stack_mask_size, 4);
+ }
+
private:
static constexpr int kDexPcOffset = 0;
static constexpr int kNativePcOffsetOffset = kDexPcOffset + sizeof(uint32_t);
@@ -262,7 +268,7 @@
}
size_t StackMapSize() const {
- return StackMap::kFixedSize + GetStackMaskSize();
+ return StackMap::ComputeAlignedStackMapSize(GetStackMaskSize());
}
DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, uint32_t number_of_dex_registers) {
diff --git a/runtime/strutil.h b/runtime/strutil.h
deleted file mode 100644
index c8d39e2..0000000
--- a/runtime/strutil.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_STRUTIL_H_
-#define ART_RUNTIME_STRUTIL_H_
-
-#include <string.h>
-
-namespace art {
-
-// Key comparison function for C strings.
-struct CStringLt {
- bool operator()(const char* s1, const char* s2) const {
- return strcmp(s1, s2) < 0;
- }
-};
-
-// Key equality function for C strings.
-struct CStringEq {
- bool operator()(const char* s1, const char* s2) const {
- return strcmp(s1, s2) == 0;
- }
-};
-
-} // namespace art
-
-#endif // ART_RUNTIME_STRUTIL_H_
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index 7aed8b0..16add79 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -178,6 +178,11 @@
// Failed to transition to Runnable. Release shared mutator_lock_ access and try again.
Locks::mutator_lock_->SharedUnlock(this);
} else {
+ // Run the flip function, if set.
+ Closure* flip_func = GetFlipFunction();
+ if (flip_func != nullptr) {
+ flip_func->Run(this);
+ }
return static_cast<ThreadState>(old_state);
}
} while (true);
@@ -208,22 +213,23 @@
if (tlsPtr_.thread_local_alloc_stack_top < tlsPtr_.thread_local_alloc_stack_end) {
// There's room.
DCHECK_LE(reinterpret_cast<uint8_t*>(tlsPtr_.thread_local_alloc_stack_top) +
- sizeof(mirror::Object*),
+ sizeof(StackReference<mirror::Object>),
reinterpret_cast<uint8_t*>(tlsPtr_.thread_local_alloc_stack_end));
- DCHECK(*tlsPtr_.thread_local_alloc_stack_top == nullptr);
- *tlsPtr_.thread_local_alloc_stack_top = obj;
+ DCHECK(tlsPtr_.thread_local_alloc_stack_top->AsMirrorPtr() == nullptr);
+ tlsPtr_.thread_local_alloc_stack_top->Assign(obj);
++tlsPtr_.thread_local_alloc_stack_top;
return true;
}
return false;
}
-inline void Thread::SetThreadLocalAllocationStack(mirror::Object** start, mirror::Object** end) {
+inline void Thread::SetThreadLocalAllocationStack(StackReference<mirror::Object>* start,
+ StackReference<mirror::Object>* end) {
DCHECK(Thread::Current() == this) << "Should be called by self";
DCHECK(start != nullptr);
DCHECK(end != nullptr);
- DCHECK_ALIGNED(start, sizeof(mirror::Object*));
- DCHECK_ALIGNED(end, sizeof(mirror::Object*));
+ DCHECK_ALIGNED(start, sizeof(StackReference<mirror::Object>));
+ DCHECK_ALIGNED(end, sizeof(StackReference<mirror::Object>));
DCHECK_LT(start, end);
tlsPtr_.thread_local_alloc_stack_end = end;
tlsPtr_.thread_local_alloc_stack_top = start;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index f7c7106..16edab3 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -33,6 +33,7 @@
#include "arch/context.h"
#include "base/mutex.h"
+#include "base/timing_logger.h"
#include "base/to_str.h"
#include "class_linker-inl.h"
#include "class_linker.h"
@@ -91,7 +92,7 @@
}
void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
- PortableEntryPoints* ppoints, QuickEntryPoints* qpoints);
+ QuickEntryPoints* qpoints);
void Thread::InitTlsEntryPoints() {
// Insert a placeholder so we can easily tell if we call an unimplemented entry point.
@@ -102,7 +103,7 @@
*it = reinterpret_cast<uintptr_t>(UnimplementedEntryPoint);
}
InitEntryPoints(&tlsPtr_.interpreter_entrypoints, &tlsPtr_.jni_entrypoints,
- &tlsPtr_.portable_entrypoints, &tlsPtr_.quick_entrypoints);
+ &tlsPtr_.quick_entrypoints);
}
void Thread::ResetQuickAllocEntryPointsForThread() {
@@ -515,12 +516,6 @@
size_t read_guard_size;
GetThreadStack(tlsPtr_.pthread_self, &read_stack_base, &read_stack_size, &read_guard_size);
- // This is included in the SIGQUIT output, but it's useful here for thread debugging.
- VLOG(threads) << StringPrintf("Native stack is at %p (%s with %s guard)",
- read_stack_base,
- PrettySize(read_stack_size).c_str(),
- PrettySize(read_guard_size).c_str());
-
tlsPtr_.stack_begin = reinterpret_cast<uint8_t*>(read_stack_base);
tlsPtr_.stack_size = read_stack_size;
@@ -537,6 +532,12 @@
return false;
}
+ // This is included in the SIGQUIT output, but it's useful here for thread debugging.
+ VLOG(threads) << StringPrintf("Native stack is at %p (%s with %s guard)",
+ read_stack_base,
+ PrettySize(read_stack_size).c_str(),
+ PrettySize(read_guard_size).c_str());
+
// Set stack_end_ to the bottom of the stack saving space of stack overflows
Runtime* runtime = Runtime::Current();
@@ -592,13 +593,13 @@
}
uint64_t Thread::GetCpuMicroTime() const {
-#if defined(HAVE_POSIX_CLOCKS)
+#if defined(__linux__)
clockid_t cpu_clock_id;
pthread_getcpuclockid(tlsPtr_.pthread_self, &cpu_clock_id);
timespec now;
clock_gettime(cpu_clock_id, &now);
return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000) + now.tv_nsec / UINT64_C(1000);
-#else
+#else // __APPLE__
UNIMPLEMENTED(WARNING);
return -1;
#endif
@@ -723,13 +724,34 @@
return success;
}
+Closure* Thread::GetFlipFunction() {
+ Atomic<Closure*>* atomic_func = reinterpret_cast<Atomic<Closure*>*>(&tlsPtr_.flip_function);
+ Closure* func;
+ do {
+ func = atomic_func->LoadRelaxed();
+ if (func == nullptr) {
+ return nullptr;
+ }
+ } while (!atomic_func->CompareExchangeWeakSequentiallyConsistent(func, nullptr));
+ DCHECK(func != nullptr);
+ return func;
+}
+
+void Thread::SetFlipFunction(Closure* function) {
+ CHECK(function != nullptr);
+ Atomic<Closure*>* atomic_func = reinterpret_cast<Atomic<Closure*>*>(&tlsPtr_.flip_function);
+ atomic_func->StoreSequentiallyConsistent(function);
+}
+
void Thread::FullSuspendCheck() {
VLOG(threads) << this << " self-suspending";
ATRACE_BEGIN("Full suspend check");
// Make thread appear suspended to other threads, release mutator_lock_.
+ tls32_.suspended_at_suspend_check = true;
TransitionFromRunnableToSuspended(kSuspended);
// Transition back to runnable noting requests to suspend, re-acquire share on mutator_lock_.
TransitionFromSuspendedToRunnable();
+ tls32_.suspended_at_suspend_check = false;
ATRACE_END();
VLOG(threads) << this << " self-reviving";
}
@@ -740,6 +762,20 @@
bool is_daemon = false;
Thread* self = Thread::Current();
+ // If flip_function is not null, it means we have run a checkpoint
+ // before the thread wakes up to execute the flip function and the
+ // thread roots haven't been forwarded. So the following access to
+ // the roots (opeer or methods in the frames) would be bad. Run it
+ // here. TODO: clean up.
+ if (thread != nullptr) {
+ ScopedObjectAccessUnchecked soa(self);
+ Thread* this_thread = const_cast<Thread*>(thread);
+ Closure* flip_func = this_thread->GetFlipFunction();
+ if (flip_func != nullptr) {
+ flip_func->Run(this_thread);
+ }
+ }
+
// Don't do this if we are aborting since the GC may have all the threads suspended. This will
// cause ScopedObjectAccessUnchecked to deadlock.
if (gAborting == 0 && self != nullptr && thread != nullptr && thread->tlsPtr_.opeer != nullptr) {
@@ -932,7 +968,10 @@
os << StringPrintf("<@addr=0x%" PRIxPTR "> (a %s)", reinterpret_cast<intptr_t>(o),
PrettyTypeOf(o).c_str());
} else {
- os << StringPrintf("<0x%08x> (a %s)", o->IdentityHashCode(), PrettyTypeOf(o).c_str());
+ // IdentityHashCode can cause thread suspension, which would invalidate o if it moved. So
+ // we get the pretty type beofre we call IdentityHashCode.
+ const std::string pretty_type(PrettyTypeOf(o));
+ os << StringPrintf("<0x%08x> (a %s)", o->IdentityHashCode(), pretty_type.c_str());
}
}
os << "\n";
@@ -977,10 +1016,24 @@
}
void Thread::DumpJavaStack(std::ostream& os) const {
+ // If flip_function is not null, it means we have run a checkpoint
+ // before the thread wakes up to execute the flip function and the
+ // thread roots haven't been forwarded. So the following access to
+ // the roots (locks or methods in the frames) would be bad. Run it
+ // here. TODO: clean up.
+ {
+ Thread* this_thread = const_cast<Thread*>(this);
+ Closure* flip_func = this_thread->GetFlipFunction();
+ if (flip_func != nullptr) {
+ flip_func->Run(this_thread);
+ }
+ }
+
// Dumping the Java stack involves the verifier for locks. The verifier operates under the
// assumption that there is no exception pending on entry. Thus, stash any pending exception.
- // TODO: Find a way to avoid const_cast.
- StackHandleScope<3> scope(const_cast<Thread*>(this));
+ // Thread::Current() instead of this in case a thread is dumping the stack of another suspended
+ // thread.
+ StackHandleScope<3> scope(Thread::Current());
Handle<mirror::Throwable> exc;
Handle<mirror::Object> throw_location_this_object;
Handle<mirror::ArtMethod> throw_location_method;
@@ -1096,7 +1149,7 @@
wait_mutex_ = new Mutex("a thread wait mutex");
wait_cond_ = new ConditionVariable("a thread wait condition variable", *wait_mutex_);
tlsPtr_.debug_invoke_req = new DebugInvokeReq;
- tlsPtr_.single_step_control = new SingleStepControl;
+ tlsPtr_.single_step_control = nullptr;
tlsPtr_.instrumentation_stack = new std::deque<instrumentation::InstrumentationStackFrame>;
tlsPtr_.name = new std::string(kThreadNameDuringStartup);
tlsPtr_.nested_signal_state = static_cast<jmp_buf*>(malloc(sizeof(jmp_buf)));
@@ -1111,6 +1164,8 @@
for (uint32_t i = 0; i < kMaxCheckpoints; ++i) {
tlsPtr_.checkpoint_functions[i] = nullptr;
}
+ tlsPtr_.flip_function = nullptr;
+ tls32_.suspended_at_suspend_check = false;
}
bool Thread::IsStillStarting() const {
@@ -1147,8 +1202,7 @@
}
}
-static void MonitorExitVisitor(mirror::Object** object, void* arg, uint32_t /*thread_id*/,
- RootType /*root_type*/)
+static void MonitorExitVisitor(mirror::Object** object, void* arg, const RootInfo& /*root_info*/)
NO_THREAD_SAFETY_ANALYSIS {
Thread* self = reinterpret_cast<Thread*>(arg);
mirror::Object* entered_monitor = *object;
@@ -1167,7 +1221,7 @@
if (tlsPtr_.jni_env != nullptr) {
// On thread detach, all monitors entered with JNI MonitorEnter are automatically exited.
- tlsPtr_.jni_env->monitors.VisitRoots(MonitorExitVisitor, self, 0, kRootVMInternal);
+ tlsPtr_.jni_env->monitors.VisitRoots(MonitorExitVisitor, self, RootInfo(kRootVMInternal));
// Release locally held global references which releasing may require the mutator lock.
if (tlsPtr_.jpeer != nullptr) {
// If pthread_create fails we don't have a jni env here.
@@ -1210,7 +1264,10 @@
tlsPtr_.opeer = nullptr;
}
- Runtime::Current()->GetHeap()->RevokeThreadLocalBuffers(this);
+ {
+ ScopedObjectAccess soa(self);
+ Runtime::Current()->GetHeap()->RevokeThreadLocalBuffers(this);
+ }
}
Thread::~Thread() {
@@ -1227,6 +1284,8 @@
CHECK(tlsPtr_.checkpoint_functions[0] == nullptr);
CHECK(tlsPtr_.checkpoint_functions[1] == nullptr);
CHECK(tlsPtr_.checkpoint_functions[2] == nullptr);
+ CHECK(tlsPtr_.flip_function == nullptr);
+ CHECK_EQ(tls32_.suspended_at_suspend_check, false);
// We may be deleting a still born thread.
SetStateUnsafe(kTerminated);
@@ -1243,7 +1302,9 @@
}
delete tlsPtr_.debug_invoke_req;
- delete tlsPtr_.single_step_control;
+ if (tlsPtr_.single_step_control != nullptr) {
+ delete tlsPtr_.single_step_control;
+ }
delete tlsPtr_.instrumentation_stack;
delete tlsPtr_.name;
delete tlsPtr_.stack_trace_sample;
@@ -1326,7 +1387,7 @@
mirror::Object* object = cur->GetReference(j);
if (object != nullptr) {
mirror::Object* old_obj = object;
- visitor(&object, arg, thread_id, kRootNativeStack);
+ visitor(&object, arg, RootInfo(kRootNativeStack, thread_id));
if (old_obj != object) {
cur->SetReference(j, object);
}
@@ -1336,7 +1397,6 @@
}
mirror::Object* Thread::DecodeJObject(jobject obj) const {
- Locks::mutator_lock_->AssertSharedHeld(this);
if (obj == nullptr) {
return nullptr;
}
@@ -1865,16 +1925,6 @@
JNI_ENTRY_POINT_INFO(pDlsymLookup)
#undef JNI_ENTRY_POINT_INFO
-#define PORTABLE_ENTRY_POINT_INFO(x) \
- if (PORTABLE_ENTRYPOINT_OFFSET(ptr_size, x).Uint32Value() == offset) { \
- os << #x; \
- return; \
- }
- PORTABLE_ENTRY_POINT_INFO(pPortableImtConflictTrampoline)
- PORTABLE_ENTRY_POINT_INFO(pPortableResolutionTrampoline)
- PORTABLE_ENTRY_POINT_INFO(pPortableToInterpreterBridge)
-#undef PORTABLE_ENTRY_POINT_INFO
-
#define QUICK_ENTRY_POINT_INFO(x) \
if (QUICK_ENTRYPOINT_OFFSET(ptr_size, x).Uint32Value() == offset) { \
os << #x; \
@@ -2142,6 +2192,7 @@
uintptr_t native_pc_offset = m->NativeQuickPcOffset(GetCurrentQuickFramePc(), entry_point);
StackMap map = m->GetStackMap(native_pc_offset);
MemoryRegion mask = map.GetStackMask();
+ // Visit stack entries that hold pointers.
for (size_t i = 0; i < mask.size_in_bits(); ++i) {
if (mask.LoadBit(i)) {
StackReference<mirror::Object>* ref_addr =
@@ -2156,6 +2207,16 @@
}
}
}
+ // Visit callee-save registers that hold pointers.
+ uint32_t register_mask = map.GetRegisterMask();
+ for (size_t i = 0; i < BitSizeOf<uint32_t>(); ++i) {
+ if (register_mask & (1 << i)) {
+ mirror::Object** ref_addr = reinterpret_cast<mirror::Object**>(GetGPRAddress(i));
+ if (*ref_addr != nullptr) {
+ visitor_(ref_addr, -1, this);
+ }
+ }
+ }
} else {
const uint8_t* native_gc_map = m->GetNativeGcMap(sizeof(void*));
CHECK(native_gc_map != nullptr) << PrettyMethod(m);
@@ -2223,8 +2284,8 @@
RootCallbackVisitor(RootCallback* callback, void* arg, uint32_t tid)
: callback_(callback), arg_(arg), tid_(tid) {}
- void operator()(mirror::Object** obj, size_t, const StackVisitor*) const {
- callback_(obj, arg_, tid_, kRootJavaFrame);
+ void operator()(mirror::Object** obj, size_t vreg, const StackVisitor* stack_visitor) const {
+ callback_(obj, arg_, JavaFrameRootInfo(tid_, stack_visitor, vreg));
}
private:
@@ -2236,23 +2297,24 @@
void Thread::VisitRoots(RootCallback* visitor, void* arg) {
uint32_t thread_id = GetThreadId();
if (tlsPtr_.opeer != nullptr) {
- visitor(&tlsPtr_.opeer, arg, thread_id, kRootThreadObject);
+ visitor(&tlsPtr_.opeer, arg, RootInfo(kRootThreadObject, thread_id));
}
if (tlsPtr_.exception != nullptr && tlsPtr_.exception != GetDeoptimizationException()) {
- visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.exception), arg, thread_id, kRootNativeStack);
+ visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.exception), arg,
+ RootInfo(kRootNativeStack, thread_id));
}
tlsPtr_.throw_location.VisitRoots(visitor, arg);
if (tlsPtr_.monitor_enter_object != nullptr) {
- visitor(&tlsPtr_.monitor_enter_object, arg, thread_id, kRootNativeStack);
+ visitor(&tlsPtr_.monitor_enter_object, arg, RootInfo(kRootNativeStack, thread_id));
}
- tlsPtr_.jni_env->locals.VisitRoots(visitor, arg, thread_id, kRootJNILocal);
- tlsPtr_.jni_env->monitors.VisitRoots(visitor, arg, thread_id, kRootJNIMonitor);
+ tlsPtr_.jni_env->locals.VisitRoots(visitor, arg, RootInfo(kRootJNILocal, thread_id));
+ tlsPtr_.jni_env->monitors.VisitRoots(visitor, arg, RootInfo(kRootJNIMonitor, thread_id));
HandleScopeVisitRoots(visitor, arg, thread_id);
if (tlsPtr_.debug_invoke_req != nullptr) {
- tlsPtr_.debug_invoke_req->VisitRoots(visitor, arg, thread_id, kRootDebugger);
+ tlsPtr_.debug_invoke_req->VisitRoots(visitor, arg, RootInfo(kRootDebugger, thread_id));
}
if (tlsPtr_.single_step_control != nullptr) {
- tlsPtr_.single_step_control->VisitRoots(visitor, arg, thread_id, kRootDebugger);
+ tlsPtr_.single_step_control->VisitRoots(visitor, arg, RootInfo(kRootDebugger, thread_id));
}
if (tlsPtr_.deoptimization_shadow_frame != nullptr) {
RootCallbackVisitor visitorToCallback(visitor, arg, thread_id);
@@ -2263,8 +2325,8 @@
}
}
if (tlsPtr_.shadow_frame_under_construction != nullptr) {
- RootCallbackVisitor visitorToCallback(visitor, arg, thread_id);
- ReferenceMapVisitor<RootCallbackVisitor> mapper(this, nullptr, visitorToCallback);
+ RootCallbackVisitor visitor_to_callback(visitor, arg, thread_id);
+ ReferenceMapVisitor<RootCallbackVisitor> mapper(this, nullptr, visitor_to_callback);
for (ShadowFrame* shadow_frame = tlsPtr_.shadow_frame_under_construction;
shadow_frame != nullptr;
shadow_frame = shadow_frame->GetLink()) {
@@ -2273,21 +2335,22 @@
}
// Visit roots on this thread's stack
Context* context = GetLongJumpContext();
- RootCallbackVisitor visitorToCallback(visitor, arg, thread_id);
- ReferenceMapVisitor<RootCallbackVisitor> mapper(this, context, visitorToCallback);
+ RootCallbackVisitor visitor_to_callback(visitor, arg, thread_id);
+ ReferenceMapVisitor<RootCallbackVisitor> mapper(this, context, visitor_to_callback);
mapper.WalkStack();
ReleaseLongJumpContext(context);
for (instrumentation::InstrumentationStackFrame& frame : *GetInstrumentationStack()) {
if (frame.this_object_ != nullptr) {
- visitor(&frame.this_object_, arg, thread_id, kRootJavaFrame);
+ visitor(&frame.this_object_, arg, RootInfo(kRootVMInternal, thread_id));
}
DCHECK(frame.method_ != nullptr);
- visitor(reinterpret_cast<mirror::Object**>(&frame.method_), arg, thread_id, kRootJavaFrame);
+ visitor(reinterpret_cast<mirror::Object**>(&frame.method_), arg,
+ RootInfo(kRootVMInternal, thread_id));
}
}
-static void VerifyRoot(mirror::Object** root, void* /*arg*/, uint32_t /*thread_id*/,
- RootType /*root_type*/) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+static void VerifyRoot(mirror::Object** root, void* /*arg*/, const RootInfo& /*root_info*/)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
VerifyObject(*root);
}
@@ -2359,5 +2422,19 @@
return mprotect(pregion, kStackOverflowProtectedSize, PROT_READ|PROT_WRITE) == 0;
}
+void Thread::ActivateSingleStepControl(SingleStepControl* ssc) {
+ CHECK(Dbg::IsDebuggerActive());
+ CHECK(GetSingleStepControl() == nullptr) << "Single step already active in thread " << *this;
+ CHECK(ssc != nullptr);
+ tlsPtr_.single_step_control = ssc;
+}
+
+void Thread::DeactivateSingleStepControl() {
+ CHECK(Dbg::IsDebuggerActive());
+ CHECK(GetSingleStepControl() != nullptr) << "Single step not active in thread " << *this;
+ SingleStepControl* ssc = GetSingleStepControl();
+ tlsPtr_.single_step_control = nullptr;
+ delete ssc;
+}
} // namespace art
diff --git a/runtime/thread.h b/runtime/thread.h
index 5b3e746..26b7b6f 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -31,10 +31,10 @@
#include "base/mutex.h"
#include "entrypoints/interpreter/interpreter_entrypoints.h"
#include "entrypoints/jni/jni_entrypoints.h"
-#include "entrypoints/portable/portable_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "globals.h"
#include "handle_scope.h"
+#include "instrumentation.h"
#include "jvalue.h"
#include "object_callbacks.h"
#include "offsets.h"
@@ -75,7 +75,7 @@
class Runtime;
class ScopedObjectAccessAlreadyRunnable;
class ShadowFrame;
-struct SingleStepControl;
+class SingleStepControl;
class Thread;
class ThreadList;
@@ -216,6 +216,9 @@
bool RequestCheckpoint(Closure* function)
EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_);
+ void SetFlipFunction(Closure* function);
+ Closure* GetFlipFunction();
+
// Called when thread detected that the thread_suspend_count_ was non-zero. Gives up share of
// mutator_lock_ and waits until it is resumed and thread_suspend_count_ is zero.
void FullSuspendCheck()
@@ -549,12 +552,6 @@
}
template<size_t pointer_size>
- static ThreadOffset<pointer_size> PortableEntryPointOffset(size_t port_entrypoint_offset) {
- return ThreadOffsetFromTlsPtr<pointer_size>(
- OFFSETOF_MEMBER(tls_ptr_sized_values, portable_entrypoints) + port_entrypoint_offset);
- }
-
- template<size_t pointer_size>
static ThreadOffset<pointer_size> SelfOffset() {
return ThreadOffsetFromTlsPtr<pointer_size>(OFFSETOF_MEMBER(tls_ptr_sized_values, self));
}
@@ -712,6 +709,15 @@
return tlsPtr_.single_step_control;
}
+ // Activates single step control for debugging. The thread takes the
+ // ownership of the given SingleStepControl*. It is deleted by a call
+ // to DeactivateSingleStepControl or upon thread destruction.
+ void ActivateSingleStepControl(SingleStepControl* ssc);
+
+ // Deactivates single step control for debugging.
+ void DeactivateSingleStepControl();
+
+
// Returns the fake exception used to activate deoptimization.
static mirror::Throwable* GetDeoptimizationException() {
return reinterpret_cast<mirror::Throwable*>(-1);
@@ -787,6 +793,12 @@
mirror::Object* AllocTlab(size_t bytes);
void SetTlab(uint8_t* start, uint8_t* end);
bool HasTlab() const;
+ uint8_t* GetTlabStart() {
+ return tlsPtr_.thread_local_start;
+ }
+ uint8_t* GetTlabPos() {
+ return tlsPtr_.thread_local_pos;
+ }
// Remove the suspend trigger for this thread by making the suspend_trigger_ TLS value
// equal to a valid pointer.
@@ -804,10 +816,12 @@
// Push an object onto the allocation stack.
- bool PushOnThreadLocalAllocationStack(mirror::Object* obj);
+ bool PushOnThreadLocalAllocationStack(mirror::Object* obj)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Set the thread local allocation pointers to the given pointers.
- void SetThreadLocalAllocationStack(mirror::Object** start, mirror::Object** end);
+ void SetThreadLocalAllocationStack(StackReference<mirror::Object>* start,
+ StackReference<mirror::Object>* end);
// Resets the thread local allocation pointers.
void RevokeThreadLocalAllocationStack();
@@ -854,6 +868,10 @@
return tlsPtr_.nested_signal_state;
}
+ bool IsSuspendedAtSuspendCheck() const {
+ return tls32_.suspended_at_suspend_check;
+ }
+
private:
explicit Thread(bool daemon);
~Thread() LOCKS_EXCLUDED(Locks::mutator_lock_,
@@ -959,7 +977,7 @@
suspend_count(0), debug_suspend_count(0), thin_lock_thread_id(0), tid(0),
daemon(is_daemon), throwing_OutOfMemoryError(false), no_thread_suspension(0),
thread_exit_check_count(0), is_exception_reported_to_instrumentation_(false),
- handling_signal_(false), padding_(0) {
+ handling_signal_(false), suspended_at_suspend_check(false) {
}
union StateAndFlags state_and_flags;
@@ -1003,8 +1021,10 @@
// True if signal is being handled by this thread.
bool32_t handling_signal_;
- // Padding to make the size aligned to 8. Remove this if we add another 32 bit field.
- int32_t padding_;
+ // True if the thread is suspended in FullSuspendCheck(). This is
+ // used to distinguish runnable threads that are suspended due to
+ // a normal suspend check from other threads.
+ bool32_t suspended_at_suspend_check;
} tls32_;
struct PACKED(8) tls_64bit_sized_values {
@@ -1031,7 +1051,7 @@
pthread_self(0), last_no_thread_suspension_cause(nullptr), thread_local_start(nullptr),
thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_objects(0),
thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr),
- nested_signal_state(nullptr) {
+ nested_signal_state(nullptr), flip_function(nullptr) {
for (size_t i = 0; i < kLockLevelCount; ++i) {
held_mutexes[i] = nullptr;
}
@@ -1128,7 +1148,6 @@
// TODO: move this to more of a global offset table model to avoid per-thread duplication.
InterpreterEntryPoints interpreter_entrypoints;
JniEntryPoints jni_entrypoints;
- PortableEntryPoints portable_entrypoints;
QuickEntryPoints quick_entrypoints;
// Thread-local allocation pointer.
@@ -1141,14 +1160,17 @@
void* rosalloc_runs[kNumRosAllocThreadLocalSizeBrackets];
// Thread-local allocation stack data/routines.
- mirror::Object** thread_local_alloc_stack_top;
- mirror::Object** thread_local_alloc_stack_end;
+ StackReference<mirror::Object>* thread_local_alloc_stack_top;
+ StackReference<mirror::Object>* thread_local_alloc_stack_end;
// Support for Mutex lock hierarchy bug detection.
BaseMutex* held_mutexes[kLockLevelCount];
// Recorded thread state for nested signals.
jmp_buf* nested_signal_state;
+
+ // The function used for thread flip.
+ Closure* flip_function;
} tlsPtr_;
// Guards the 'interrupted_' and 'wait_monitor_' members.
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index beafcda..05a0bff 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -27,6 +27,7 @@
#include <sstream>
+#include "base/histogram-inl.h"
#include "base/mutex.h"
#include "base/mutex-inl.h"
#include "base/timing_logger.h"
@@ -43,10 +44,16 @@
namespace art {
static constexpr uint64_t kLongThreadSuspendThreshold = MsToNs(5);
+static constexpr uint64_t kThreadSuspendTimeoutMs = 30 * 1000; // 30s.
+// Use 0 since we want to yield to prevent blocking for an unpredictable amount of time.
+static constexpr useconds_t kThreadSuspendInitialSleepUs = 0;
+static constexpr useconds_t kThreadSuspendMaxYieldUs = 3000;
+static constexpr useconds_t kThreadSuspendMaxSleepUs = 5000;
ThreadList::ThreadList()
: suspend_all_count_(0), debug_suspend_all_count_(0),
- thread_exit_cond_("thread exit condition variable", *Locks::thread_list_lock_) {
+ thread_exit_cond_("thread exit condition variable", *Locks::thread_list_lock_),
+ suspend_all_historam_("suspend all histogram", 16, 64) {
CHECK(Monitor::IsValidLockWord(LockWord::FromThinLockId(kMaxThreadId, 1)));
}
@@ -97,6 +104,15 @@
}
void ThreadList::DumpForSigQuit(std::ostream& os) {
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ // Only print if we have samples.
+ if (suspend_all_historam_.SampleSize() > 0) {
+ Histogram<uint64_t>::CumulativeData data;
+ suspend_all_historam_.CreateHistogram(&data);
+ suspend_all_historam_.PrintConfidenceIntervals(os, 0.99, data); // Dump time to suspend.
+ }
+ }
Dump(os);
DumpUnattachedThreads(os);
}
@@ -139,6 +155,10 @@
closedir(d);
}
+// Dump checkpoint timeout in milliseconds. Larger amount on the host, as dumping will invoke
+// addr2line when available.
+static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 10000 : 20000;
+
// A closure used by Thread::Dump.
class DumpCheckpoint FINAL : public Closure {
public:
@@ -159,14 +179,15 @@
MutexLock mu(self, *Locks::logging_lock_);
*os_ << local_os.str();
}
- barrier_.Pass(self);
+ if (thread->GetState() == kRunnable) {
+ barrier_.Pass(self);
+ }
}
void WaitForThreadsToRunThroughCheckpoint(size_t threads_running_checkpoint) {
Thread* self = Thread::Current();
ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
- const uint32_t kWaitTimeoutMs = 10000;
- bool timed_out = barrier_.Increment(self, threads_running_checkpoint, kWaitTimeoutMs);
+ bool timed_out = barrier_.Increment(self, threads_running_checkpoint, kDumpWaitTimeout);
if (timed_out) {
// Avoid a recursive abort.
LOG((kIsDebugBuild && (gAborting == 0)) ? FATAL : ERROR)
@@ -188,7 +209,9 @@
}
DumpCheckpoint checkpoint(&os);
size_t threads_running_checkpoint = RunCheckpoint(&checkpoint);
- checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint);
+ if (threads_running_checkpoint != 0) {
+ checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint);
+ }
}
void ThreadList::AssertThreadsAreSuspended(Thread* self, Thread* ignore1, Thread* ignore2) {
@@ -219,22 +242,13 @@
#endif
// Unlike suspending all threads where we can wait to acquire the mutator_lock_, suspending an
-// individual thread requires polling. delay_us is the requested sleep and total_delay_us
-// accumulates the total time spent sleeping for timeouts. The first sleep is just a yield,
-// subsequently sleeps increase delay_us from 1ms to 500ms by doubling.
-static void ThreadSuspendSleep(useconds_t* delay_us, useconds_t* total_delay_us) {
- useconds_t new_delay_us = (*delay_us) * 2;
- CHECK_GE(new_delay_us, *delay_us);
- if (new_delay_us < 500000) { // Don't allow sleeping to be more than 0.5s.
- *delay_us = new_delay_us;
- }
- if (*delay_us == 0) {
+// individual thread requires polling. delay_us is the requested sleep wait. If delay_us is 0 then
+// we use sched_yield instead of calling usleep.
+static void ThreadSuspendSleep(useconds_t delay_us) {
+ if (delay_us == 0) {
sched_yield();
- // Default to 1 milliseconds (note that this gets multiplied by 2 before the first sleep).
- *delay_us = 500;
} else {
- usleep(*delay_us);
- *total_delay_us += *delay_us;
+ usleep(delay_us);
}
}
@@ -283,16 +297,23 @@
// Run the checkpoint on the suspended threads.
for (const auto& thread : suspended_count_modified_threads) {
if (!thread->IsSuspended()) {
- // Wait until the thread is suspended.
- useconds_t total_delay_us = 0;
+ if (ATRACE_ENABLED()) {
+ std::ostringstream oss;
+ thread->ShortDump(oss);
+ ATRACE_BEGIN((std::string("Waiting for suspension of thread ") + oss.str()).c_str());
+ }
+ // Busy wait until the thread is suspended.
+ const uint64_t start_time = NanoTime();
do {
- useconds_t delay_us = 100;
- ThreadSuspendSleep(&delay_us, &total_delay_us);
+ ThreadSuspendSleep(kThreadSuspendInitialSleepUs);
} while (!thread->IsSuspended());
+ const uint64_t total_delay = NanoTime() - start_time;
// Shouldn't need to wait for longer than 1000 microseconds.
- constexpr useconds_t kLongWaitThresholdUS = 1000;
- if (UNLIKELY(total_delay_us > kLongWaitThresholdUS)) {
- LOG(WARNING) << "Waited " << total_delay_us << " us for thread suspend!";
+ constexpr uint64_t kLongWaitThreshold = MsToNs(1);
+ ATRACE_END();
+ if (UNLIKELY(total_delay > kLongWaitThreshold)) {
+ LOG(WARNING) << "Long wait of " << PrettyDuration(total_delay) << " for "
+ << *thread << " suspension!";
}
}
// We know for sure that the thread is suspended at this point.
@@ -310,8 +331,7 @@
Thread::resume_cond_->Broadcast(self);
}
- // Add one for self.
- return count + suspended_count_modified_threads.size() + 1;
+ return count;
}
// Request that a checkpoint function be run on all active (non-suspended)
@@ -342,6 +362,95 @@
return count;
}
+// A checkpoint/suspend-all hybrid to switch thread roots from
+// from-space to to-space refs. Used to synchronize threads at a point
+// to mark the initiation of marking while maintaining the to-space
+// invariant.
+size_t ThreadList::FlipThreadRoots(Closure* thread_flip_visitor, Closure* flip_callback,
+ gc::collector::GarbageCollector* collector) {
+ TimingLogger::ScopedTiming split("ThreadListFlip", collector->GetTimings());
+ const uint64_t start_time = NanoTime();
+ Thread* self = Thread::Current();
+ Locks::mutator_lock_->AssertNotHeld(self);
+ Locks::thread_list_lock_->AssertNotHeld(self);
+ Locks::thread_suspend_count_lock_->AssertNotHeld(self);
+ CHECK_NE(self->GetState(), kRunnable);
+
+ std::vector<Thread*> runnable_threads;
+ std::vector<Thread*> other_threads;
+
+ // Suspend all threads once.
+ {
+ MutexLock mu(self, *Locks::thread_list_lock_);
+ MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
+ // Update global suspend all state for attaching threads.
+ ++suspend_all_count_;
+ // Increment everybody's suspend count (except our own).
+ for (const auto& thread : list_) {
+ if (thread == self) {
+ continue;
+ }
+ thread->ModifySuspendCount(self, +1, false);
+ }
+ }
+
+ // Run the flip callback for the collector.
+ Locks::mutator_lock_->ExclusiveLock(self);
+ flip_callback->Run(self);
+ Locks::mutator_lock_->ExclusiveUnlock(self);
+ collector->RegisterPause(NanoTime() - start_time);
+
+ // Resume runnable threads.
+ {
+ MutexLock mu(self, *Locks::thread_list_lock_);
+ MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
+ --suspend_all_count_;
+ for (const auto& thread : list_) {
+ if (thread == self) {
+ continue;
+ }
+ // Set the flip function for both runnable and suspended threads
+ // because Thread::DumpState/DumpJavaStack() (invoked by a
+ // checkpoint) may cause the flip function to be run for a
+ // runnable/suspended thread before a runnable threads runs it
+ // for itself or we run it for a suspended thread below.
+ thread->SetFlipFunction(thread_flip_visitor);
+ if (thread->IsSuspendedAtSuspendCheck()) {
+ // The thread will resume right after the broadcast.
+ thread->ModifySuspendCount(self, -1, false);
+ runnable_threads.push_back(thread);
+ } else {
+ other_threads.push_back(thread);
+ }
+ }
+ Thread::resume_cond_->Broadcast(self);
+ }
+
+ // Run the closure on the other threads and let them resume.
+ {
+ ReaderMutexLock mu(self, *Locks::mutator_lock_);
+ for (const auto& thread : other_threads) {
+ Closure* flip_func = thread->GetFlipFunction();
+ if (flip_func != nullptr) {
+ flip_func->Run(thread);
+ }
+ }
+ // Run it for self.
+ thread_flip_visitor->Run(self);
+ }
+
+ // Resume other threads.
+ {
+ MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
+ for (const auto& thread : other_threads) {
+ thread->ModifySuspendCount(self, -1, false);
+ }
+ Thread::resume_cond_->Broadcast(self);
+ }
+
+ return runnable_threads.size() + other_threads.size() + 1; // +1 for self.
+}
+
void ThreadList::SuspendAll() {
Thread* self = Thread::Current();
@@ -351,7 +460,7 @@
VLOG(threads) << "Thread[null] SuspendAll starting...";
}
ATRACE_BEGIN("Suspending mutator threads");
- uint64_t start_time = NanoTime();
+ const uint64_t start_time = NanoTime();
Locks::mutator_lock_->AssertNotHeld(self);
Locks::thread_list_lock_->AssertNotHeld(self);
@@ -377,16 +486,18 @@
// Block on the mutator lock until all Runnable threads release their share of access.
#if HAVE_TIMED_RWLOCK
// Timeout if we wait more than 30 seconds.
- if (!Locks::mutator_lock_->ExclusiveLockWithTimeout(self, 30 * 1000, 0)) {
+ if (!Locks::mutator_lock_->ExclusiveLockWithTimeout(self, kThreadSuspendTimeoutMs, 0)) {
UnsafeLogFatalForThreadSuspendAllTimeout();
}
#else
Locks::mutator_lock_->ExclusiveLock(self);
#endif
- uint64_t end_time = NanoTime();
- if (end_time - start_time > kLongThreadSuspendThreshold) {
- LOG(WARNING) << "Suspending all threads took: " << PrettyDuration(end_time - start_time);
+ const uint64_t end_time = NanoTime();
+ const uint64_t suspend_time = end_time - start_time;
+ suspend_all_historam_.AdjustAndAddValue(suspend_time);
+ if (suspend_time > kLongThreadSuspendThreshold) {
+ LOG(WARNING) << "Suspending all threads took: " << PrettyDuration(suspend_time);
}
if (kDebugLocking) {
@@ -454,6 +565,9 @@
}
void ThreadList::Resume(Thread* thread, bool for_debugger) {
+ // This assumes there was an ATRACE_BEGIN when we suspended the thread.
+ ATRACE_END();
+
Thread* self = Thread::Current();
DCHECK_NE(thread, self);
VLOG(threads) << "Resume(" << reinterpret_cast<void*>(thread) << ") starting..."
@@ -501,12 +615,11 @@
Thread* ThreadList::SuspendThreadByPeer(jobject peer, bool request_suspension,
bool debug_suspension, bool* timed_out) {
- static const useconds_t kTimeoutUs = 30 * 1000000; // 30s.
- useconds_t total_delay_us = 0;
- useconds_t delay_us = 0;
- bool did_suspend_request = false;
+ const uint64_t start_time = NanoTime();
+ useconds_t sleep_us = kThreadSuspendInitialSleepUs;
*timed_out = false;
- Thread* self = Thread::Current();
+ Thread* const self = Thread::Current();
+ Thread* suspended_thread = nullptr;
VLOG(threads) << "SuspendThreadByPeer starting";
while (true) {
Thread* thread;
@@ -520,10 +633,18 @@
MutexLock thread_list_mu(self, *Locks::thread_list_lock_);
thread = Thread::FromManagedThread(soa, peer);
if (thread == nullptr) {
+ if (suspended_thread != nullptr) {
+ MutexLock suspend_count_mu(self, *Locks::thread_suspend_count_lock_);
+ // If we incremented the suspend count but the thread reset its peer, we need to
+ // re-decrement it since it is shutting down and may deadlock the runtime in
+ // ThreadList::WaitForOtherNonDaemonThreadsToExit.
+ suspended_thread->ModifySuspendCount(soa.Self(), -1, debug_suspension);
+ }
ThreadSuspendByPeerWarning(self, WARNING, "No such thread for suspend", peer);
return nullptr;
}
if (!Contains(thread)) {
+ CHECK(suspended_thread == nullptr);
VLOG(threads) << "SuspendThreadByPeer failed for unattached thread: "
<< reinterpret_cast<void*>(thread);
return nullptr;
@@ -538,9 +659,10 @@
// which will allow this thread to be suspended.
continue;
}
- thread->ModifySuspendCount(self, +1, debug_suspension);
+ CHECK(suspended_thread == nullptr);
+ suspended_thread = thread;
+ suspended_thread->ModifySuspendCount(self, +1, debug_suspension);
request_suspension = false;
- did_suspend_request = true;
} else {
// If the caller isn't requesting suspension, a suspension should have already occurred.
CHECK_GT(thread->GetSuspendCount(), 0);
@@ -555,21 +677,37 @@
// done.
if (thread->IsSuspended()) {
VLOG(threads) << "SuspendThreadByPeer thread suspended: " << *thread;
+ if (ATRACE_ENABLED()) {
+ std::string name;
+ thread->GetThreadName(name);
+ ATRACE_BEGIN(StringPrintf("SuspendThreadByPeer suspended %s for peer=%p", name.c_str(),
+ peer).c_str());
+ }
return thread;
}
- if (total_delay_us >= kTimeoutUs) {
+ const uint64_t total_delay = NanoTime() - start_time;
+ if (total_delay >= MsToNs(kThreadSuspendTimeoutMs)) {
ThreadSuspendByPeerWarning(self, FATAL, "Thread suspension timed out", peer);
- if (did_suspend_request) {
- thread->ModifySuspendCount(soa.Self(), -1, debug_suspension);
+ if (suspended_thread != nullptr) {
+ CHECK_EQ(suspended_thread, thread);
+ suspended_thread->ModifySuspendCount(soa.Self(), -1, debug_suspension);
}
*timed_out = true;
return nullptr;
+ } else if (sleep_us == 0 &&
+ total_delay > static_cast<uint64_t>(kThreadSuspendMaxYieldUs) * 1000) {
+ // We have spun for kThreadSuspendMaxYieldUs time, switch to sleeps to prevent
+ // excessive CPU usage.
+ sleep_us = kThreadSuspendMaxYieldUs / 2;
}
}
// Release locks and come out of runnable state.
}
- VLOG(threads) << "SuspendThreadByPeer sleeping to allow thread chance to suspend";
- ThreadSuspendSleep(&delay_us, &total_delay_us);
+ VLOG(threads) << "SuspendThreadByPeer waiting to allow thread chance to suspend";
+ ThreadSuspendSleep(sleep_us);
+ // This may stay at 0 if sleep_us == 0, but this is WAI since we want to avoid using usleep at
+ // all if possible. This shouldn't be an issue since time to suspend should always be small.
+ sleep_us = std::min(sleep_us * 2, kThreadSuspendMaxSleepUs);
}
}
@@ -580,12 +718,11 @@
Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspension,
bool* timed_out) {
- static const useconds_t kTimeoutUs = 30 * 1000000; // 30s.
- useconds_t total_delay_us = 0;
- useconds_t delay_us = 0;
+ const uint64_t start_time = NanoTime();
+ useconds_t sleep_us = kThreadSuspendInitialSleepUs;
*timed_out = false;
Thread* suspended_thread = nullptr;
- Thread* self = Thread::Current();
+ Thread* const self = Thread::Current();
CHECK_NE(thread_id, kInvalidThreadId);
VLOG(threads) << "SuspendThreadByThreadId starting";
while (true) {
@@ -638,22 +775,35 @@
// count, or else we've waited and it has self suspended) or is the current thread, we're
// done.
if (thread->IsSuspended()) {
+ if (ATRACE_ENABLED()) {
+ std::string name;
+ thread->GetThreadName(name);
+ ATRACE_BEGIN(StringPrintf("SuspendThreadByThreadId suspended %s id=%d",
+ name.c_str(), thread_id).c_str());
+ }
VLOG(threads) << "SuspendThreadByThreadId thread suspended: " << *thread;
return thread;
}
- if (total_delay_us >= kTimeoutUs) {
+ const uint64_t total_delay = NanoTime() - start_time;
+ if (total_delay >= MsToNs(kThreadSuspendTimeoutMs)) {
ThreadSuspendByThreadIdWarning(WARNING, "Thread suspension timed out", thread_id);
if (suspended_thread != nullptr) {
thread->ModifySuspendCount(soa.Self(), -1, debug_suspension);
}
*timed_out = true;
return nullptr;
+ } else if (sleep_us == 0 &&
+ total_delay > static_cast<uint64_t>(kThreadSuspendMaxYieldUs) * 1000) {
+ // We have spun for kThreadSuspendMaxYieldUs time, switch to sleeps to prevent
+ // excessive CPU usage.
+ sleep_us = kThreadSuspendMaxYieldUs / 2;
}
}
// Release locks and come out of runnable state.
}
- VLOG(threads) << "SuspendThreadByThreadId sleeping to allow thread chance to suspend";
- ThreadSuspendSleep(&delay_us, &total_delay_us);
+ VLOG(threads) << "SuspendThreadByThreadId waiting to allow thread chance to suspend";
+ ThreadSuspendSleep(sleep_us);
+ sleep_us = std::min(sleep_us * 2, kThreadSuspendMaxSleepUs);
}
}
@@ -775,7 +925,6 @@
void ThreadList::ResumeAllForDebugger() {
Thread* self = Thread::Current();
Thread* debug_thread = Dbg::GetDebugThread();
- bool needs_resume = false;
VLOG(threads) << *self << " ResumeAllForDebugger starting...";
@@ -788,32 +937,34 @@
MutexLock suspend_count_mu(self, *Locks::thread_suspend_count_lock_);
// Update global suspend all state for attaching threads.
DCHECK_GE(suspend_all_count_, debug_suspend_all_count_);
- needs_resume = (debug_suspend_all_count_ > 0);
- if (needs_resume) {
+ if (debug_suspend_all_count_ > 0) {
--suspend_all_count_;
--debug_suspend_all_count_;
- // Decrement everybody's suspend count (except our own).
- for (const auto& thread : list_) {
- if (thread == self || thread == debug_thread) {
- continue;
- }
- if (thread->GetDebugSuspendCount() == 0) {
- // This thread may have been individually resumed with ThreadReference.Resume.
- continue;
- }
- VLOG(threads) << "requesting thread resume: " << *thread;
- thread->ModifySuspendCount(self, -1, true);
- }
} else {
// We've been asked to resume all threads without being asked to
- // suspend them all before. Let's print a warning.
+ // suspend them all before. That may happen if a debugger tries
+ // to resume some suspended threads (with suspend count == 1)
+ // at once with a VirtualMachine.Resume command. Let's print a
+ // warning.
LOG(WARNING) << "Debugger attempted to resume all threads without "
<< "having suspended them all before.";
}
+ // Decrement everybody's suspend count (except our own).
+ for (const auto& thread : list_) {
+ if (thread == self || thread == debug_thread) {
+ continue;
+ }
+ if (thread->GetDebugSuspendCount() == 0) {
+ // This thread may have been individually resumed with ThreadReference.Resume.
+ continue;
+ }
+ VLOG(threads) << "requesting thread resume: " << *thread;
+ thread->ModifySuspendCount(self, -1, true);
+ }
}
}
- if (needs_resume) {
+ {
MutexLock mu(self, *Locks::thread_suspend_count_lock_);
Thread::resume_cond_->Broadcast(self);
}
@@ -1003,28 +1154,6 @@
}
}
-class VerifyRootWrapperArg {
- public:
- VerifyRootWrapperArg(VerifyRootCallback* callback, void* arg) : callback_(callback), arg_(arg) {
- }
- VerifyRootCallback* const callback_;
- void* const arg_;
-};
-
-static void VerifyRootWrapperCallback(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
- RootType root_type) {
- VerifyRootWrapperArg* wrapperArg = reinterpret_cast<VerifyRootWrapperArg*>(arg);
- wrapperArg->callback_(*root, wrapperArg->arg_, 0, NULL, root_type);
-}
-
-void ThreadList::VerifyRoots(VerifyRootCallback* callback, void* arg) const {
- VerifyRootWrapperArg wrapper(callback, arg);
- MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
- for (const auto& thread : list_) {
- thread->VisitRoots(VerifyRootWrapperCallback, &wrapper);
- }
-}
-
uint32_t ThreadList::AllocThreadId(Thread* self) {
MutexLock mu(self, *Locks::allocated_thread_ids_lock_);
for (size_t i = 0; i < allocated_ids_.size(); ++i) {
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 13684c7..d18315a 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -17,7 +17,9 @@
#ifndef ART_RUNTIME_THREAD_LIST_H_
#define ART_RUNTIME_THREAD_LIST_H_
+#include "base/histogram.h"
#include "base/mutex.h"
+#include "gc_root.h"
#include "jni.h"
#include "object_callbacks.h"
@@ -25,6 +27,11 @@
#include <list>
namespace art {
+namespace gc {
+ namespace collector {
+ class GarbageCollector;
+ } // namespac collector
+} // namespace gc
class Closure;
class Thread;
class TimingLogger;
@@ -39,11 +46,10 @@
~ThreadList();
void DumpForSigQuit(std::ostream& os)
- LOCKS_EXCLUDED(Locks::thread_list_lock_);
+ LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::mutator_lock_);
// For thread suspend timeout dumps.
void Dump(std::ostream& os)
- LOCKS_EXCLUDED(Locks::thread_list_lock_,
- Locks::thread_suspend_count_lock_);
+ LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::thread_suspend_count_lock_);
pid_t GetLockOwner(); // For SignalCatcher.
// Thread suspension support.
@@ -94,6 +100,14 @@
LOCKS_EXCLUDED(Locks::thread_list_lock_,
Locks::thread_suspend_count_lock_);
+ // Flip thread roots from from-space refs to to-space refs. Used by
+ // the concurrent copying collector.
+ size_t FlipThreadRoots(Closure* thread_flip_visitor, Closure* flip_callback,
+ gc::collector::GarbageCollector* collector)
+ LOCKS_EXCLUDED(Locks::mutator_lock_,
+ Locks::thread_list_lock_,
+ Locks::thread_suspend_count_lock_);
+
// Suspends all threads
void SuspendAllForDebugger()
LOCKS_EXCLUDED(Locks::mutator_lock_,
@@ -125,9 +139,6 @@
void VisitRoots(RootCallback* callback, void* arg) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VerifyRoots(VerifyRootCallback* callback, void* arg) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
// Return a copy of the thread list.
std::list<Thread*> GetList() EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_) {
return list_;
@@ -169,6 +180,10 @@
// Signaled when threads terminate. Used to determine when all non-daemons have terminated.
ConditionVariable thread_exit_cond_ GUARDED_BY(Locks::thread_list_lock_);
+ // Thread suspend time histogram. Only modified when all the threads are suspended, so guarding
+ // by mutator lock ensures no thread can read when another thread is modifying it.
+ Histogram<uint64_t> suspend_all_historam_ GUARDED_BY(Locks::mutator_lock_);
+
friend class Thread;
DISALLOW_COPY_AND_ASSIGN(ThreadList);
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index d6330c8..79b57af 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -22,19 +22,32 @@
#include "barrier.h"
#include "base/mutex.h"
-#include "closure.h"
#include "mem_map.h"
namespace art {
class ThreadPool;
+class Closure {
+ public:
+ virtual ~Closure() { }
+ virtual void Run(Thread* self) = 0;
+};
+
class Task : public Closure {
public:
- // Called when references reaches 0.
+ // Called after Closure::Run has been called.
virtual void Finalize() { }
};
+class SelfDeletingTask : public Task {
+ public:
+ virtual ~SelfDeletingTask() { }
+ virtual void Finalize() {
+ delete this;
+ }
+};
+
class ThreadPoolWorker {
public:
static const size_t kDefaultStackSize = 1 * MB;
diff --git a/runtime/thread_state.h b/runtime/thread_state.h
index 6e5deeb..b5479ed 100644
--- a/runtime/thread_state.h
+++ b/runtime/thread_state.h
@@ -41,6 +41,7 @@
kWaitingInMainSignalCatcherLoop, // WAITING TS_WAIT blocking/reading/processing signals
kWaitingForDeoptimization, // WAITING TS_WAIT waiting for deoptimization suspend all
kWaitingForMethodTracingStart, // WAITING TS_WAIT waiting for method tracing to start
+ kWaitingForVisitObjects, // WAITING TS_WAIT waiting for visiting objects
kStarting, // NEW TS_WAIT native thread started, not yet ready to run managed code
kNative, // RUNNABLE TS_RUNNING running in a JNI native method
kSuspended, // RUNNABLE TS_RUNNING suspended by GC or debugger
diff --git a/runtime/throw_location.cc b/runtime/throw_location.cc
index 04abe64..4d2aec0 100644
--- a/runtime/throw_location.cc
+++ b/runtime/throw_location.cc
@@ -34,11 +34,11 @@
void ThrowLocation::VisitRoots(RootCallback* visitor, void* arg) {
if (this_object_ != nullptr) {
- visitor(&this_object_, arg, 0, kRootVMInternal);
+ visitor(&this_object_, arg, RootInfo(kRootVMInternal));
DCHECK(this_object_ != nullptr);
}
if (method_ != nullptr) {
- visitor(reinterpret_cast<mirror::Object**>(&method_), arg, 0, kRootVMInternal);
+ visitor(reinterpret_cast<mirror::Object**>(&method_), arg, RootInfo(kRootVMInternal));
DCHECK(method_ != nullptr);
}
}
diff --git a/runtime/throw_location.h b/runtime/throw_location.h
index b36eb67..bec0da4 100644
--- a/runtime/throw_location.h
+++ b/runtime/throw_location.h
@@ -20,6 +20,7 @@
#include "object_callbacks.h"
#include "base/macros.h"
#include "base/mutex.h"
+#include "gc_root.h"
#include <stdint.h>
#include <string>
diff --git a/runtime/trace.cc b/runtime/trace.cc
index b510844..0950abeb 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -38,9 +38,7 @@
#include "ScopedLocalRef.h"
#include "thread.h"
#include "thread_list.h"
-#if !defined(ART_USE_PORTABLE_COMPILER)
#include "entrypoints/quick/quick_entrypoints.h"
-#endif
namespace art {
@@ -152,33 +150,33 @@
}
void Trace::SetDefaultClockSource(TraceClockSource clock_source) {
-#if defined(HAVE_POSIX_CLOCKS)
+#if defined(__linux__)
default_clock_source_ = clock_source;
#else
- if (clock_source != kTraceClockSourceWall) {
+ if (clock_source != TraceClockSource::kWall) {
LOG(WARNING) << "Ignoring tracing request to use CPU time.";
}
#endif
}
static uint16_t GetTraceVersion(TraceClockSource clock_source) {
- return (clock_source == kTraceClockSourceDual) ? kTraceVersionDualClock
+ return (clock_source == TraceClockSource::kDual) ? kTraceVersionDualClock
: kTraceVersionSingleClock;
}
static uint16_t GetRecordSize(TraceClockSource clock_source) {
- return (clock_source == kTraceClockSourceDual) ? kTraceRecordSizeDualClock
+ return (clock_source == TraceClockSource::kDual) ? kTraceRecordSizeDualClock
: kTraceRecordSizeSingleClock;
}
bool Trace::UseThreadCpuClock() {
- return (clock_source_ == kTraceClockSourceThreadCpu) ||
- (clock_source_ == kTraceClockSourceDual);
+ return (clock_source_ == TraceClockSource::kThreadCpu) ||
+ (clock_source_ == TraceClockSource::kDual);
}
bool Trace::UseWallClock() {
- return (clock_source_ == kTraceClockSourceWall) ||
- (clock_source_ == kTraceClockSourceDual);
+ return (clock_source_ == TraceClockSource::kWall) ||
+ (clock_source_ == TraceClockSource::kDual);
}
void Trace::MeasureClockOverhead() {
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 478066f..7e2e0a6 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -30,7 +30,8 @@
// TODO: remove (only used for debugging purpose).
static constexpr bool kEnableTransactionStats = false;
-Transaction::Transaction() : log_lock_("transaction log lock", kTransactionLogLock) {
+Transaction::Transaction()
+ : log_lock_("transaction log lock", kTransactionLogLock), aborted_(false) {
CHECK(Runtime::Current()->IsCompiler());
}
@@ -57,6 +58,35 @@
}
}
+void Transaction::Abort(const std::string& abort_message) {
+ MutexLock mu(Thread::Current(), log_lock_);
+ // We may abort more than once if the java.lang.InternalError thrown at the
+ // time of the abort has been caught during execution of a class initializer.
+ // We just keep the message of the first abort because it will cause the
+ // transaction to be rolled back anyway.
+ if (!aborted_) {
+ aborted_ = true;
+ abort_message_ = abort_message;
+ }
+}
+
+void Transaction::ThrowInternalError(Thread* self) {
+ DCHECK(IsAborted());
+ std::string abort_msg(GetAbortMessage());
+ self->ThrowNewException(self->GetCurrentLocationForThrow(), "Ljava/lang/InternalError;",
+ abort_msg.c_str());
+}
+
+bool Transaction::IsAborted() {
+ MutexLock mu(Thread::Current(), log_lock_);
+ return aborted_;
+}
+
+const std::string& Transaction::GetAbortMessage() {
+ MutexLock mu(Thread::Current(), log_lock_);
+ return abort_message_;
+}
+
void Transaction::RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset,
uint8_t value, bool is_volatile) {
DCHECK(obj != nullptr);
@@ -150,7 +180,7 @@
intern_string_logs_.push_front(log);
}
-void Transaction::Abort() {
+void Transaction::Rollback() {
CHECK(!Runtime::Current()->IsActiveTransaction());
Thread* self = Thread::Current();
self->AssertNoPendingException();
@@ -206,7 +236,7 @@
it.second.VisitRoots(callback, arg);
mirror::Object* old_root = it.first;
mirror::Object* new_root = old_root;
- callback(&new_root, arg, 0, kRootUnknown);
+ callback(&new_root, arg, RootInfo(kRootUnknown));
if (new_root != old_root) {
moving_roots.push_back(std::make_pair(old_root, new_root));
}
@@ -233,7 +263,7 @@
mirror::Array* old_root = it.first;
CHECK(!old_root->IsObjectArray());
mirror::Array* new_root = old_root;
- callback(reinterpret_cast<mirror::Object**>(&new_root), arg, 0, kRootUnknown);
+ callback(reinterpret_cast<mirror::Object**>(&new_root), arg, RootInfo(kRootUnknown));
if (new_root != old_root) {
moving_roots.push_back(std::make_pair(old_root, new_root));
}
@@ -396,7 +426,7 @@
mirror::Object* obj =
reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(field_value.value));
if (obj != nullptr) {
- callback(&obj, arg, 0, kRootUnknown);
+ callback(&obj, arg, RootInfo(kRootUnknown));
field_value.value = reinterpret_cast<uintptr_t>(obj);
}
}
@@ -441,7 +471,7 @@
}
void Transaction::InternStringLog::VisitRoots(RootCallback* callback, void* arg) {
- callback(reinterpret_cast<mirror::Object**>(&str_), arg, 0, kRootInternedString);
+ callback(reinterpret_cast<mirror::Object**>(&str_), arg, RootInfo(kRootInternedString));
}
void Transaction::ArrayLog::LogValue(size_t index, uint64_t value) {
diff --git a/runtime/transaction.h b/runtime/transaction.h
index 566f231..be614f9 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -20,6 +20,7 @@
#include "base/macros.h"
#include "base/mutex.h"
#include "base/value_object.h"
+#include "gc_root.h"
#include "object_callbacks.h"
#include "offsets.h"
#include "primitive.h"
@@ -41,6 +42,14 @@
Transaction();
~Transaction();
+ void Abort(const std::string& abort_message)
+ LOCKS_EXCLUDED(log_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void ThrowInternalError(Thread* self)
+ LOCKS_EXCLUDED(log_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsAborted() LOCKS_EXCLUDED(log_lock_);
+
// Record object field changes.
void RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset, uint8_t value,
bool is_volatile)
@@ -84,7 +93,7 @@
LOCKS_EXCLUDED(log_lock_);
// Abort transaction by undoing all recorded changes.
- void Abort()
+ void Rollback()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
LOCKS_EXCLUDED(log_lock_);
@@ -205,10 +214,14 @@
EXCLUSIVE_LOCKS_REQUIRED(log_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const std::string& GetAbortMessage() LOCKS_EXCLUDED(log_lock_);
+
Mutex log_lock_ ACQUIRED_AFTER(Locks::intern_table_lock_);
std::map<mirror::Object*, ObjectLog> object_logs_ GUARDED_BY(log_lock_);
std::map<mirror::Array*, ArrayLog> array_logs_ GUARDED_BY(log_lock_);
std::list<InternStringLog> intern_string_logs_ GUARDED_BY(log_lock_);
+ bool aborted_ GUARDED_BY(log_lock_);
+ std::string abort_message_ GUARDED_BY(log_lock_);
DISALLOW_COPY_AND_ASSIGN(Transaction);
};
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index 8c4b90d..b80fe22 100644
--- a/runtime/transaction_test.cc
+++ b/runtime/transaction_test.cc
@@ -24,8 +24,68 @@
namespace art {
-class TransactionTest : public CommonRuntimeTest {};
+class TransactionTest : public CommonRuntimeTest {
+ public:
+ // Tests failing class initialization due to native call with transaction rollback.
+ void testTransactionAbort(const char* tested_class_signature) {
+ ScopedObjectAccess soa(Thread::Current());
+ jobject jclass_loader = LoadDex("Transaction");
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+ ASSERT_TRUE(class_loader.Get() != nullptr);
+ // Load and initialize java.lang.ExceptionInInitializerError and java.lang.InternalError
+ // classes so they can be thrown during class initialization if the transaction aborts.
+ MutableHandle<mirror::Class> h_klass(
+ hs.NewHandle(class_linker_->FindSystemClass(soa.Self(),
+ "Ljava/lang/ExceptionInInitializerError;")));
+ ASSERT_TRUE(h_klass.Get() != nullptr);
+ class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+ ASSERT_TRUE(h_klass->IsInitialized());
+
+ h_klass.Assign(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/InternalError;"));
+ ASSERT_TRUE(h_klass.Get() != nullptr);
+ class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+ ASSERT_TRUE(h_klass->IsInitialized());
+
+ // Load and verify utility class.
+ h_klass.Assign(class_linker_->FindClass(soa.Self(), "LTransaction$AbortHelperClass;",
+ class_loader));
+ ASSERT_TRUE(h_klass.Get() != nullptr);
+ class_linker_->VerifyClass(soa.Self(), h_klass);
+ ASSERT_TRUE(h_klass->IsVerified());
+
+ // Load and verify tested class.
+ h_klass.Assign(class_linker_->FindClass(soa.Self(), tested_class_signature, class_loader));
+ ASSERT_TRUE(h_klass.Get() != nullptr);
+ class_linker_->VerifyClass(soa.Self(), h_klass);
+ ASSERT_TRUE(h_klass->IsVerified());
+
+ mirror::Class::Status old_status = h_klass->GetStatus();
+ uint32_t old_lock_word = h_klass->GetLockWord(false).GetValue();
+
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+ Runtime::Current()->ExitTransactionMode();
+ ASSERT_FALSE(success);
+ ASSERT_TRUE(h_klass->IsErroneous());
+ ASSERT_TRUE(soa.Self()->IsExceptionPending());
+ ASSERT_TRUE(transaction.IsAborted());
+
+ // Check class's monitor get back to its original state without rolling back changes.
+ uint32_t new_lock_word = h_klass->GetLockWord(false).GetValue();
+ EXPECT_EQ(old_lock_word, new_lock_word);
+
+ // Check class status is rolled back properly.
+ soa.Self()->ClearException();
+ transaction.Rollback();
+ ASSERT_EQ(old_status, h_klass->GetStatus());
+ }
+};
+
+// Tests object's class is preserved after transaction rollback.
TEST_F(TransactionTest, Object_class) {
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<2> hs(soa.Self());
@@ -40,11 +100,12 @@
ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
Runtime::Current()->ExitTransactionMode();
- // Aborting transaction must not clear the Object::class field.
- transaction.Abort();
+ // Rolling back transaction's changes must not clear the Object::class field.
+ transaction.Rollback();
EXPECT_EQ(h_obj->GetClass(), h_klass.Get());
}
+// Tests object's monitor state is preserved after transaction rollback.
TEST_F(TransactionTest, Object_monitor) {
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<2> hs(soa.Self());
@@ -66,13 +127,14 @@
uint32_t new_lock_word = h_obj->GetLockWord(false).GetValue();
Runtime::Current()->ExitTransactionMode();
- // Aborting transaction must not clear the Object::class field.
- transaction.Abort();
+ // Rolling back transaction's changes must not change monitor's state.
+ transaction.Rollback();
uint32_t aborted_lock_word = h_obj->GetLockWord(false).GetValue();
EXPECT_NE(old_lock_word, new_lock_word);
EXPECT_EQ(aborted_lock_word, new_lock_word);
}
+// Tests array's length is preserved after transaction rollback.
TEST_F(TransactionTest, Array_length) {
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<2> hs(soa.Self());
@@ -95,11 +157,12 @@
ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
Runtime::Current()->ExitTransactionMode();
- // Aborting transaction must not clear the Object::class field.
- transaction.Abort();
+ // Rolling back transaction's changes must not reset array's length.
+ transaction.Rollback();
EXPECT_EQ(h_obj->GetLength(), kArraySize);
}
+// Tests static fields are reset to their default value after transaction rollback.
TEST_F(TransactionTest, StaticFieldsTest) {
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<4> hs(soa.Self());
@@ -110,8 +173,10 @@
Handle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStaticFieldsTest;", class_loader)));
ASSERT_TRUE(h_klass.Get() != nullptr);
- class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+ bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+ ASSERT_TRUE(success);
ASSERT_TRUE(h_klass->IsInitialized());
+ ASSERT_FALSE(soa.Self()->IsExceptionPending());
// Lookup fields.
mirror::ArtField* booleanField = h_klass->FindDeclaredStaticField("booleanField", "Z");
@@ -155,7 +220,7 @@
ASSERT_DOUBLE_EQ(doubleField->GetDouble(h_klass.Get()), static_cast<double>(0.0));
mirror::ArtField* objectField = h_klass->FindDeclaredStaticField("objectField",
- "Ljava/lang/Object;");
+ "Ljava/lang/Object;");
ASSERT_TRUE(objectField != nullptr);
ASSERT_EQ(objectField->GetTypeAsPrimitiveType(), Primitive::kPrimNot);
ASSERT_EQ(objectField->GetObject(h_klass.Get()), nullptr);
@@ -168,7 +233,7 @@
ASSERT_TRUE(h_obj.Get() != nullptr);
ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
- // Modify fields inside transaction and abort it.
+ // Modify fields inside transaction then rollback changes.
Transaction transaction;
Runtime::Current()->EnterTransactionMode(&transaction);
booleanField->SetBoolean<true>(h_klass.Get(), true);
@@ -181,7 +246,7 @@
doubleField->SetDouble<true>(h_klass.Get(), 1.0);
objectField->SetObject<true>(h_klass.Get(), h_obj.Get());
Runtime::Current()->ExitTransactionMode();
- transaction.Abort();
+ transaction.Rollback();
// Check values have properly been restored to their original (default) value.
EXPECT_EQ(booleanField->GetBoolean(h_klass.Get()), false);
@@ -195,6 +260,7 @@
EXPECT_EQ(objectField->GetObject(h_klass.Get()), nullptr);
}
+// Tests instance fields are reset to their default value after transaction rollback.
TEST_F(TransactionTest, InstanceFieldsTest) {
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<5> hs(soa.Self());
@@ -205,8 +271,10 @@
Handle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LInstanceFieldsTest;", class_loader)));
ASSERT_TRUE(h_klass.Get() != nullptr);
- class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+ bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+ ASSERT_TRUE(success);
ASSERT_TRUE(h_klass->IsInitialized());
+ ASSERT_FALSE(soa.Self()->IsExceptionPending());
// Allocate an InstanceFieldTest object.
Handle<mirror::Object> h_instance(hs.NewHandle(h_klass->AllocObject(soa.Self())));
@@ -267,7 +335,7 @@
ASSERT_TRUE(h_obj.Get() != nullptr);
ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
- // Modify fields inside transaction and abort it.
+ // Modify fields inside transaction then rollback changes.
Transaction transaction;
Runtime::Current()->EnterTransactionMode(&transaction);
booleanField->SetBoolean<true>(h_instance.Get(), true);
@@ -280,7 +348,7 @@
doubleField->SetDouble<true>(h_instance.Get(), 1.0);
objectField->SetObject<true>(h_instance.Get(), h_obj.Get());
Runtime::Current()->ExitTransactionMode();
- transaction.Abort();
+ transaction.Rollback();
// Check values have properly been restored to their original (default) value.
EXPECT_EQ(booleanField->GetBoolean(h_instance.Get()), false);
@@ -294,7 +362,7 @@
EXPECT_EQ(objectField->GetObject(h_instance.Get()), nullptr);
}
-
+// Tests static array fields are reset to their default value after transaction rollback.
TEST_F(TransactionTest, StaticArrayFieldsTest) {
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<4> hs(soa.Self());
@@ -305,8 +373,10 @@
Handle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStaticArrayFieldsTest;", class_loader)));
ASSERT_TRUE(h_klass.Get() != nullptr);
- class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+ bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+ ASSERT_TRUE(success);
ASSERT_TRUE(h_klass->IsInitialized());
+ ASSERT_FALSE(soa.Self()->IsExceptionPending());
// Lookup fields.
mirror::ArtField* booleanArrayField = h_klass->FindDeclaredStaticField("booleanArrayField", "[Z");
@@ -382,7 +452,7 @@
ASSERT_TRUE(h_obj.Get() != nullptr);
ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
- // Modify fields inside transaction and abort it.
+ // Modify fields inside transaction then rollback changes.
Transaction transaction;
Runtime::Current()->EnterTransactionMode(&transaction);
booleanArray->SetWithoutChecks<true>(0, true);
@@ -395,7 +465,7 @@
doubleArray->SetWithoutChecks<true>(0, 1.0);
objectArray->SetWithoutChecks<true>(0, h_obj.Get());
Runtime::Current()->ExitTransactionMode();
- transaction.Abort();
+ transaction.Rollback();
// Check values have properly been restored to their original (default) value.
EXPECT_EQ(booleanArray->GetWithoutChecks(0), false);
@@ -409,6 +479,7 @@
EXPECT_EQ(objectArray->GetWithoutChecks(0), nullptr);
}
+// Tests successful class initialization without class initializer.
TEST_F(TransactionTest, EmptyClass) {
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<2> hs(soa.Self());
@@ -417,18 +488,22 @@
ASSERT_TRUE(class_loader.Get() != nullptr);
Handle<mirror::Class> h_klass(
- hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$EmptyStatic;", class_loader)));
+ hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$EmptyStatic;",
+ class_loader)));
ASSERT_TRUE(h_klass.Get() != nullptr);
class_linker_->VerifyClass(soa.Self(), h_klass);
ASSERT_TRUE(h_klass->IsVerified());
Transaction transaction;
Runtime::Current()->EnterTransactionMode(&transaction);
- class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+ bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
Runtime::Current()->ExitTransactionMode();
+ ASSERT_TRUE(success);
+ ASSERT_TRUE(h_klass->IsInitialized());
ASSERT_FALSE(soa.Self()->IsExceptionPending());
}
+// Tests successful class initialization with class initializer.
TEST_F(TransactionTest, StaticFieldClass) {
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<2> hs(soa.Self());
@@ -445,51 +520,37 @@
Transaction transaction;
Runtime::Current()->EnterTransactionMode(&transaction);
- class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+ bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
Runtime::Current()->ExitTransactionMode();
+ ASSERT_TRUE(success);
+ ASSERT_TRUE(h_klass->IsInitialized());
ASSERT_FALSE(soa.Self()->IsExceptionPending());
}
-TEST_F(TransactionTest, BlacklistedClass) {
- ScopedObjectAccess soa(Thread::Current());
- jobject jclass_loader = LoadDex("Transaction");
- StackHandleScope<2> hs(soa.Self());
- Handle<mirror::ClassLoader> class_loader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
- ASSERT_TRUE(class_loader.Get() != nullptr);
-
- // Load and verify java.lang.ExceptionInInitializerError and java.lang.InternalError which will
- // be thrown by class initialization due to native call.
- MutableHandle<mirror::Class> h_klass(
- hs.NewHandle(class_linker_->FindSystemClass(soa.Self(),
- "Ljava/lang/ExceptionInInitializerError;")));
- ASSERT_TRUE(h_klass.Get() != nullptr);
- class_linker_->VerifyClass(soa.Self(), h_klass);
- ASSERT_TRUE(h_klass->IsVerified());
- h_klass.Assign(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/InternalError;"));
- ASSERT_TRUE(h_klass.Get() != nullptr);
- class_linker_->VerifyClass(soa.Self(), h_klass);
- ASSERT_TRUE(h_klass->IsVerified());
-
- // Load and verify Transaction$NativeSupport used in class initialization.
- h_klass.Assign(class_linker_->FindClass(soa.Self(), "LTransaction$NativeSupport;",
- class_loader));
- ASSERT_TRUE(h_klass.Get() != nullptr);
- class_linker_->VerifyClass(soa.Self(), h_klass);
- ASSERT_TRUE(h_klass->IsVerified());
-
- h_klass.Assign(class_linker_->FindClass(soa.Self(), "LTransaction$BlacklistedClass;",
- class_loader));
- ASSERT_TRUE(h_klass.Get() != nullptr);
- class_linker_->VerifyClass(soa.Self(), h_klass);
- ASSERT_TRUE(h_klass->IsVerified());
-
- Transaction transaction;
- Runtime::Current()->EnterTransactionMode(&transaction);
- class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
- Runtime::Current()->ExitTransactionMode();
- ASSERT_TRUE(soa.Self()->IsExceptionPending());
+// Tests failing class initialization due to native call.
+TEST_F(TransactionTest, NativeCallAbortClass) {
+ testTransactionAbort("LTransaction$NativeCallAbortClass;");
}
+// Tests failing class initialization due to native call in a "synchronized" statement
+// (which must catch any exception, do the monitor-exit then re-throw the caught exception).
+TEST_F(TransactionTest, SynchronizedNativeCallAbortClass) {
+ testTransactionAbort("LTransaction$SynchronizedNativeCallAbortClass;");
+}
+// Tests failing class initialization due to native call, even if an "all" catch handler
+// catches the exception thrown when aborting the transaction.
+TEST_F(TransactionTest, CatchNativeCallAbortClass) {
+ testTransactionAbort("LTransaction$CatchNativeCallAbortClass;");
+}
+
+// Tests failing class initialization with multiple transaction aborts.
+TEST_F(TransactionTest, MultipleNativeCallAbortClass) {
+ testTransactionAbort("LTransaction$MultipleNativeCallAbortClass;");
+}
+
+// Tests failing class initialization due to allocating instance of finalizable class.
+TEST_F(TransactionTest, FinalizableAbortClass) {
+ testTransactionAbort("LTransaction$FinalizableAbortClass;");
+}
} // namespace art
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 1211547..af16d7e 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -39,17 +39,10 @@
#include "scoped_thread_state_change.h"
#include "utf-inl.h"
-#if !defined(HAVE_POSIX_CLOCKS)
-#include <sys/time.h>
-#endif
-
-#if defined(HAVE_PRCTL)
-#include <sys/prctl.h>
-#endif
-
#if defined(__APPLE__)
#include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED
#include <sys/syscall.h>
+#include <sys/time.h>
#endif
#include <backtrace/Backtrace.h> // For DumpNativeStack.
@@ -60,6 +53,10 @@
namespace art {
+#if defined(__linux__)
+static constexpr bool kUseAddr2line = !kIsTargetBuild;
+#endif
+
pid_t GetTid() {
#if defined(__APPLE__)
uint64_t owner;
@@ -164,11 +161,11 @@
}
uint64_t MilliTime() {
-#if defined(HAVE_POSIX_CLOCKS)
+#if defined(__linux__)
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000) + now.tv_nsec / UINT64_C(1000000);
-#else
+#else // __APPLE__
timeval now;
gettimeofday(&now, NULL);
return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000) + now.tv_usec / UINT64_C(1000);
@@ -176,11 +173,11 @@
}
uint64_t MicroTime() {
-#if defined(HAVE_POSIX_CLOCKS)
+#if defined(__linux__)
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000) + now.tv_nsec / UINT64_C(1000);
-#else
+#else // __APPLE__
timeval now;
gettimeofday(&now, NULL);
return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000) + now.tv_usec;
@@ -188,11 +185,11 @@
}
uint64_t NanoTime() {
-#if defined(HAVE_POSIX_CLOCKS)
+#if defined(__linux__)
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000000) + now.tv_nsec;
-#else
+#else // __APPLE__
timeval now;
gettimeofday(&now, NULL);
return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000000) + now.tv_usec * UINT64_C(1000);
@@ -200,11 +197,11 @@
}
uint64_t ThreadCpuNanoTime() {
-#if defined(HAVE_POSIX_CLOCKS)
+#if defined(__linux__)
timespec now;
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now);
return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000000) + now.tv_nsec;
-#else
+#else // __APPLE__
UNIMPLEMENTED(WARNING);
return -1;
#endif
@@ -1057,21 +1054,17 @@
} else {
s = thread_name + len - 15;
}
-#if defined(__BIONIC__)
+#if defined(__linux__)
// pthread_setname_np fails rather than truncating long strings.
- char buf[16]; // MAX_TASK_COMM_LEN=16 is hard-coded into bionic
+ char buf[16]; // MAX_TASK_COMM_LEN=16 is hard-coded in the kernel.
strncpy(buf, s, sizeof(buf)-1);
buf[sizeof(buf)-1] = '\0';
errno = pthread_setname_np(pthread_self(), buf);
if (errno != 0) {
PLOG(WARNING) << "Unable to set the name of current thread to '" << buf << "'";
}
-#elif defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
+#else // __APPLE__
pthread_setname_np(thread_name);
-#elif defined(HAVE_PRCTL)
- prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0); // NOLINT (unsigned long)
-#else
- UNIMPLEMENTED(WARNING) << thread_name;
#endif
}
@@ -1117,8 +1110,76 @@
return "";
}
+#if defined(__linux__)
+
+ALWAYS_INLINE
+static inline void WritePrefix(std::ostream* os, const char* prefix, bool odd) {
+ if (prefix != nullptr) {
+ *os << prefix;
+ }
+ *os << " ";
+ if (!odd) {
+ *os << " ";
+ }
+}
+
+static bool RunCommand(std::string cmd, std::ostream* os, const char* prefix) {
+ FILE* stream = popen(cmd.c_str(), "r");
+ if (stream) {
+ if (os != nullptr) {
+ bool odd_line = true; // We indent them differently.
+ bool wrote_prefix = false; // Have we already written a prefix?
+ constexpr size_t kMaxBuffer = 128; // Relatively small buffer. Should be OK as we're on an
+ // alt stack, but just to be sure...
+ char buffer[kMaxBuffer];
+ while (!feof(stream)) {
+ if (fgets(buffer, kMaxBuffer, stream) != nullptr) {
+ // Split on newlines.
+ char* tmp = buffer;
+ for (;;) {
+ char* new_line = strchr(tmp, '\n');
+ if (new_line == nullptr) {
+ // Print the rest.
+ if (*tmp != 0) {
+ if (!wrote_prefix) {
+ WritePrefix(os, prefix, odd_line);
+ }
+ wrote_prefix = true;
+ *os << tmp;
+ }
+ break;
+ }
+ if (!wrote_prefix) {
+ WritePrefix(os, prefix, odd_line);
+ }
+ char saved = *(new_line + 1);
+ *(new_line + 1) = 0;
+ *os << tmp;
+ *(new_line + 1) = saved;
+ tmp = new_line + 1;
+ odd_line = !odd_line;
+ wrote_prefix = false;
+ }
+ }
+ }
+ }
+ pclose(stream);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static void Addr2line(const std::string& map_src, uintptr_t offset, std::ostream& os,
+ const char* prefix) {
+ std::string cmdline(StringPrintf("addr2line --functions --inlines --demangle -e %s %zx",
+ map_src.c_str(), offset));
+ RunCommand(cmdline.c_str(), &os, prefix);
+}
+#endif
+
void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix,
- mirror::ArtMethod* current_method) {
+ mirror::ArtMethod* current_method, void* ucontext_ptr) {
#if __linux__
// b/18119146
if (RUNNING_ON_VALGRIND != 0) {
@@ -1134,7 +1195,7 @@
#endif
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
- if (!backtrace->Unwind(0)) {
+ if (!backtrace->Unwind(0, reinterpret_cast<ucontext*>(ucontext_ptr))) {
os << prefix << "(backtrace::Unwind failed for thread " << tid << ")\n";
return;
} else if (backtrace->NumFrames() == 0) {
@@ -1142,6 +1203,16 @@
return;
}
+ // Check whether we have and should use addr2line.
+ bool use_addr2line;
+ if (kUseAddr2line) {
+ // Try to run it to see whether we have it. Push an argument so that it doesn't assume a.out
+ // and print to stderr.
+ use_addr2line = (gAborting > 0) && RunCommand("addr2line -h", nullptr, nullptr);
+ } else {
+ use_addr2line = false;
+ }
+
for (Backtrace::const_iterator it = backtrace->begin();
it != backtrace->end(); ++it) {
// We produce output like this:
@@ -1153,16 +1224,19 @@
// after the <RELATIVE_ADDR>. There can be any prefix data before the
// #XX. <RELATIVE_ADDR> has to be a hex number but with no 0x prefix.
os << prefix << StringPrintf("#%02zu pc ", it->num);
- if (!it->map) {
+ bool try_addr2line = false;
+ if (!BacktraceMap::IsValid(it->map)) {
os << StringPrintf("%08" PRIxPTR " ???", it->pc);
} else {
- os << StringPrintf("%08" PRIxPTR " ", it->pc - it->map->start)
- << it->map->name << " (";
+ os << StringPrintf("%08" PRIxPTR " ", it->pc - it->map.start);
+ os << it->map.name;
+ os << " (";
if (!it->func_name.empty()) {
os << it->func_name;
if (it->func_offset != 0) {
os << "+" << it->func_offset;
}
+ try_addr2line = true;
} else if (current_method != nullptr &&
Locks::mutator_lock_->IsSharedHeld(Thread::Current()) &&
current_method->PcIsWithinQuickCode(it->pc)) {
@@ -1175,9 +1249,12 @@
os << ")";
}
os << "\n";
+ if (try_addr2line && use_addr2line) {
+ Addr2line(it->map.name, it->pc - it->map.start, os, prefix);
+ }
}
#else
- UNUSED(os, tid, prefix, current_method);
+ UNUSED(os, tid, prefix, current_method, ucontext_ptr);
#endif
}
diff --git a/runtime/utils.h b/runtime/utils.h
index 668c897..1c2576c 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -430,7 +430,8 @@
// Sleep for the given number of nanoseconds, a bad way to handle contention.
void NanoSleep(uint64_t ns);
-// Initialize a timespec to either an absolute or relative time.
+// Initialize a timespec to either a relative time (ms,ns), or to the absolute
+// time corresponding to the indicated clock value plus the supplied offset.
void InitTimeSpec(bool absolute, int clock, int64_t ms, int32_t ns, timespec* ts);
// Splits a string using the given separator character into a vector of
@@ -464,7 +465,7 @@
// Dumps the native stack for thread 'tid' to 'os'.
void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix = "",
- mirror::ArtMethod* current_method = nullptr)
+ mirror::ArtMethod* current_method = nullptr, void* ucontext = nullptr)
NO_THREAD_SAFETY_ANALYSIS;
// Dumps the kernel stack for thread 'tid' to 'os'. Note that this is only available on linux-x86.
@@ -548,6 +549,13 @@
template <typename T>
using UniqueCPtr = std::unique_ptr<T, FreeDelete>;
+// C++14 from-the-future import (std::make_unique)
+// Invoke the constructor of 'T' with the provided args, and wrap the result in a unique ptr.
+template <typename T, typename ... Args>
+std::unique_ptr<T> MakeUnique(Args&& ... args) {
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
} // namespace art
#endif // ART_RUNTIME_UTILS_H_
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index a98bc90..a3dd13c 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -392,6 +392,9 @@
}
TEST_F(UtilsTest, ExecError) {
+ // This will lead to error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
std::vector<std::string> command;
command.push_back("bogus");
std::string error_msg;
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 1b3cc8f..474a066 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -103,6 +103,14 @@
return false;
}
+static void SafelyMarkAllRegistersAsConflicts(MethodVerifier* verifier, RegisterLine* reg_line) {
+ if (verifier->IsConstructor()) {
+ // Before we mark all regs as conflicts, check that we don't have an uninitialized this.
+ reg_line->CheckConstructorReturn(verifier);
+ }
+ reg_line->MarkAllRegistersAsConflicts(verifier);
+}
+
MethodVerifier::FailureKind MethodVerifier::VerifyClass(Thread* self,
mirror::Class* klass,
bool allow_soft_failures,
@@ -278,7 +286,7 @@
MethodVerifier verifier(self, dex_file, dex_cache, class_loader, class_def, code_item,
method_idx, method, method_access_flags, true, allow_soft_failures,
- need_precise_constants);
+ need_precise_constants, true);
if (verifier.Verify()) {
// Verification completed, however failures may be pending that didn't cause the verification
// to hard fail.
@@ -344,7 +352,8 @@
const DexFile::CodeItem* code_item, uint32_t dex_method_idx,
Handle<mirror::ArtMethod> method, uint32_t method_access_flags,
bool can_load_classes, bool allow_soft_failures,
- bool need_precise_constants, bool verify_to_dump)
+ bool need_precise_constants, bool verify_to_dump,
+ bool allow_thread_suspension)
: self_(self),
reg_types_(can_load_classes),
work_insn_idx_(-1),
@@ -369,7 +378,8 @@
need_precise_constants_(need_precise_constants),
has_check_casts_(false),
has_virtual_or_interface_invokes_(false),
- verify_to_dump_(verify_to_dump) {
+ verify_to_dump_(verify_to_dump),
+ allow_thread_suspension_(allow_thread_suspension) {
Runtime::Current()->AddMethodVerifier(this);
DCHECK(class_def != nullptr);
}
@@ -388,7 +398,7 @@
Handle<mirror::ArtMethod> method(hs.NewHandle(m));
MethodVerifier verifier(self, m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(),
m->GetCodeItem(), m->GetDexMethodIndex(), method, m->GetAccessFlags(),
- false, true, false);
+ false, true, false, false);
verifier.interesting_dex_pc_ = dex_pc;
verifier.monitor_enter_dex_pcs_ = monitor_enter_dex_pcs;
verifier.FindLocksAtDexPc();
@@ -435,7 +445,7 @@
Handle<mirror::ArtMethod> method(hs.NewHandle(m));
MethodVerifier verifier(self, m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(),
m->GetCodeItem(), m->GetDexMethodIndex(), method, m->GetAccessFlags(),
- true, true, false);
+ true, true, false, true);
return verifier.FindAccessedFieldAtDexPc(dex_pc);
}
@@ -467,7 +477,7 @@
Handle<mirror::ArtMethod> method(hs.NewHandle(m));
MethodVerifier verifier(self, m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(),
m->GetCodeItem(), m->GetDexMethodIndex(), method, m->GetAccessFlags(),
- true, true, false);
+ true, true, false, true);
return verifier.FindInvokedMethodAtDexPc(dex_pc);
}
@@ -725,7 +735,16 @@
/* Flag instructions that are garbage collection points */
// All invoke points are marked as "Throw" points already.
// We are relying on this to also count all the invokes as interesting.
- if (inst->IsBranch() || inst->IsSwitch() || inst->IsThrow()) {
+ if (inst->IsBranch()) {
+ insn_flags_[dex_pc].SetCompileTimeInfoPoint();
+ // The compiler also needs safepoints for fall-through to loop heads.
+ // Such a loop head must be a target of a branch.
+ int32_t offset = 0;
+ bool cond, self_ok;
+ bool target_ok = GetBranchOffset(dex_pc, &offset, &cond, &self_ok);
+ DCHECK(target_ok);
+ insn_flags_[dex_pc + offset].SetCompileTimeInfoPoint();
+ } else if (inst->IsSwitch() || inst->IsThrow()) {
insn_flags_[dex_pc].SetCompileTimeInfoPoint();
} else if (inst->IsReturn()) {
insn_flags_[dex_pc].SetCompileTimeInfoPointAndReturn();
@@ -1385,7 +1404,9 @@
/* Continue until no instructions are marked "changed". */
while (true) {
- self_->AllowThreadSuspension();
+ if (allow_thread_suspension_) {
+ self_->AllowThreadSuspension();
+ }
// Find the first marked one. Use "start_guess" as a way to find one quickly.
uint32_t insn_idx = start_guess;
for (; insn_idx < insns_size; insn_idx++) {
@@ -1550,6 +1571,16 @@
std::unique_ptr<RegisterLine> branch_line;
std::unique_ptr<RegisterLine> fallthrough_line;
+ /*
+ * If we are in a constructor, and we currently have an UninitializedThis type
+ * in a register somewhere, we need to make sure it isn't overwritten.
+ */
+ bool track_uninitialized_this = false;
+ size_t uninitialized_this_loc = 0;
+ if (IsConstructor()) {
+ track_uninitialized_this = work_line_->GetUninitializedThisLoc(this, &uninitialized_this_loc);
+ }
+
switch (inst->Opcode()) {
case Instruction::NOP:
/*
@@ -1612,6 +1643,12 @@
break;
case Instruction::MOVE_EXCEPTION: {
+ // We do not allow MOVE_EXCEPTION as the first instruction in a method. This is a simple case
+ // where one entrypoint to the catch block is not actually an exception path.
+ if (work_insn_idx_ == 0) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "move-exception at pc 0x0";
+ break;
+ }
/*
* This statement can only appear as the first instruction in an exception handler. We verify
* that as part of extracting the exception type from the catch block list.
@@ -2696,6 +2733,18 @@
case Instruction::IGET_OBJECT_QUICK:
VerifyQuickFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.JavaLangObject(false), false);
break;
+ case Instruction::IGET_BOOLEAN_QUICK:
+ VerifyQuickFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Boolean(), true);
+ break;
+ case Instruction::IGET_BYTE_QUICK:
+ VerifyQuickFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Byte(), true);
+ break;
+ case Instruction::IGET_CHAR_QUICK:
+ VerifyQuickFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Char(), true);
+ break;
+ case Instruction::IGET_SHORT_QUICK:
+ VerifyQuickFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Short(), true);
+ break;
case Instruction::IPUT_QUICK:
VerifyQuickFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Integer(), true);
break;
@@ -2735,31 +2784,10 @@
}
/* These should never appear during verification. */
- case Instruction::UNUSED_3E:
- case Instruction::UNUSED_3F:
- case Instruction::UNUSED_40:
- case Instruction::UNUSED_41:
- case Instruction::UNUSED_42:
- case Instruction::UNUSED_43:
+ case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
+ case Instruction::UNUSED_F3 ... Instruction::UNUSED_FF:
case Instruction::UNUSED_79:
case Instruction::UNUSED_7A:
- case Instruction::UNUSED_EF:
- case Instruction::UNUSED_F0:
- case Instruction::UNUSED_F1:
- case Instruction::UNUSED_F2:
- case Instruction::UNUSED_F3:
- case Instruction::UNUSED_F4:
- case Instruction::UNUSED_F5:
- case Instruction::UNUSED_F6:
- case Instruction::UNUSED_F7:
- case Instruction::UNUSED_F8:
- case Instruction::UNUSED_F9:
- case Instruction::UNUSED_FA:
- case Instruction::UNUSED_FB:
- case Instruction::UNUSED_FC:
- case Instruction::UNUSED_FD:
- case Instruction::UNUSED_FE:
- case Instruction::UNUSED_FF:
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_);
break;
@@ -2769,6 +2797,20 @@
*/
} // end - switch (dec_insn.opcode)
+ /*
+ * If we are in a constructor, and we had an UninitializedThis type
+ * in a register somewhere, we need to make sure it wasn't overwritten.
+ */
+ if (track_uninitialized_this) {
+ bool was_invoke_direct = (inst->Opcode() == Instruction::INVOKE_DIRECT ||
+ inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE);
+ if (work_line_->WasUninitializedThisOverwritten(this, uninitialized_this_loc,
+ was_invoke_direct)) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "Constructor failed to initialize this object";
+ }
+ }
+
if (have_pending_hard_failure_) {
if (Runtime::Current()->IsCompiler()) {
/* When compiling, check that the last failure is a hard failure */
@@ -2950,7 +2992,7 @@
const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn_idx);
Instruction::Code opcode = ret_inst->Opcode();
if ((opcode == Instruction::RETURN_VOID) || (opcode == Instruction::RETURN_VOID_BARRIER)) {
- work_line_->MarkAllRegistersAsConflicts(this);
+ SafelyMarkAllRegistersAsConflicts(this, work_line_.get());
} else {
if (opcode == Instruction::RETURN_WIDE) {
work_line_->MarkAllRegistersAsConflictsExceptWide(this, ret_inst->VRegA_11x());
@@ -3159,7 +3201,7 @@
}
// See if the method type implied by the invoke instruction matches the access flags for the
// target method.
- if ((method_type == METHOD_DIRECT && !res_method->IsDirect()) ||
+ if ((method_type == METHOD_DIRECT && (!res_method->IsDirect() || res_method->IsStatic())) ||
(method_type == METHOD_STATIC && !res_method->IsStatic()) ||
((method_type == METHOD_VIRTUAL || method_type == METHOD_INTERFACE) && res_method->IsDirect())
) {
@@ -3408,7 +3450,7 @@
return nullptr;
}
mirror::ArtMethod* res_method = dispatch_class->GetVTableEntry(vtable_index);
- if (FailOrAbort(this, !Thread::Current()->IsExceptionPending(),
+ if (FailOrAbort(this, !self_->IsExceptionPending(),
"Unexpected exception pending for quickened invoke at ",
work_insn_idx_)) {
return nullptr;
@@ -3418,7 +3460,9 @@
mirror::ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst,
bool is_range) {
- DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_);
+ DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_)
+ << PrettyMethod(dex_method_idx_, *dex_file_, true) << "@" << work_insn_idx_;
+
mirror::ArtMethod* res_method = GetQuickInvokedMethod(inst, work_line_.get(),
is_range);
if (res_method == nullptr) {
@@ -3900,6 +3944,10 @@
DCHECK(inst->Opcode() == Instruction::IGET_QUICK ||
inst->Opcode() == Instruction::IGET_WIDE_QUICK ||
inst->Opcode() == Instruction::IGET_OBJECT_QUICK ||
+ inst->Opcode() == Instruction::IGET_BOOLEAN_QUICK ||
+ inst->Opcode() == Instruction::IGET_BYTE_QUICK ||
+ inst->Opcode() == Instruction::IGET_CHAR_QUICK ||
+ inst->Opcode() == Instruction::IGET_SHORT_QUICK ||
inst->Opcode() == Instruction::IPUT_QUICK ||
inst->Opcode() == Instruction::IPUT_WIDE_QUICK ||
inst->Opcode() == Instruction::IPUT_OBJECT_QUICK ||
@@ -4099,7 +4147,7 @@
const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn);
Instruction::Code opcode = ret_inst->Opcode();
if ((opcode == Instruction::RETURN_VOID) || (opcode == Instruction::RETURN_VOID_BARRIER)) {
- target_line->MarkAllRegistersAsConflicts(this);
+ SafelyMarkAllRegistersAsConflicts(this, target_line);
} else {
target_line->CopyFromLine(merge_line);
if (opcode == Instruction::RETURN_WIDE) {
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 0c4bf3c..b83e647 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -207,10 +207,11 @@
const DexFile::CodeItem* code_item, uint32_t method_idx,
Handle<mirror::ArtMethod> method,
uint32_t access_flags, bool can_load_classes, bool allow_soft_failures,
- bool need_precise_constants) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ bool need_precise_constants, bool allow_thread_suspension)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
: MethodVerifier(self, dex_file, dex_cache, class_loader, class_def, code_item, method_idx,
method, access_flags, can_load_classes, allow_soft_failures,
- need_precise_constants, false) {}
+ need_precise_constants, false, allow_thread_suspension) {}
~MethodVerifier();
@@ -238,6 +239,20 @@
bool HasFailures() const;
const RegType& ResolveCheckedClass(uint32_t class_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::ArtMethod* GetQuickInvokedMethod(const Instruction* inst,
+ RegisterLine* reg_line,
+ bool is_range)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Is the method being verified a constructor?
+ bool IsConstructor() const {
+ return (method_access_flags_ & kAccConstructor) != 0;
+ }
+
+ // Is the method verified static?
+ bool IsStatic() const {
+ return (method_access_flags_ & kAccStatic) != 0;
+ }
private:
// Private constructor for dumping.
@@ -246,7 +261,7 @@
const DexFile::CodeItem* code_item, uint32_t method_idx,
Handle<mirror::ArtMethod> method, uint32_t access_flags,
bool can_load_classes, bool allow_soft_failures, bool need_precise_constants,
- bool verify_to_dump)
+ bool verify_to_dump, bool allow_thread_suspension)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Adds the given string to the beginning of the last failure message.
@@ -586,11 +601,6 @@
mirror::ArtMethod* res_method)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- mirror::ArtMethod* GetQuickInvokedMethod(const Instruction* inst,
- RegisterLine* reg_line,
- bool is_range)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
mirror::ArtMethod* VerifyInvokeVirtualQuickArgs(const Instruction* inst, bool is_range)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -626,16 +636,6 @@
bool UpdateRegisters(uint32_t next_insn, RegisterLine* merge_line, bool update_merge_line)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Is the method being verified a constructor?
- bool IsConstructor() const {
- return (method_access_flags_ & kAccConstructor) != 0;
- }
-
- // Is the method verified static?
- bool IsStatic() const {
- return (method_access_flags_ & kAccStatic) != 0;
- }
-
// Return the register type for the method.
const RegType& GetMethodReturnType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -730,6 +730,11 @@
// VerifyMethodAndDump.
const bool verify_to_dump_;
+ // Whether or not we call AllowThreadSuspension periodically, we want a way to disable this for
+ // thread dumping checkpoints since we may get thread suspension at an inopportune time due to
+ // FindLocksAtDexPC, resulting in deadlocks.
+ const bool allow_thread_suspension_;
+
DISALLOW_COPY_AND_ASSIGN(MethodVerifier);
};
std::ostream& operator<<(std::ostream& os, const MethodVerifier::FailureKind& rhs);
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index 770ca7e..f67adc1 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -41,14 +41,12 @@
<< error_msg;
}
- void VerifyDexFile(const DexFile* dex)
+ void VerifyDexFile(const DexFile& dex)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ASSERT_TRUE(dex != NULL);
-
// Verify all the classes defined in this file
- for (size_t i = 0; i < dex->NumClassDefs(); i++) {
- const DexFile::ClassDef& class_def = dex->GetClassDef(i);
- const char* descriptor = dex->GetClassDescriptor(class_def);
+ for (size_t i = 0; i < dex.NumClassDefs(); i++) {
+ const DexFile::ClassDef& class_def = dex.GetClassDef(i);
+ const char* descriptor = dex.GetClassDescriptor(class_def);
VerifyClass(descriptor);
}
}
@@ -56,7 +54,8 @@
TEST_F(MethodVerifierTest, LibCore) {
ScopedObjectAccess soa(Thread::Current());
- VerifyDexFile(java_lang_dex_file_);
+ ASSERT_TRUE(java_lang_dex_file_ != nullptr);
+ VerifyDexFile(*java_lang_dex_file_);
}
} // namespace verifier
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 41541b5..3510665 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -779,9 +779,7 @@
}
void RegType::VisitRoots(RootCallback* callback, void* arg) const {
- if (!klass_.IsNull()) {
- callback(reinterpret_cast<mirror::Object**>(&klass_), arg, 0, kRootUnknown);
- }
+ klass_.VisitRootIfNonNull(callback, arg, RootInfo(kRootUnknown));
}
void UninitializedThisReferenceType::CheckInvariants() const {
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index 72d7938..3b09871 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -25,6 +25,49 @@
namespace art {
namespace verifier {
+bool RegisterLine::WasUninitializedThisOverwritten(MethodVerifier* verifier,
+ size_t this_loc,
+ bool was_invoke_direct) const {
+ DCHECK(verifier->IsConstructor());
+
+ // Is the UnintializedThis type still there?
+ if (GetRegisterType(verifier, this_loc).IsUninitializedThisReference() ||
+ GetRegisterType(verifier, this_loc).IsUnresolvedAndUninitializedThisReference()) {
+ return false;
+ }
+
+ // If there is an initialized reference here now, did we just perform an invoke-direct? Note that
+ // this is the correct approach for dex bytecode: results of invoke-direct are stored in the
+ // result register. Overwriting "this_loc" can only be done by a constructor call.
+ if (GetRegisterType(verifier, this_loc).IsReferenceTypes() && was_invoke_direct) {
+ return false;
+ // Otherwise we could have just copied a different initialized reference to this location.
+ }
+
+ // The UnintializedThis in the register is gone, so check to see if it's somewhere else now.
+ for (size_t i = 0; i < num_regs_; i++) {
+ if (GetRegisterType(verifier, i).IsUninitializedThisReference() ||
+ GetRegisterType(verifier, i).IsUnresolvedAndUninitializedThisReference()) {
+ // We found it somewhere else...
+ return false;
+ }
+ }
+
+ // The UninitializedThis is gone from the original register, and now we can't find it.
+ return true;
+}
+
+bool RegisterLine::GetUninitializedThisLoc(MethodVerifier* verifier, size_t* vreg) const {
+ for (size_t i = 0; i < num_regs_; i++) {
+ if (GetRegisterType(verifier, i).IsUninitializedThisReference() ||
+ GetRegisterType(verifier, i).IsUnresolvedAndUninitializedThisReference()) {
+ *vreg = i;
+ return true;
+ }
+ }
+ return false;
+}
+
bool RegisterLine::CheckConstructorReturn(MethodVerifier* verifier) const {
for (size_t i = 0; i < num_regs_; i++) {
if (GetRegisterType(verifier, i).IsUninitializedThisReference() ||
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index 52b5c13..ca61a0b 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -157,6 +157,18 @@
*/
bool CheckConstructorReturn(MethodVerifier* verifier) const;
+ /*
+ * Check if an UninitializedThis at the specified location has been overwritten before
+ * being correctly initialized.
+ */
+ bool WasUninitializedThisOverwritten(MethodVerifier* verifier, size_t this_loc,
+ bool was_invoke_direct) const;
+
+ /*
+ * Get the first location of an UninitializedThis type, or return kInvalidVreg if there are none.
+ */
+ bool GetUninitializedThisLoc(MethodVerifier* verifier, size_t* vreg) const;
+
// Compare two register lines. Returns 0 if they match.
// Using this for a sort is unwise, since the value can change based on machine endianness.
int CompareLine(const RegisterLine* line2) const {
diff --git a/runtime/vmap_table.h b/runtime/vmap_table.h
index df5cd80..db9e1ea 100644
--- a/runtime/vmap_table.h
+++ b/runtime/vmap_table.h
@@ -65,7 +65,7 @@
uint16_t adjusted_vreg = vreg + kEntryAdjustment;
size_t end = DecodeUnsignedLeb128(&table);
bool high_reg = (kind == kLongHiVReg) || (kind == kDoubleHiVReg);
- bool target64 = (kRuntimeISA == kArm64) || (kRuntimeISA == kX86_64);
+ bool target64 = (kRuntimeISA == kArm64) || (kRuntimeISA == kX86_64) || (kRuntimeISA == kMips64);
if (target64 && high_reg) {
// Wide promoted registers are associated with the sreg of the low portion.
adjusted_vreg--;
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 16338c4..e368d2c 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -66,9 +66,9 @@
jmethodID WellKnownClasses::java_lang_Character_valueOf;
jmethodID WellKnownClasses::java_lang_ClassLoader_loadClass;
jmethodID WellKnownClasses::java_lang_ClassNotFoundException_init;
-jmethodID WellKnownClasses::java_lang_Daemons_requestGC;
jmethodID WellKnownClasses::java_lang_Daemons_requestHeapTrim;
jmethodID WellKnownClasses::java_lang_Daemons_start;
+jmethodID WellKnownClasses::java_lang_Daemons_stop;
jmethodID WellKnownClasses::java_lang_Double_valueOf;
jmethodID WellKnownClasses::java_lang_Float_valueOf;
jmethodID WellKnownClasses::java_lang_Integer_valueOf;
@@ -204,9 +204,9 @@
java_lang_ClassNotFoundException_init = CacheMethod(env, java_lang_ClassNotFoundException, false, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V");
java_lang_ClassLoader_loadClass = CacheMethod(env, java_lang_ClassLoader, false, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
- java_lang_Daemons_requestGC = CacheMethod(env, java_lang_Daemons, true, "requestGC", "()V");
java_lang_Daemons_requestHeapTrim = CacheMethod(env, java_lang_Daemons, true, "requestHeapTrim", "()V");
java_lang_Daemons_start = CacheMethod(env, java_lang_Daemons, true, "start", "()V");
+ java_lang_Daemons_stop = CacheMethod(env, java_lang_Daemons, true, "stop", "()V");
ScopedLocalRef<jclass> java_lang_ref_FinalizerReference(env, env->FindClass("java/lang/ref/FinalizerReference"));
java_lang_ref_FinalizerReference_add = CacheMethod(env, java_lang_ref_FinalizerReference.get(), true, "add", "(Ljava/lang/Object;)V");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index d651b90..1a4f0f8 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -77,9 +77,9 @@
static jmethodID java_lang_Character_valueOf;
static jmethodID java_lang_ClassLoader_loadClass;
static jmethodID java_lang_ClassNotFoundException_init;
- static jmethodID java_lang_Daemons_requestGC;
static jmethodID java_lang_Daemons_requestHeapTrim;
static jmethodID java_lang_Daemons_start;
+ static jmethodID java_lang_Daemons_stop;
static jmethodID java_lang_Double_valueOf;
static jmethodID java_lang_Float_valueOf;
static jmethodID java_lang_Integer_valueOf;
diff --git a/sigchainlib/Android.mk b/sigchainlib/Android.mk
index b7ff360..e1aae11 100644
--- a/sigchainlib/Android.mk
+++ b/sigchainlib/Android.mk
@@ -28,6 +28,8 @@
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_build.mk
+LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
+$(eval $(call set-target-local-clang-vars))
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
@@ -40,6 +42,7 @@
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_build.mk
+$(eval $(call set-target-local-clang-vars))
include $(BUILD_STATIC_LIBRARY)
# Build host library.
@@ -54,6 +57,7 @@
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
LOCAL_LDLIBS = -ldl
LOCAL_MULTILIB := both
+LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
include $(BUILD_HOST_SHARED_LIBRARY)
include $(CLEAR_VARS)
@@ -67,5 +71,4 @@
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
LOCAL_LDLIBS = -ldl
LOCAL_MULTILIB := both
-include external/libcxx/libcxx.mk
include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index 601e321..2eb518c 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -170,12 +170,13 @@
// Note that we check that the signal number is in range here. An out of range signal
// number should behave exactly as the libc sigaction.
if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
- if (old_action != NULL) {
- *old_action = user_sigactions[signal].GetAction();
- }
+ struct sigaction saved_action = user_sigactions[signal].GetAction();
if (new_action != NULL) {
user_sigactions[signal].SetAction(*new_action, false);
}
+ if (old_action != NULL) {
+ *old_action = saved_action;
+ }
return 0;
}
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index c2877be..544cbc5 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -294,20 +294,20 @@
assert(!env->ExceptionCheck());
// Create a string object.
- jobject library_string = env->NewStringUTF("arttest");
+ jobject library_string = env->NewStringUTF("non_existing_library");
assert(library_string != nullptr);
assert(!env->ExceptionCheck());
env->CallStaticVoidMethod(system_clazz, loadLibraryMethodId, library_string);
- if (env->ExceptionCheck()) {
- // At most we expect UnsatisfiedLinkError.
- jthrowable thrown = env->ExceptionOccurred();
- env->ExceptionClear();
+ assert(env->ExceptionCheck());
- jclass unsatisfied_link_error_clazz = env->FindClass("java/lang/UnsatisfiedLinkError");
- jclass thrown_class = env->GetObjectClass(thrown);
- assert(env->IsSameObject(unsatisfied_link_error_clazz, thrown_class));
- }
+ // We expect UnsatisfiedLinkError.
+ jthrowable thrown = env->ExceptionOccurred();
+ env->ExceptionClear();
+
+ jclass unsatisfied_link_error_clazz = env->FindClass("java/lang/UnsatisfiedLinkError");
+ jclass thrown_class = env->GetObjectClass(thrown);
+ assert(env->IsSameObject(unsatisfied_link_error_clazz, thrown_class));
}
}
diff --git a/test/004-ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
index 631c4be..40be56c 100644
--- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
@@ -52,11 +52,11 @@
// v2 is added because of the instruction at DexPC 0024. Object merges with 0 is Object. See:
// 0024: move-object v3, v2
// 0025: goto 0013
- // Detaled dex instructions for ReferenceMap.java are at the end of this function.
+ // Detailed dex instructions for ReferenceMap.java are at the end of this function.
// CHECK_REGS_CONTAIN_REFS(8, 3, 2, 1); // v8: this, v3: y, v2: y, v1: x
// We eliminate the non-live registers at a return, so only v3 is live.
// Note that it is OK for a compiler to not have a dex map at this dex PC because
- // a return is not a safepoint.
+ // a return is not necessarily a safepoint.
CHECK_REGS_CONTAIN_REFS(0x13U, false); // v3: y
CHECK_REGS_CONTAIN_REFS(0x18U, true, 8, 2, 1, 0); // v8: this, v2: y, v1: x, v0: ex
CHECK_REGS_CONTAIN_REFS(0x1aU, true, 8, 5, 2, 1, 0); // v8: this, v5: x[1], v2: y, v1: x, v0: ex
@@ -68,8 +68,10 @@
CHECK_REGS_CONTAIN_REFS(0x27U, true, 8, 4, 2, 1); // v8: this, v4: ex, v2: y, v1: x
CHECK_REGS_CONTAIN_REFS(0x29U, true, 8, 4, 2, 1); // v8: this, v4: ex, v2: y, v1: x
CHECK_REGS_CONTAIN_REFS(0x2cU, true, 8, 4, 2, 1); // v8: this, v4: ex, v2: y, v1: x
- CHECK_REGS_CONTAIN_REFS(0x2fU, true, 8, 4, 3, 2, 1); // v8: this, v4: ex, v3: y, v2: y, v1: x
- CHECK_REGS_CONTAIN_REFS(0x32U, true, 8, 3, 2, 1, 0); // v8: this, v3: y, v2: y, v1: x, v0: ex
+ // Note that it is OK for a compiler to not have a dex map at these two dex PCs because
+ // a goto is not necessarily a safepoint.
+ CHECK_REGS_CONTAIN_REFS(0x2fU, false, 8, 4, 3, 2, 1); // v8: this, v4: ex, v3: y, v2: y, v1: x
+ CHECK_REGS_CONTAIN_REFS(0x32U, false, 8, 3, 2, 1, 0); // v8: this, v3: y, v2: y, v1: x, v0: ex
}
return true;
diff --git a/test/004-SignalTest/src/Main.java b/test/004-SignalTest/src/Main.java
index 0391592..8b1f49b 100644
--- a/test/004-SignalTest/src/Main.java
+++ b/test/004-SignalTest/src/Main.java
@@ -20,7 +20,7 @@
private static native int testSignal();
private static void stackOverflow() {
- stackOverflow();
+ stackOverflow();
}
public static void main(String[] args) {
@@ -40,7 +40,6 @@
}
try {
stackOverflow();
-
// Should never get here.
throw new AssertionError();
} catch (StackOverflowError e) {
diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java
index 743d62c..3d0f074 100644
--- a/test/004-UnsafeTest/src/Main.java
+++ b/test/004-UnsafeTest/src/Main.java
@@ -94,6 +94,16 @@
unsafe.putLong(t, longOffset, longValue);
check(t.longVar, longValue, "Unsafe.putLong(Object, long, long)");
check(unsafe.getLong(t, longOffset), longValue, "Unsafe.getLong(Object, long)");
+
+ if (unsafe.compareAndSwapInt(t, intOffset, 0, 1)) {
+ System.out.println("Unexpectedly succeeding compareAndSwap...");
+ }
+ if (!unsafe.compareAndSwapInt(t, intOffset, intValue, 0)) {
+ System.out.println("Unexpectedly not succeeding compareAndSwap...");
+ }
+ if (!unsafe.compareAndSwapInt(t, intOffset, 0, 1)) {
+ System.out.println("Unexpectedly not succeeding compareAndSwap...");
+ }
}
private static class TestClass {
diff --git a/test/015-switch/expected.txt b/test/015-switch/expected.txt
index 91b4714..be6d2ca 100644
--- a/test/015-switch/expected.txt
+++ b/test/015-switch/expected.txt
@@ -1,3 +1,119 @@
+packed
+default
+default
+0
+1
+2
+default
+default
+packed2
+-2
+-1
+0
+1
+2
+default
+default
+packed3
+default
+default
+default
+default
+2
+3
+4
+5
+6
+default
+default
+packed4
+default
+2147483646
+2147483647
+default
+packed5
+-2147483648
+-2147483647
+default
+packed6
+-2147483648
+default
+packed7
+default
+default
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+default
+sparse
+default
+default
+0
+1
+default
+3
+default
+default
+sparse2
+-2
+-1
+0
+default
+2
+default
+default
+sparse3
+default
+default
+default
+default
+2
+default
+4
+5
+6
+default
+default
+sparse4
+2147483645
+default
+2147483647
+default
+sparse5
+-2147483648
+default
+default
+sparse7
+default
+default
+1
+2
+default
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+default
CORRECT (one)
CORRECT (not found)
CORRECT (large)
diff --git a/test/015-switch/src/Main.java b/test/015-switch/src/Main.java
index dd97a8c..2a7995a 100644
--- a/test/015-switch/src/Main.java
+++ b/test/015-switch/src/Main.java
@@ -18,7 +18,338 @@
* Test switch() blocks
*/
public class Main {
+
+ // TODO: This should be translated to smali tests, so it is guaranteed we have the right kind
+ // of switch.
+
+ // Simple packed-switch.
+ public static void packedSwitch(int value) {
+ switch (value) {
+ case 0:
+ System.out.println("0"); break;
+ case 1:
+ System.out.println("1"); break;
+ case 2:
+ System.out.println("2"); break;
+ case 3:
+ System.out.println("3"); break;
+ case 4:
+ System.out.println("4"); break;
+ default:
+ System.out.println("default"); break;
+ }
+ }
+
+ // Simple packed-switch starting at a negative index.
+ public static void packedSwitch2(int value) {
+ switch (value) {
+ case -3:
+ System.out.println("-3"); break;
+ case -2:
+ System.out.println("-2"); break;
+ case -1:
+ System.out.println("-1"); break;
+ case 0:
+ System.out.println("0"); break;
+ case 1:
+ System.out.println("1"); break;
+ case 2:
+ System.out.println("2"); break;
+ default:
+ System.out.println("default"); break;
+ }
+ }
+
+ // Simple packed-switch starting above 0.
+ public static void packedSwitch3(int value) {
+ switch (value) {
+ case 2:
+ System.out.println("2"); break;
+ case 3:
+ System.out.println("3"); break;
+ case 4:
+ System.out.println("4"); break;
+ case 5:
+ System.out.println("5"); break;
+ case 6:
+ System.out.println("6"); break;
+ default:
+ System.out.println("default"); break;
+ }
+ }
+
+ // Simple packed-switch going up to max_int.
+ public static void packedSwitch4(int value) {
+ switch (value) {
+ case Integer.MAX_VALUE - 1:
+ System.out.println(Integer.MAX_VALUE - 1); break;
+ case Integer.MAX_VALUE:
+ System.out.println(Integer.MAX_VALUE); break;
+ default:
+ System.out.println("default"); break;
+ }
+ }
+
+ // Simple packed-switch starting at min_int.
+ public static void packedSwitch5(int value) {
+ switch (value) {
+ case Integer.MIN_VALUE:
+ System.out.println(Integer.MIN_VALUE); break;
+ case Integer.MIN_VALUE + 1:
+ System.out.println(Integer.MIN_VALUE + 1); break;
+ default:
+ System.out.println("default"); break;
+ }
+ }
+
+ // Simple (packed-)switch with only min_int.
+ public static void packedSwitch6(int value) {
+ switch (value) {
+ case Integer.MIN_VALUE:
+ System.out.println(Integer.MIN_VALUE); break;
+ default:
+ System.out.println("default"); break;
+ }
+ }
+
+ // Long packed-switch that might lead to not creating chained-ifs.
+ public static void packedSwitch7(int value) {
+ switch (value) {
+ case 1:
+ System.out.println(1); break;
+ case 2:
+ System.out.println(2); break;
+ case 3:
+ System.out.println(3); break;
+ case 4:
+ System.out.println(4); break;
+ case 5:
+ System.out.println(5); break;
+ case 6:
+ System.out.println(6); break;
+ case 7:
+ System.out.println(7); break;
+ case 8:
+ System.out.println(8); break;
+ case 9:
+ System.out.println(9); break;
+ case 10:
+ System.out.println(10); break;
+ case 11:
+ System.out.println(11); break;
+ case 12:
+ System.out.println(12); break;
+ case 13:
+ System.out.println(13); break;
+ case 14:
+ System.out.println(14); break;
+ case 15:
+ System.out.println(15); break;
+ default:
+ System.out.println("default"); break;
+ }
+ }
+
+ // Sparse switch, just leave a gap.
+ public static void sparseSwitch(int value) {
+ switch (value) {
+ case 0:
+ System.out.println("0"); break;
+ case 1:
+ System.out.println("1"); break;
+ case 3:
+ System.out.println("3"); break;
+ case 4:
+ System.out.println("4"); break;
+ default:
+ System.out.println("default"); break;
+ }
+ }
+
+ // Simple sparse-switch starting at a negative index.
+ public static void sparseSwitch2(int value) {
+ switch (value) {
+ case -3:
+ System.out.println("-3"); break;
+ case -2:
+ System.out.println("-2"); break;
+ case -1:
+ System.out.println("-1"); break;
+ case 0:
+ System.out.println("0"); break;
+ case 2:
+ System.out.println("2"); break;
+ default:
+ System.out.println("default"); break;
+ }
+ }
+
+ // Simple sparse-switch starting above 0.
+ public static void sparseSwitch3(int value) {
+ switch (value) {
+ case 2:
+ System.out.println("2"); break;
+ case 4:
+ System.out.println("4"); break;
+ case 5:
+ System.out.println("5"); break;
+ case 6:
+ System.out.println("6"); break;
+ default:
+ System.out.println("default"); break;
+ }
+ }
+
+ // Simple sparse-switch going up to max_int.
+ public static void sparseSwitch4(int value) {
+ switch (value) {
+ case Integer.MAX_VALUE - 2:
+ System.out.println(Integer.MAX_VALUE - 2); break;
+ case Integer.MAX_VALUE:
+ System.out.println(Integer.MAX_VALUE); break;
+ default:
+ System.out.println("default"); break;
+ }
+ }
+
+ // Simple sparse-switch starting at min_int.
+ public static void sparseSwitch5(int value) {
+ switch (value) {
+ case Integer.MIN_VALUE:
+ System.out.println(Integer.MIN_VALUE); break;
+ case Integer.MIN_VALUE + 2:
+ System.out.println(Integer.MIN_VALUE + 2); break;
+ default:
+ System.out.println("default"); break;
+ }
+ }
+
+ // Long sparse-switch that might lead to not creating chained-ifs.
+ public static void sparseSwitch7(int value) {
+ switch (value) {
+ case 1:
+ System.out.println(1); break;
+ case 2:
+ System.out.println(2); break;
+ case 4:
+ System.out.println(4); break;
+ case 5:
+ System.out.println(5); break;
+ case 6:
+ System.out.println(6); break;
+ case 7:
+ System.out.println(7); break;
+ case 8:
+ System.out.println(8); break;
+ case 9:
+ System.out.println(9); break;
+ case 10:
+ System.out.println(10); break;
+ case 11:
+ System.out.println(11); break;
+ case 12:
+ System.out.println(12); break;
+ case 13:
+ System.out.println(13); break;
+ case 14:
+ System.out.println(14); break;
+ case 15:
+ System.out.println(15); break;
+ default:
+ System.out.println("default"); break;
+ }
+ }
+
public static void main(String args[]) {
+ /*
+ * Note: We are using for loops and calls to hopefully avoid simplifying the switch
+ * structure from constant propagation. When inlining is supported, this needs to
+ * be revisited.
+ */
+
+ System.out.println("packed");
+ for (int i = -2; i < 3; i++) {
+ packedSwitch(i);
+ }
+ packedSwitch(Integer.MIN_VALUE);
+ packedSwitch(Integer.MAX_VALUE);
+
+ System.out.println("packed2");
+ for (int i = -2; i < 3; i++) {
+ packedSwitch2(i);
+ }
+ packedSwitch2(Integer.MIN_VALUE);
+ packedSwitch2(Integer.MAX_VALUE);
+
+ System.out.println("packed3");
+ for (int i = -2; i < 7; i++) {
+ packedSwitch3(i);
+ }
+ packedSwitch3(Integer.MIN_VALUE);
+ packedSwitch3(Integer.MAX_VALUE);
+
+ System.out.println("packed4");
+ for (int i = Integer.MAX_VALUE - 2; i > 0; i++) {
+ packedSwitch4(i);
+ }
+ packedSwitch4(Integer.MIN_VALUE);
+
+ System.out.println("packed5");
+ for (int i = Integer.MIN_VALUE; i < Integer.MIN_VALUE + 2; i++) {
+ packedSwitch5(i);
+ }
+ packedSwitch5(Integer.MAX_VALUE);
+
+ System.out.println("packed6");
+ packedSwitch6(Integer.MIN_VALUE);
+ packedSwitch6(Integer.MAX_VALUE);
+
+ System.out.println("packed7");
+ for (int i = -1; i < 17; i++) {
+ packedSwitch7(i);
+ }
+
+
+ System.out.println("sparse");
+ for (int i = -2; i < 4; i++) {
+ sparseSwitch(i);
+ }
+ sparseSwitch(Integer.MIN_VALUE);
+ sparseSwitch(Integer.MAX_VALUE);
+
+ System.out.println("sparse2");
+ for (int i = -2; i < 3; i++) {
+ sparseSwitch2(i);
+ }
+ sparseSwitch2(Integer.MIN_VALUE);
+ sparseSwitch2(Integer.MAX_VALUE);
+
+ System.out.println("sparse3");
+ for (int i = -2; i < 7; i++) {
+ sparseSwitch3(i);
+ }
+ sparseSwitch3(Integer.MIN_VALUE);
+ sparseSwitch3(Integer.MAX_VALUE);
+
+ System.out.println("sparse4");
+ for (int i = Integer.MAX_VALUE - 2; i > 0; i++) {
+ sparseSwitch4(i);
+ }
+ sparseSwitch4(Integer.MIN_VALUE);
+
+ System.out.println("sparse5");
+ for (int i = Integer.MIN_VALUE; i < Integer.MIN_VALUE + 2; i++) {
+ sparseSwitch5(i);
+ }
+ sparseSwitch5(Integer.MAX_VALUE);
+
+ System.out.println("sparse7");
+ for (int i = -1; i < 17; i++) {
+ sparseSwitch7(i);
+ }
+
+ // Older tests.
+
int a = 1;
switch (a) {
diff --git a/test/074-gc-thrash/src/Main.java b/test/074-gc-thrash/src/Main.java
index 32fbf2d..238e73a 100644
--- a/test/074-gc-thrash/src/Main.java
+++ b/test/074-gc-thrash/src/Main.java
@@ -292,8 +292,8 @@
break;
}
- strong[depth] = funStr;
weak[depth] = new WeakReference(funStr);
+ strong[depth] = funStr;
if (depth+1 < MAX_DEPTH)
dive(depth+1, iteration+1);
else
diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java
index 56972ff..0e90c4d 100644
--- a/test/082-inline-execute/src/Main.java
+++ b/test/082-inline-execute/src/Main.java
@@ -34,6 +34,7 @@
test_Math_max_F();
test_Math_min_D();
test_Math_max_D();
+ test_Math_sqrt();
test_Math_ceil();
test_Math_floor();
test_Math_rint();
@@ -54,6 +55,7 @@
test_StrictMath_max_F();
test_StrictMath_min_D();
test_StrictMath_max_D();
+ test_StrictMath_sqrt();
test_StrictMath_ceil();
test_StrictMath_floor();
test_StrictMath_rint();
@@ -119,14 +121,29 @@
}
}
+ // Break up the charAt tests. The optimizing compiler doesn't optimize methods with try-catch yet,
+ // so we need to separate out the tests that are expected to throw exception
+
public static void test_String_charAt() {
- String testStr = "Now is the time";
+ String testStr = "Now is the time to test some stuff";
- Assert.assertEquals('N', testStr.charAt(0));
- Assert.assertEquals('o', testStr.charAt(1));
- Assert.assertEquals(' ', testStr.charAt(10));
- Assert.assertEquals('e', testStr.charAt(testStr.length()-1));
+ Assert.assertEquals(testStr.length() - 1, 33); // 33 = testStr.length()-1 as a constant.
+ Assert.assertEquals('f', testStr.charAt(33));
+ test_String_charAt(testStr, 'N', 'o', ' ', 'f');
+ test_String_charAt(testStr.substring(3,15), ' ', 'i', 'm', 'e');
+ }
+ public static void test_String_charAt(String testStr, char a, char b, char c, char d) {
+ Assert.assertEquals(a, testStr.charAt(0));
+ Assert.assertEquals(b, testStr.charAt(1));
+ Assert.assertEquals(c, testStr.charAt(10));
+ Assert.assertEquals(d, testStr.charAt(testStr.length()-1));
+
+ test_String_charAtExc(testStr);
+ test_String_charAtExc2(testStr);
+ }
+
+ private static void test_String_charAtExc(String testStr) {
try {
testStr.charAt(-1);
Assert.fail();
@@ -137,6 +154,44 @@
Assert.fail();
} catch (StringIndexOutOfBoundsException expected) {
}
+ try {
+ if (testStr.length() == 34) {
+ testStr.charAt(34); // 34 = "Now is the time to test some stuff".length()
+ } else {
+ Assert.assertEquals(testStr.length(), 12); // 12 = " is the time".length()
+ testStr.charAt(12);
+ }
+ Assert.fail();
+ } catch (StringIndexOutOfBoundsException expected) {
+ }
+ try {
+ test_String_charAt_inner(testStr, -1);
+ Assert.fail();
+ } catch (StringIndexOutOfBoundsException expected) {
+ }
+ try {
+ test_String_charAt_inner(testStr, 80);
+ Assert.fail();
+ } catch (StringIndexOutOfBoundsException expected) {
+ }
+ try {
+ if (testStr.length() == 34) {
+ // 34 = "Now is the time to test some stuff".length()
+ test_String_charAt_inner(testStr, 34);
+ } else {
+ Assert.assertEquals(testStr.length(), 12); // 12 = " is the time".length()
+ test_String_charAt_inner(testStr, 12);
+ }
+ Assert.fail();
+ } catch (StringIndexOutOfBoundsException expected) {
+ }
+
+ String strEmpty = "";
+ try {
+ strEmpty.charAt(0);
+ Assert.fail();
+ } catch (StringIndexOutOfBoundsException expected) {
+ }
String strNull = null;
try {
@@ -146,6 +201,32 @@
}
}
+ private static char test_String_charAt_inner(String s, int index) {
+ // Using non-constant index here (assuming that this method wasn't inlined).
+ return s.charAt(index);
+ }
+
+ private static void test_String_charAtExc2(String testStr) {
+ try {
+ test_String_charAtExc3(testStr);
+ Assert.fail();
+ } catch (StringIndexOutOfBoundsException expected) {
+ }
+ try {
+ test_String_charAtExc4(testStr);
+ Assert.fail();
+ } catch (StringIndexOutOfBoundsException expected) {
+ }
+ }
+
+ private static void test_String_charAtExc3(String testStr) {
+ Assert.assertEquals('N', testStr.charAt(-1));
+ }
+
+ private static void test_String_charAtExc4(String testStr) {
+ Assert.assertEquals('N', testStr.charAt(100));
+ }
+
static int start;
private static int[] negIndex = { -100000 };
public static void test_String_indexOf() {
@@ -276,6 +357,7 @@
}
public static void test_Math_abs_I() {
+ Math.abs(-1);
Assert.assertEquals(Math.abs(0), 0);
Assert.assertEquals(Math.abs(123), 123);
Assert.assertEquals(Math.abs(-123), 123);
@@ -286,15 +368,18 @@
}
public static void test_Math_abs_J() {
+ Math.abs(-1L);
Assert.assertEquals(Math.abs(0L), 0L);
Assert.assertEquals(Math.abs(123L), 123L);
Assert.assertEquals(Math.abs(-123L), 123L);
Assert.assertEquals(Math.abs(Long.MAX_VALUE), Long.MAX_VALUE);
Assert.assertEquals(Math.abs(Long.MIN_VALUE), Long.MIN_VALUE);
Assert.assertEquals(Math.abs(Long.MIN_VALUE - 1), Long.MAX_VALUE);
+ Assert.assertEquals(Math.abs(2147483648L), 2147483648L);
}
public static void test_Math_min_I() {
+ Math.min(1, 0);
Assert.assertEquals(Math.min(0, 0), 0);
Assert.assertEquals(Math.min(1, 0), 0);
Assert.assertEquals(Math.min(0, 1), 0);
@@ -304,6 +389,7 @@
}
public static void test_Math_max_I() {
+ Math.max(1, 0);
Assert.assertEquals(Math.max(0, 0), 0);
Assert.assertEquals(Math.max(1, 0), 1);
Assert.assertEquals(Math.max(0, 1), 1);
@@ -313,6 +399,7 @@
}
public static void test_Math_min_J() {
+ Math.min(1L, 0L);
Assert.assertEquals(Math.min(0L, 0L), 0L);
Assert.assertEquals(Math.min(1L, 0L), 0L);
Assert.assertEquals(Math.min(0L, 1L), 0L);
@@ -322,6 +409,7 @@
}
public static void test_Math_max_J() {
+ Math.max(1L, 0L);
Assert.assertEquals(Math.max(0L, 0L), 0L);
Assert.assertEquals(Math.max(1L, 0L), 1L);
Assert.assertEquals(Math.max(0L, 1L), 1L);
@@ -331,6 +419,7 @@
}
public static void test_Math_min_F() {
+ Math.min(1.0f, Float.NaN);
Assert.assertTrue(Float.isNaN(Math.min(1.0f, Float.NaN)));
Assert.assertTrue(Float.isNaN(Math.min(Float.NaN, 1.0f)));
Assert.assertEquals(Math.min(-0.0f, 0.0f), -0.0f);
@@ -345,6 +434,7 @@
}
public static void test_Math_max_F() {
+ Math.max(1.0f, Float.NaN);
Assert.assertTrue(Float.isNaN(Math.max(1.0f, Float.NaN)));
Assert.assertTrue(Float.isNaN(Math.max(Float.NaN, 1.0f)));
Assert.assertEquals(Math.max(-0.0f, 0.0f), 0.0f);
@@ -359,6 +449,7 @@
}
public static void test_Math_min_D() {
+ Math.min(1.0d, Double.NaN);
Assert.assertTrue(Double.isNaN(Math.min(1.0d, Double.NaN)));
Assert.assertTrue(Double.isNaN(Math.min(Double.NaN, 1.0d)));
Assert.assertEquals(Math.min(-0.0d, 0.0d), -0.0d);
@@ -373,6 +464,7 @@
}
public static void test_Math_max_D() {
+ Math.max(1.0d, Double.NaN);
Assert.assertTrue(Double.isNaN(Math.max(1.0d, Double.NaN)));
Assert.assertTrue(Double.isNaN(Math.max(Double.NaN, 1.0d)));
Assert.assertEquals(Math.max(-0.0d, 0.0d), 0.0d);
@@ -386,7 +478,15 @@
Assert.assertEquals(Math.max(Double.MIN_VALUE, Double.MAX_VALUE), Double.MAX_VALUE);
}
+ public static void test_Math_sqrt() {
+ Math.sqrt(+4.0);
+ Assert.assertEquals(Math.sqrt(+4.0), +2.0d, 0.0);
+ Assert.assertEquals(Math.sqrt(+49.0), +7.0d, 0.0);
+ Assert.assertEquals(Math.sqrt(+1.44), +1.2d, 0.0);
+ }
+
public static void test_Math_ceil() {
+ Math.ceil(-0.9);
Assert.assertEquals(Math.ceil(+0.0), +0.0d, 0.0);
Assert.assertEquals(Math.ceil(-0.0), -0.0d, 0.0);
Assert.assertEquals(Math.ceil(-0.9), -0.0d, 0.0);
@@ -408,6 +508,7 @@
}
public static void test_Math_floor() {
+ Math.floor(+2.1);
Assert.assertEquals(Math.floor(+0.0), +0.0d, 0.0);
Assert.assertEquals(Math.floor(-0.0), -0.0d, 0.0);
Assert.assertEquals(Math.floor(+2.0), +2.0d, 0.0);
@@ -426,6 +527,7 @@
}
public static void test_Math_rint() {
+ Math.rint(+2.1);
Assert.assertEquals(Math.rint(+0.0), +0.0d, 0.0);
Assert.assertEquals(Math.rint(-0.0), -0.0d, 0.0);
Assert.assertEquals(Math.rint(+2.0), +2.0d, 0.0);
@@ -444,6 +546,7 @@
}
public static void test_Math_round_D() {
+ Math.round(2.1d);
Assert.assertEquals(Math.round(+0.0d), (long)+0.0);
Assert.assertEquals(Math.round(-0.0d), (long)+0.0);
Assert.assertEquals(Math.round(2.0d), 2l);
@@ -465,6 +568,7 @@
}
public static void test_Math_round_F() {
+ Math.round(2.1f);
Assert.assertEquals(Math.round(+0.0f), (int)+0.0);
Assert.assertEquals(Math.round(-0.0f), (int)+0.0);
Assert.assertEquals(Math.round(2.0f), 2);
@@ -485,6 +589,7 @@
}
public static void test_StrictMath_abs_I() {
+ StrictMath.abs(-1);
Assert.assertEquals(StrictMath.abs(0), 0);
Assert.assertEquals(StrictMath.abs(123), 123);
Assert.assertEquals(StrictMath.abs(-123), 123);
@@ -495,6 +600,7 @@
}
public static void test_StrictMath_abs_J() {
+ StrictMath.abs(-1L);
Assert.assertEquals(StrictMath.abs(0L), 0L);
Assert.assertEquals(StrictMath.abs(123L), 123L);
Assert.assertEquals(StrictMath.abs(-123L), 123L);
@@ -504,6 +610,7 @@
}
public static void test_StrictMath_min_I() {
+ StrictMath.min(1, 0);
Assert.assertEquals(StrictMath.min(0, 0), 0);
Assert.assertEquals(StrictMath.min(1, 0), 0);
Assert.assertEquals(StrictMath.min(0, 1), 0);
@@ -513,6 +620,7 @@
}
public static void test_StrictMath_max_I() {
+ StrictMath.max(1, 0);
Assert.assertEquals(StrictMath.max(0, 0), 0);
Assert.assertEquals(StrictMath.max(1, 0), 1);
Assert.assertEquals(StrictMath.max(0, 1), 1);
@@ -522,6 +630,7 @@
}
public static void test_StrictMath_min_J() {
+ StrictMath.min(1L, 0L);
Assert.assertEquals(StrictMath.min(0L, 0L), 0L);
Assert.assertEquals(StrictMath.min(1L, 0L), 0L);
Assert.assertEquals(StrictMath.min(0L, 1L), 0L);
@@ -531,6 +640,7 @@
}
public static void test_StrictMath_max_J() {
+ StrictMath.max(1L, 0L);
Assert.assertEquals(StrictMath.max(0L, 0L), 0L);
Assert.assertEquals(StrictMath.max(1L, 0L), 1L);
Assert.assertEquals(StrictMath.max(0L, 1L), 1L);
@@ -540,6 +650,7 @@
}
public static void test_StrictMath_min_F() {
+ StrictMath.min(1.0f, Float.NaN);
Assert.assertTrue(Float.isNaN(StrictMath.min(1.0f, Float.NaN)));
Assert.assertTrue(Float.isNaN(StrictMath.min(Float.NaN, 1.0f)));
Assert.assertEquals(StrictMath.min(-0.0f, 0.0f), -0.0f);
@@ -554,6 +665,7 @@
}
public static void test_StrictMath_max_F() {
+ StrictMath.max(1.0f, Float.NaN);
Assert.assertTrue(Float.isNaN(StrictMath.max(1.0f, Float.NaN)));
Assert.assertTrue(Float.isNaN(StrictMath.max(Float.NaN, 1.0f)));
Assert.assertEquals(StrictMath.max(-0.0f, 0.0f), 0.0f);
@@ -568,6 +680,7 @@
}
public static void test_StrictMath_min_D() {
+ StrictMath.min(1.0d, Double.NaN);
Assert.assertTrue(Double.isNaN(StrictMath.min(1.0d, Double.NaN)));
Assert.assertTrue(Double.isNaN(StrictMath.min(Double.NaN, 1.0d)));
Assert.assertEquals(StrictMath.min(-0.0d, 0.0d), -0.0d);
@@ -582,6 +695,7 @@
}
public static void test_StrictMath_max_D() {
+ StrictMath.max(1.0d, Double.NaN);
Assert.assertTrue(Double.isNaN(StrictMath.max(1.0d, Double.NaN)));
Assert.assertTrue(Double.isNaN(StrictMath.max(Double.NaN, 1.0d)));
Assert.assertEquals(StrictMath.max(-0.0d, 0.0d), 0.0d);
@@ -595,7 +709,15 @@
Assert.assertEquals(StrictMath.max(Double.MIN_VALUE, Double.MAX_VALUE), Double.MAX_VALUE);
}
+ public static void test_StrictMath_sqrt() {
+ StrictMath.sqrt(+4.0);
+ Assert.assertEquals(StrictMath.sqrt(+4.0), +2.0d, 0.0);
+ Assert.assertEquals(StrictMath.sqrt(+49.0), +7.0d, 0.0);
+ Assert.assertEquals(StrictMath.sqrt(+1.44), +1.2d, 0.0);
+ }
+
public static void test_StrictMath_ceil() {
+ StrictMath.ceil(-0.9);
Assert.assertEquals(StrictMath.ceil(+0.0), +0.0d, 0.0);
Assert.assertEquals(StrictMath.ceil(-0.0), -0.0d, 0.0);
Assert.assertEquals(StrictMath.ceil(-0.9), -0.0d, 0.0);
@@ -617,6 +739,7 @@
}
public static void test_StrictMath_floor() {
+ StrictMath.floor(+2.1);
Assert.assertEquals(StrictMath.floor(+0.0), +0.0d, 0.0);
Assert.assertEquals(StrictMath.floor(-0.0), -0.0d, 0.0);
Assert.assertEquals(StrictMath.floor(+2.0), +2.0d, 0.0);
@@ -635,6 +758,7 @@
}
public static void test_StrictMath_rint() {
+ StrictMath.rint(+2.1);
Assert.assertEquals(StrictMath.rint(+0.0), +0.0d, 0.0);
Assert.assertEquals(StrictMath.rint(-0.0), -0.0d, 0.0);
Assert.assertEquals(StrictMath.rint(+2.0), +2.0d, 0.0);
@@ -653,6 +777,7 @@
}
public static void test_StrictMath_round_D() {
+ StrictMath.round(2.1d);
Assert.assertEquals(StrictMath.round(+0.0d), (long)+0.0);
Assert.assertEquals(StrictMath.round(-0.0d), (long)+0.0);
Assert.assertEquals(StrictMath.round(2.0d), 2l);
@@ -674,6 +799,7 @@
}
public static void test_StrictMath_round_F() {
+ StrictMath.round(2.1f);
Assert.assertEquals(StrictMath.round(+0.0f), (int)+0.0);
Assert.assertEquals(StrictMath.round(-0.0f), (int)+0.0);
Assert.assertEquals(StrictMath.round(2.0f), 2);
@@ -694,6 +820,7 @@
}
public static void test_Float_floatToRawIntBits() {
+ Float.floatToRawIntBits(-1.0f);
Assert.assertEquals(Float.floatToRawIntBits(-1.0f), 0xbf800000);
Assert.assertEquals(Float.floatToRawIntBits(0.0f), 0);
Assert.assertEquals(Float.floatToRawIntBits(1.0f), 0x3f800000);
@@ -703,6 +830,7 @@
}
public static void test_Float_intBitsToFloat() {
+ Float.intBitsToFloat(0xbf800000);
Assert.assertEquals(Float.intBitsToFloat(0xbf800000), -1.0f);
Assert.assertEquals(Float.intBitsToFloat(0x00000000), 0.0f);
Assert.assertEquals(Float.intBitsToFloat(0x3f800000), 1.0f);
@@ -712,6 +840,7 @@
}
public static void test_Double_doubleToRawLongBits() {
+ Double.doubleToRawLongBits(-1.0);
Assert.assertEquals(Double.doubleToRawLongBits(-1.0), 0xbff0000000000000L);
Assert.assertEquals(Double.doubleToRawLongBits(0.0), 0x0000000000000000L);
Assert.assertEquals(Double.doubleToRawLongBits(1.0), 0x3ff0000000000000L);
@@ -721,6 +850,7 @@
}
public static void test_Double_longBitsToDouble() {
+ Double.longBitsToDouble(0xbff0000000000000L);
Assert.assertEquals(Double.longBitsToDouble(0xbff0000000000000L), -1.0);
Assert.assertEquals(Double.longBitsToDouble(0x0000000000000000L), 0.0);
Assert.assertEquals(Double.longBitsToDouble(0x3ff0000000000000L), 1.0);
@@ -730,6 +860,7 @@
}
public static void test_Short_reverseBytes() {
+ Short.reverseBytes((short)0x1357);
Assert.assertEquals(Short.reverseBytes((short)0x0000), (short)0x0000);
Assert.assertEquals(Short.reverseBytes((short)0xffff), (short)0xffff);
Assert.assertEquals(Short.reverseBytes((short)0x8000), (short)0x0080);
@@ -741,6 +872,7 @@
}
public static void test_Integer_reverseBytes() {
+ Integer.reverseBytes(0x13579bdf);
Assert.assertEquals(Integer.reverseBytes(0x00000000), 0x00000000);
Assert.assertEquals(Integer.reverseBytes(0xffffffff), 0xffffffff);
Assert.assertEquals(Integer.reverseBytes(0x80000000), 0x00000080);
@@ -750,6 +882,7 @@
}
public static void test_Long_reverseBytes() {
+ Long.reverseBytes(0x13579bdf2468ace0L);
Assert.assertEquals(Long.reverseBytes(0x0000000000000000L), 0x0000000000000000L);
Assert.assertEquals(Long.reverseBytes(0xffffffffffffffffL), 0xffffffffffffffffL);
Assert.assertEquals(Long.reverseBytes(0x8000000000000000L), 0x0000000000000080L);
@@ -758,6 +891,7 @@
}
public static void test_Integer_reverse() {
+ Integer.reverse(0x12345678);
Assert.assertEquals(Integer.reverse(1), 0x80000000);
Assert.assertEquals(Integer.reverse(-1), 0xffffffff);
Assert.assertEquals(Integer.reverse(0), 0);
@@ -768,6 +902,7 @@
}
public static void test_Long_reverse() {
+ Long.reverse(0x1234567812345678L);
Assert.assertEquals(Long.reverse(1L), 0x8000000000000000L);
Assert.assertEquals(Long.reverse(-1L), 0xffffffffffffffffL);
Assert.assertEquals(Long.reverse(0L), 0L);
@@ -822,6 +957,7 @@
b[1] = 0x12;
b[2] = 0x11;
long address = (long)address_of.invoke(runtime, b);
+ peek_short.invoke(null, address, false);
Assert.assertEquals((short)peek_short.invoke(null, address, false), 0x1213); // Aligned read
Assert.assertEquals((short)peek_short.invoke(null, address + 1, false), 0x1112); // Unaligned read
}
@@ -834,6 +970,7 @@
b[3] = 0x12;
b[4] = 0x11;
long address = (long)address_of.invoke(runtime, b);
+ peek_int.invoke(null, address, false);
Assert.assertEquals((int)peek_int.invoke(null, address, false), 0x12131415);
Assert.assertEquals((int)peek_int.invoke(null, address + 1, false), 0x11121314);
}
@@ -850,6 +987,7 @@
b[7] = 0x12;
b[8] = 0x11;
long address = (long)address_of.invoke(runtime, b);
+ peek_long.invoke(null, address, false);
Assert.assertEquals((long)peek_long.invoke(null, address, false), 0x1213141516171819L);
Assert.assertEquals((long)peek_long.invoke(null, address + 1, false), 0x1112131415161718L);
}
diff --git a/test/099-vmdebug/src/Main.java b/test/099-vmdebug/src/Main.java
index 7f24b1b..4d781c3 100644
--- a/test/099-vmdebug/src/Main.java
+++ b/test/099-vmdebug/src/Main.java
@@ -28,14 +28,22 @@
testMethodTracing();
}
- private static void testMethodTracing() throws Exception {
- File tempFile;
+ private static File createTempFile() throws Exception {
try {
- tempFile = File.createTempFile("test", ".trace");
+ return File.createTempFile("test", ".trace");
} catch (IOException e) {
- System.setProperty("java.io.tmpdir", "/sdcard");
- tempFile = File.createTempFile("test", ".trace");
+ System.setProperty("java.io.tmpdir", "/data/local/tmp");
+ try {
+ return File.createTempFile("test", ".trace");
+ } catch (IOException e2) {
+ System.setProperty("java.io.tmpdir", "/sdcard");
+ return File.createTempFile("test", ".trace");
+ }
}
+ }
+
+ private static void testMethodTracing() throws Exception {
+ File tempFile = createTempFile();
tempFile.deleteOnExit();
String tempFileName = tempFile.getPath();
diff --git a/test/109-suspend-check/src/Main.java b/test/109-suspend-check/src/Main.java
index ae10576..cd5130d 100644
--- a/test/109-suspend-check/src/Main.java
+++ b/test/109-suspend-check/src/Main.java
@@ -21,10 +21,15 @@
System.out.println("Running (" + TEST_TIME + " seconds) ...");
InfiniteForLoop forLoop = new InfiniteForLoop();
InfiniteWhileLoop whileLoop = new InfiniteWhileLoop();
+ InfiniteWhileLoopWithIntrinsic whileLoopWithIntrinsic =
+ new InfiniteWhileLoopWithIntrinsic();
+ InfiniteDoWhileLoopWithLong doWhileLoopWithLong = new InfiniteDoWhileLoopWithLong();
InfiniteDoWhileLoop doWhileLoop = new InfiniteDoWhileLoop();
MakeGarbage garbage = new MakeGarbage();
forLoop.start();
whileLoop.start();
+ whileLoopWithIntrinsic.start();
+ doWhileLoopWithLong.start();
doWhileLoop.start();
garbage.start();
for (int i = 0; i < TEST_TIME; i++) {
@@ -34,6 +39,8 @@
}
forLoop.stopNow();
whileLoop.stopNow();
+ whileLoopWithIntrinsic.stopNow();
+ doWhileLoopWithLong.stopNow();
doWhileLoop.stopNow();
garbage.stopNow();
System.out.println("Done.");
@@ -48,6 +55,35 @@
}
}
+class InfiniteWhileLoopWithIntrinsic extends Thread {
+ volatile private boolean keepGoing = true;
+ private String[] strings = { "a", "b", "c", "d" };
+ private int sum = 0;
+ public void run() {
+ int i = 0;
+ while (keepGoing) {
+ i++;
+ sum += strings[i & 3].length();
+ }
+ }
+ public void stopNow() {
+ keepGoing = false;
+ }
+}
+
+class InfiniteDoWhileLoopWithLong extends Thread {
+ volatile private long keepGoing = 7L;
+ public void run() {
+ int i = 0;
+ do {
+ i++;
+ } while (keepGoing >= 4L);
+ }
+ public void stopNow() {
+ keepGoing = 1L;
+ }
+}
+
class InfiniteWhileLoop extends Thread {
volatile private boolean keepGoing = true;
public void run() {
diff --git a/test/114-ParallelGC/src/Main.java b/test/114-ParallelGC/src/Main.java
index 8e2519d..46029cf 100644
--- a/test/114-ParallelGC/src/Main.java
+++ b/test/114-ParallelGC/src/Main.java
@@ -16,51 +16,36 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.CyclicBarrier;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
public class Main implements Runnable {
- public final static long TIMEOUT_VALUE = 5; // Timeout in minutes.
- public final static long MAX_SIZE = 1000; // Maximum size of array-list to allocate.
+ // Timeout in minutes. Make it larger than the run-test timeout to get a native thread dump by
+ // ART on timeout when running on the host.
+ private final static long TIMEOUT_VALUE = 7;
+
+ private final static long MAX_SIZE = 1000; // Maximum size of array-list to allocate.
+
+ private final static int THREAD_COUNT = 16;
+
+ // Use a couple of different forms of synchronizing to test some of these...
+ private final static AtomicInteger counter = new AtomicInteger();
+ private final static Object gate = new Object();
+ private volatile static int waitCount = 0;
public static void main(String[] args) throws Exception {
- Thread[] threads = new Thread[16];
+ Thread[] threads = new Thread[THREAD_COUNT];
- // Use a cyclic system of synchronous queues to pass a boolean token around.
- //
- // The combinations are:
- //
- // Worker receives: true false false true
- // Worker has OOM: false false true true
- // |
- // v
- // Value to pass: true false false false
- // Exit out of loop: false true true true
- // Wait on in queue: true false false true
- //
- // Finally, the workers are supposed to wait on the barrier to synchronize the GC run.
-
- CyclicBarrier barrier = new CyclicBarrier(threads.length);
- List<SynchronousQueue<Boolean>> queues = new ArrayList<SynchronousQueue<Boolean>>(
- threads.length);
- for (int i = 0; i < threads.length; i++) {
- queues.add(new SynchronousQueue<Boolean>());
- }
+ // This barrier is used to synchronize the threads starting to allocate.
+ // Note: Even though a barrier is not allocation-free, this one is fine, as it will be used
+ // before filling the heap.
+ CyclicBarrier startBarrier = new CyclicBarrier(threads.length);
for (int i = 0; i < threads.length; i++) {
- threads[i] = new Thread(new Main(i, queues.get(i), queues.get((i + 1) % threads.length),
- barrier));
+ threads[i] = new Thread(new Main(startBarrier));
+ threads[i].start();
}
- for (Thread thread : threads) {
- thread.start();
- }
-
- // Push off the cycle.
- checkTimeout(queues.get(0).offer(Boolean.TRUE, TIMEOUT_VALUE, TimeUnit.MINUTES));
// Wait for the threads to finish.
for (Thread thread : threads) {
@@ -68,86 +53,89 @@
}
// Allocate objects to definitely run GC before quitting.
+ ArrayList<Object> l = new ArrayList<Object>();
try {
- for (int i = 0; i < 1000; i++) {
- new ArrayList<Object>(i);
+ for (int i = 0; i < 100000; i++) {
+ l.add(new ArrayList<Object>(i));
}
} catch (OutOfMemoryError oom) {
}
+ // Make the (outer) ArrayList unreachable. Note it may still
+ // be reachable under an interpreter or a compiler without a
+ // liveness analysis.
+ l = null;
+ new ArrayList<Object>(50);
}
- private static void checkTimeout(Object o) {
- checkTimeout(o != null);
+ private Main(CyclicBarrier startBarrier) {
+ this.startBarrier = startBarrier;
}
- private static void checkTimeout(boolean b) {
- if (!b) {
- // Something went wrong.
- System.out.println("Bad things happened, timeout.");
- System.exit(1);
- }
- }
-
- private final int id;
- private final SynchronousQueue<Boolean> waitOn;
- private final SynchronousQueue<Boolean> pushTo;
- private final CyclicBarrier finalBarrier;
-
- private Main(int id, SynchronousQueue<Boolean> waitOn, SynchronousQueue<Boolean> pushTo,
- CyclicBarrier finalBarrier) {
- this.id = id;
- this.waitOn = waitOn;
- this.pushTo = pushTo;
- this.finalBarrier = finalBarrier;
- }
+ private ArrayList<Object> store;
+ private CyclicBarrier startBarrier;
public void run() {
try {
work();
- } catch (Exception exc) {
- // Any exception is bad.
- exc.printStackTrace(System.err);
+ } catch (Throwable t) {
+ // Any exception or error getting here is bad.
+ try {
+ // May need allocations...
+ t.printStackTrace(System.err);
+ } catch (Throwable tInner) {
+ }
System.exit(1);
}
}
- public void work() throws BrokenBarrierException, InterruptedException, TimeoutException {
+ private void work() throws Exception {
+ // Any exceptions except an OOME in the allocation loop are bad and handed off to the
+ // caller which should abort the whole runtime.
+
ArrayList<Object> l = new ArrayList<Object>();
+ store = l; // Keep it alive.
- // Main loop.
- for (int i = 0; ; i++) {
- Boolean receivedB = waitOn.poll(TIMEOUT_VALUE, TimeUnit.MINUTES);
- checkTimeout(receivedB);
- boolean received = receivedB;
+ // Wait for the start signal.
+ startBarrier.await(TIMEOUT_VALUE, java.util.concurrent.TimeUnit.MINUTES);
- // This is the first stage, try to allocate up till MAX_SIZE.
- boolean oom = i >= MAX_SIZE;
- try {
- l.add(new ArrayList<Object>(i));
- } catch (OutOfMemoryError oome) {
- oom = true;
- }
-
- if (!received || oom) {
- // First stage, always push false.
- checkTimeout(pushTo.offer(Boolean.FALSE, TIMEOUT_VALUE, TimeUnit.MINUTES));
-
- // If we received true, wait for the false to come around.
- if (received) {
- checkTimeout(waitOn.poll(TIMEOUT_VALUE, TimeUnit.MINUTES));
+ // Allocate.
+ try {
+ for (int i = 0; i < MAX_SIZE; i++) {
+ l.add(new ArrayList<Object>(i));
}
-
- // Break out of the loop.
- break;
- } else {
- // Pass on true.
- checkTimeout(pushTo.offer(Boolean.TRUE, TIMEOUT_VALUE, TimeUnit.MINUTES));
- }
+ } catch (OutOfMemoryError oome) {
+ // Fine, we're done.
}
- // We have reached the final point. Wait on the barrier, but at most a minute.
- finalBarrier.await(TIMEOUT_VALUE, TimeUnit.MINUTES);
+ // Atomically increment the counter and check whether we were last.
+ int number = counter.incrementAndGet();
- // Done.
+ if (number < THREAD_COUNT) {
+ // Not last.
+ synchronized (gate) {
+ // Increment the wait counter.
+ waitCount++;
+ gate.wait(TIMEOUT_VALUE * 1000 * 60);
+ }
+ } else {
+ // Last. Wait until waitCount == THREAD_COUNT - 1.
+ for (int loops = 0; ; loops++) {
+ synchronized (gate) {
+ if (waitCount == THREAD_COUNT - 1) {
+ // OK, everyone's waiting. Notify and break out.
+ gate.notifyAll();
+ break;
+ } else if (loops > 40) {
+ // 1s wait, too many tries.
+ System.out.println("Waited too long for the last thread.");
+ System.exit(1);
+ }
+ }
+ // Wait a bit.
+ Thread.sleep(25);
+ }
+ }
+
+ store = null; // Allow GC to reclaim it.
}
}
diff --git a/test/116-nodex2oat/run b/test/116-nodex2oat/run
index 2df6705..9e5c7dd 100755
--- a/test/116-nodex2oat/run
+++ b/test/116-nodex2oat/run
@@ -14,11 +14,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Remove prebuild from the flags, this test is for testing not having oat files.
-flags="${@/--prebuild/}"
+flags="${@}"
-# Use the non-prebuild script.
-RUN="${RUN/push-and-run-prebuilt-test-jar/push-and-run-test-jar}"
+# This test is supposed to test without oat files, so doesn't work for prebuild. Make sure that
+# flag isn't set, or complain.
+# Note: prebuild is the default.
+if [[ "${flags}" == *--prebuild* || "${flags}" != *--no-prebuild* ]] ; then
+ echo "Test 116-nodex2oat cannot run in prebuild mode."
+ exit 1
+fi
# Make sure we can run without an oat file,
echo "Run -Xnodex2oat"
diff --git a/test/117-nopatchoat/run b/test/117-nopatchoat/run
index a7c96a0..c749c74 100755
--- a/test/117-nopatchoat/run
+++ b/test/117-nopatchoat/run
@@ -16,10 +16,23 @@
# ensure flags includes prebuild and relocate. It doesn't make sense unless we
# have a oat file we want to relocate.
-# TODO Unfortunately we have no way to force prebuild on for both host and target (or skip if not on).
-flags="${@/--relocate/}"
-flags="${flags/--no-relocate/}"
-flags="${flags} --relocate"
+flags="$@"
+
+# This test is supposed to test with oat files. Make sure that the no-prebuild flag isn't set,
+# or complain.
+# Note: prebuild is the default.
+if [[ "${flags}" == *--no-prebuild* ]] ; then
+ echo "Test 117-nopatchoat is not intended to run in no-prebuild mode."
+ exit 1
+fi
+
+# This test is supposed to test relocation. Make sure that the no-relocate flag isn't set,
+# or complain.
+# Note: relocate is the default.
+if [[ "${flags}" == *--no-relocate* ]] ; then
+ echo "Test 117-nopatchoat is not intended to run in no-relocate mode."
+ exit 1
+fi
# Make sure we can run without relocation
echo "Run without dex2oat/patchoat"
diff --git a/test/118-noimage-dex2oat/expected.txt b/test/118-noimage-dex2oat/expected.txt
index 6825fae..bcb695d 100644
--- a/test/118-noimage-dex2oat/expected.txt
+++ b/test/118-noimage-dex2oat/expected.txt
@@ -1,6 +1,9 @@
Run -Xnoimage-dex2oat
Has image is false, is image dex2oat enabled is false, is BOOTCLASSPATH on disk is false.
+testB18485243 PASS
Run -Ximage-dex2oat
Has image is true, is image dex2oat enabled is true, is BOOTCLASSPATH on disk is true.
+testB18485243 PASS
Run default
Has image is true, is image dex2oat enabled is true, is BOOTCLASSPATH on disk is true.
+testB18485243 PASS
diff --git a/test/118-noimage-dex2oat/run b/test/118-noimage-dex2oat/run
index 92a4ec2..2037797 100644
--- a/test/118-noimage-dex2oat/run
+++ b/test/118-noimage-dex2oat/run
@@ -14,17 +14,26 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+flags="$@"
+
+# This test is supposed to test without oat files, so doesn't work for prebuild. Make sure that
+# flag isn't set, or complain.
+# Note: prebuild is the default.
+if [[ "${flags}" == *--prebuild* || "${flags}" != *--no-prebuild* ]] ; then
+ echo "Test 118-noimage-dex2oat cannot run in prebuild mode."
+ exit 1
+fi
+
# Force relocation otherwise we will just use the already created core.oat/art pair.
-flags="${@/--no-relocate/--relocate}"
+# Note: relocate is the default.
+if [[ "${flags}" == *--no-relocate* ]] ; then
+ echo "Test 118-noimage-dex2oat is not intended to run in no-relocate mode."
+ exit 1
+fi
-# Use the non-prebuild script.
-RUN="${RUN/push-and-run-prebuilt-test-jar/push-and-run-test-jar}"
-
-if [ $(basename $RUN) == 'host-run-test-jar' ]; then
+if [[ $@ == *--host* ]]; then
framework="${ANDROID_HOST_OUT}/framework"
bpath_suffix="-hostdex"
- # Remove prebuild from the flags, this test is for testing not having oat files.
- flags="${flags/--prebuild/}"
else
framework="/system/framework"
bpath_suffix=""
diff --git a/test/118-noimage-dex2oat/smali/b_18485243.smali b/test/118-noimage-dex2oat/smali/b_18485243.smali
new file mode 100644
index 0000000..41fbc74
--- /dev/null
+++ b/test/118-noimage-dex2oat/smali/b_18485243.smali
@@ -0,0 +1,22 @@
+.class public LB18485243;
+.super Ljava/lang/Object;
+.source "b_18485243.smali"
+
+.method public constructor <init>()V
+ .registers 2
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method private static toInt()I
+ .registers 1
+ const v0, 0
+ return v0
+.end method
+
+.method public run()I
+ .registers 3
+ invoke-direct {p0}, LB18485243;->toInt()I
+ move-result v0
+ return v0
+.end method
diff --git a/test/118-noimage-dex2oat/src/Main.java b/test/118-noimage-dex2oat/src/Main.java
index c83b84d..9bf5bb3 100644
--- a/test/118-noimage-dex2oat/src/Main.java
+++ b/test/118-noimage-dex2oat/src/Main.java
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Main {
@@ -36,6 +37,8 @@
} else if (!hasImage && isBootClassPathOnDisk) {
throw new Error("Image with dex2oat enabled runs without an image file");
}
+
+ testB18485243();
}
static {
@@ -67,4 +70,19 @@
return (boolean) isBootClassPathOnDiskMethod.invoke(null, instructionSet);
}
}
+
+ private static void testB18485243() throws Exception {
+ Class<?> k = Class.forName("B18485243");
+ Object o = k.newInstance();
+ Method m = k.getDeclaredMethod("run");
+ try {
+ m.invoke(o);
+ } catch (InvocationTargetException e) {
+ Throwable actual = e.getTargetException();
+ if (!(actual instanceof IncompatibleClassChangeError)) {
+ throw new AssertionError("Expected IncompatibleClassChangeError", actual);
+ }
+ }
+ System.out.println("testB18485243 PASS");
+ }
}
diff --git a/test/119-noimage-patchoat/run b/test/119-noimage-patchoat/run
index 745b0c9..c409cbb 100644
--- a/test/119-noimage-patchoat/run
+++ b/test/119-noimage-patchoat/run
@@ -14,10 +14,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Force relocation otherwise we will just use the already created core.oat/art pair.
-flags="${@/--no-relocate/--relocate}"
+flags="$@"
-if [ $(basename $RUN) == 'host-run-test-jar' ]; then
+# Force relocation otherwise we will just use the already created core.oat/art pair.
+# Note: relocate is the default.
+if [[ "${flags}" == *--no-relocate* ]] ; then
+ echo "Test 119-noimage-patchoat is not intended to run in no-relocate mode."
+ exit 1
+fi
+
+if [[ $@ == *--host* ]]; then
false_bin="/bin/false"
else
false_bin="/system/bin/false"
diff --git a/test/122-npe/src/Main.java b/test/122-npe/src/Main.java
index 2fdcb9c..8f68205 100644
--- a/test/122-npe/src/Main.java
+++ b/test/122-npe/src/Main.java
@@ -191,6 +191,132 @@
check(npe, thisLine += 7);
try {
+ ((Value) null).volatileObjectField.toString();
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileObjectField = "Fisk";
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((Value) null).volatileIntField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileIntField = 42;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useFloat(((Value) null).volatileFloatField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileFloatField = 42.0F;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useLong(((Value) null).volatileLongField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileLongField = 42L;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useDouble(((Value) null).volatileDoubleField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileDoubleField = 42.0d;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((Value) null).volatileByteField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileByteField = 42;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ if (((Value) null).volatileBooleanField) { }
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileBooleanField = true;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((Value) null).volatileCharField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileCharField = '\u0042';
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((Value) null).volatileShortField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).volatileShortField = 42;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
((Object[]) null)[0].toString();
} catch (NullPointerException e) {
npe = e;
@@ -477,11 +603,22 @@
static class Value {
Object objectField;
int intField;
- float floatField; long longField;
+ float floatField;
+ long longField;
double doubleField;
byte byteField;
boolean booleanField;
char charField;
short shortField;
+
+ volatile Object volatileObjectField;
+ volatile int volatileIntField;
+ volatile float volatileFloatField;
+ volatile long volatileLongField;
+ volatile double volatileDoubleField;
+ volatile byte volatileByteField;
+ volatile boolean volatileBooleanField;
+ volatile char volatileCharField;
+ volatile short volatileShortField;
}
}
diff --git a/test/129-ThreadGetId/expected.txt b/test/129-ThreadGetId/expected.txt
new file mode 100644
index 0000000..134d8d0
--- /dev/null
+++ b/test/129-ThreadGetId/expected.txt
@@ -0,0 +1 @@
+Finishing
diff --git a/test/129-ThreadGetId/info.txt b/test/129-ThreadGetId/info.txt
new file mode 100644
index 0000000..443062d
--- /dev/null
+++ b/test/129-ThreadGetId/info.txt
@@ -0,0 +1 @@
+Regression test for b/18661622
diff --git a/test/129-ThreadGetId/src/Main.java b/test/129-ThreadGetId/src/Main.java
new file mode 100644
index 0000000..9934bba
--- /dev/null
+++ b/test/129-ThreadGetId/src/Main.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Map;
+
+public class Main implements Runnable {
+ static final int numberOfThreads = 5;
+ static final int totalOperations = 1000;
+
+ public static void main(String[] args) throws Exception {
+ final Thread[] threads = new Thread[numberOfThreads];
+ for (int t = 0; t < threads.length; t++) {
+ threads[t] = new Thread(new Main());
+ threads[t].start();
+ }
+ for (Thread t : threads) {
+ t.join();
+ }
+ System.out.println("Finishing");
+ }
+
+ public void test_getId() {
+ if (Thread.currentThread().getId() <= 0) {
+ System.out.println("current thread's ID is not positive");
+ }
+ // Check all the current threads for positive IDs.
+ Map<Thread, StackTraceElement[]> stMap = Thread.getAllStackTraces();
+ for (Thread thread : stMap.keySet()) {
+ if (thread.getId() <= 0) {
+ System.out.println("thread's ID is not positive: " + thread.getName());
+ }
+ }
+ }
+
+ public void run() {
+ for (int i = 0; i < totalOperations; ++i) {
+ test_getId();
+ }
+ }
+}
diff --git a/test/130-hprof/expected.txt b/test/130-hprof/expected.txt
new file mode 100644
index 0000000..cc3d9f2
--- /dev/null
+++ b/test/130-hprof/expected.txt
@@ -0,0 +1 @@
+Generated data.
diff --git a/test/130-hprof/info.txt b/test/130-hprof/info.txt
new file mode 100644
index 0000000..64475ef
--- /dev/null
+++ b/test/130-hprof/info.txt
@@ -0,0 +1 @@
+Dump the heap for this test.
diff --git a/test/130-hprof/src/Main.java b/test/130-hprof/src/Main.java
new file mode 100644
index 0000000..67e5232
--- /dev/null
+++ b/test/130-hprof/src/Main.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2009 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.io.File;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+public class Main {
+ private static final int TEST_LENGTH = 100;
+
+ private static boolean makeArray(int i) {
+ return i % 10 == 0;
+ }
+
+ private static void fillArray(Object global[], Object local[], int i) {
+ // Very stupid linking.
+ local[0] = global;
+ for (int j = 1; j < local.length; j++) {
+ local[j] = global[j];
+ }
+ }
+
+ public static void main(String[] args) {
+ // Create some data.
+ Object data[] = new Object[TEST_LENGTH];
+ for (int i = 0; i < data.length; i++) {
+ if (makeArray(i)) {
+ data[i] = new Object[TEST_LENGTH];
+ } else {
+ data[i] = String.valueOf(i);
+ }
+ }
+ for (int i = 0; i < data.length; i++) {
+ if (makeArray(i)) {
+ Object data2[] = (Object[]) data[i];
+ fillArray(data, data2, i);
+ }
+ }
+ System.out.println("Generated data.");
+
+ File dumpFile = null;
+ File convFile = null;
+
+ try {
+ // Now dump the heap.
+ dumpFile = createDump();
+
+ // Run hprof-conv on it.
+ convFile = getConvFile();
+
+ File hprof_conv = getHprofConf();
+ try {
+ ProcessBuilder pb = new ProcessBuilder(
+ hprof_conv.getAbsoluteFile().toString(),
+ dumpFile.getAbsoluteFile().toString(),
+ convFile.getAbsoluteFile().toString());
+ pb.redirectErrorStream(true);
+ Process process = pb.start();
+ int ret = process.waitFor();
+ if (ret != 0) {
+ throw new RuntimeException("Exited abnormally with " + ret);
+ }
+ } catch (Exception exc) {
+ throw new RuntimeException(exc);
+ }
+ } finally {
+ // Delete the files.
+ if (dumpFile != null) {
+ dumpFile.delete();
+ }
+ if (convFile != null) {
+ convFile.delete();
+ }
+ }
+ }
+
+ private static File getHprofConf() {
+ // Use the java.library.path. It points to the lib directory.
+ File libDir = new File(System.getProperty("java.library.path"));
+ return new File(new File(libDir.getParentFile(), "bin"), "hprof-conv");
+ }
+
+ private static File createDump() {
+ java.lang.reflect.Method dumpHprofDataMethod = getDumpHprofDataMethod();
+ if (dumpHprofDataMethod != null) {
+ File f = getDumpFile();
+ try {
+ dumpHprofDataMethod.invoke(null, f.getAbsoluteFile().toString());
+ return f;
+ } catch (Exception exc) {
+ exc.printStackTrace(System.out);
+ }
+ } else {
+ System.out.println("Could not find dump method!");
+ }
+ return null;
+ }
+
+ /**
+ * Finds VMDebug.dumpHprofData() through reflection. In the reference
+ * implementation this will not be available.
+ *
+ * @return the reflection object, or null if the method can't be found
+ */
+ private static Method getDumpHprofDataMethod() {
+ ClassLoader myLoader = Main.class.getClassLoader();
+ Class vmdClass;
+ try {
+ vmdClass = myLoader.loadClass("dalvik.system.VMDebug");
+ } catch (ClassNotFoundException cnfe) {
+ return null;
+ }
+
+ Method meth;
+ try {
+ meth = vmdClass.getMethod("dumpHprofData",
+ new Class[] { String.class });
+ } catch (NoSuchMethodException nsme) {
+ System.err.println("Found VMDebug but not dumpHprofData method");
+ return null;
+ }
+
+ return meth;
+ }
+
+ private static File getDumpFile() {
+ try {
+ return File.createTempFile("test-130-hprof", "dump");
+ } catch (Exception exc) {
+ return null;
+ }
+ }
+
+ private static File getConvFile() {
+ try {
+ return File.createTempFile("test-130-hprof", "conv");
+ } catch (Exception exc) {
+ return null;
+ }
+ }
+}
diff --git a/test/131-structural-change/build b/test/131-structural-change/build
new file mode 100755
index 0000000..7ddc81d
--- /dev/null
+++ b/test/131-structural-change/build
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# 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.
+
+# Stop if something fails.
+set -e
+
+mkdir classes
+${JAVAC} -d classes `find src -name '*.java'`
+
+mkdir classes-ex
+${JAVAC} -d classes-ex `find src-ex -name '*.java'`
+
+if [ ${NEED_DEX} = "true" ]; then
+ ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
+ zip $TEST_NAME.jar classes.dex
+ ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex
+ zip ${TEST_NAME}-ex.jar classes.dex
+fi
diff --git a/test/131-structural-change/expected.txt b/test/131-structural-change/expected.txt
new file mode 100644
index 0000000..cc7713d
--- /dev/null
+++ b/test/131-structural-change/expected.txt
@@ -0,0 +1,2 @@
+Should really reach here.
+Done.
diff --git a/test/131-structural-change/info.txt b/test/131-structural-change/info.txt
new file mode 100644
index 0000000..6d5817b
--- /dev/null
+++ b/test/131-structural-change/info.txt
@@ -0,0 +1 @@
+Check whether a structural change in a (non-native) multi-dex scenario is detected.
diff --git a/test/131-structural-change/run b/test/131-structural-change/run
new file mode 100755
index 0000000..63fdb8c
--- /dev/null
+++ b/test/131-structural-change/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+# Use secondary switch to add secondary dex file to class path.
+exec ${RUN} "${@}" --secondary
diff --git a/runtime/closure.h b/test/131-structural-change/src-ex/A.java
similarity index 65%
copy from runtime/closure.h
copy to test/131-structural-change/src-ex/A.java
index 9bea28f..800347b 100644
--- a/runtime/closure.h
+++ b/test/131-structural-change/src-ex/A.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -14,19 +14,7 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_CLOSURE_H_
-#define ART_RUNTIME_CLOSURE_H_
-
-namespace art {
-
-class Thread;
-
-class Closure {
- public:
- virtual ~Closure() { }
- virtual void Run(Thread* self) = 0;
-};
-
-} // namespace art
-
-#endif // ART_RUNTIME_CLOSURE_H_
+public class A {
+ public void bar() {
+ }
+}
diff --git a/runtime/closure.h b/test/131-structural-change/src-ex/B.java
similarity index 65%
rename from runtime/closure.h
rename to test/131-structural-change/src-ex/B.java
index 9bea28f..61369db 100644
--- a/runtime/closure.h
+++ b/test/131-structural-change/src-ex/B.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -14,19 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_CLOSURE_H_
-#define ART_RUNTIME_CLOSURE_H_
-
-namespace art {
-
-class Thread;
-
-class Closure {
- public:
- virtual ~Closure() { }
- virtual void Run(Thread* self) = 0;
-};
-
-} // namespace art
-
-#endif // ART_RUNTIME_CLOSURE_H_
+public class B extends A {
+ public void test() {
+ System.out.println("Should not reach this!");
+ }
+}
diff --git a/runtime/closure.h b/test/131-structural-change/src/A.java
similarity index 65%
copy from runtime/closure.h
copy to test/131-structural-change/src/A.java
index 9bea28f..b07de58 100644
--- a/runtime/closure.h
+++ b/test/131-structural-change/src/A.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -14,19 +14,13 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_CLOSURE_H_
-#define ART_RUNTIME_CLOSURE_H_
+public class A {
+ public void foo() {
+ }
-namespace art {
+ public void bar() {
+ }
-class Thread;
-
-class Closure {
- public:
- virtual ~Closure() { }
- virtual void Run(Thread* self) = 0;
-};
-
-} // namespace art
-
-#endif // ART_RUNTIME_CLOSURE_H_
+ public void baz() {
+ }
+}
diff --git a/test/131-structural-change/src/Main.java b/test/131-structural-change/src/Main.java
new file mode 100644
index 0000000..8dfa280
--- /dev/null
+++ b/test/131-structural-change/src/Main.java
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * Structural hazard test.
+ */
+public class Main {
+ public static void main(String[] args) {
+ new Main().run();
+ }
+
+ private void run() {
+ try {
+ Class<?> bClass = getClass().getClassLoader().loadClass("A");
+ System.out.println("Should really reach here.");
+ } catch (Exception e) {
+ e.printStackTrace(System.out);
+ }
+
+ boolean haveOatFile = hasOat();
+ boolean gotError = false;
+ try {
+ Class<?> bClass = getClass().getClassLoader().loadClass("B");
+ } catch (IncompatibleClassChangeError icce) {
+ gotError = true;
+ } catch (Exception e) {
+ e.printStackTrace(System.out);
+ }
+ if (haveOatFile ^ gotError) {
+ System.out.println("Did not get expected error.");
+ }
+ System.out.println("Done.");
+ }
+
+ static {
+ System.loadLibrary("arttest");
+ }
+
+ private native static boolean hasOat();
+}
diff --git a/test/132-daemon-locks-shutdown/expected.txt b/test/132-daemon-locks-shutdown/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/132-daemon-locks-shutdown/expected.txt
diff --git a/test/132-daemon-locks-shutdown/info.txt b/test/132-daemon-locks-shutdown/info.txt
new file mode 100644
index 0000000..f804064
--- /dev/null
+++ b/test/132-daemon-locks-shutdown/info.txt
@@ -0,0 +1 @@
+Tests that we can shut down the runtime with daemons still looping over locks.
diff --git a/test/132-daemon-locks-shutdown/src/Main.java b/test/132-daemon-locks-shutdown/src/Main.java
new file mode 100644
index 0000000..b5bbc8c
--- /dev/null
+++ b/test/132-daemon-locks-shutdown/src/Main.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Test that daemon threads still contending for a lock don't make the runtime abort on shutdown.
+ */
+public class Main {
+
+ public final static int THREAD_COUNT = 32;
+
+ public static void main(String[] args) throws Exception {
+ Object sync = new Object();
+
+ for (int i = 0; i < THREAD_COUNT; i++) {
+ Thread t = new Thread(new Wait(sync));
+ t.setDaemon(true);
+ t.start();
+ }
+ }
+
+ private static class Wait implements Runnable {
+ private Object obj;
+
+ public Wait(Object obj) {
+ this.obj = obj;
+ }
+
+ public void run() {
+ for (;;) {
+ synchronized(obj) {
+ try {
+ obj.wait(1);
+ } catch (Exception exc) {
+ exc.printStackTrace(System.out);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/test/133-static-invoke-super/expected.txt b/test/133-static-invoke-super/expected.txt
new file mode 100644
index 0000000..089d8e8
--- /dev/null
+++ b/test/133-static-invoke-super/expected.txt
@@ -0,0 +1,3 @@
+basis: performed 50000000 iterations
+test1: performed 50000000 iterations
+Timing is acceptable.
diff --git a/test/133-static-invoke-super/info.txt b/test/133-static-invoke-super/info.txt
new file mode 100644
index 0000000..606331b
--- /dev/null
+++ b/test/133-static-invoke-super/info.txt
@@ -0,0 +1,2 @@
+This is a performance test of invoking static methods in super class. To see the numbers, invoke
+this test with the "--timing" option.
diff --git a/test/133-static-invoke-super/run b/test/133-static-invoke-super/run
new file mode 100755
index 0000000..e27a622
--- /dev/null
+++ b/test/133-static-invoke-super/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# As this is a performance test we always use the non-debug build.
+exec ${RUN} "${@/#libartd.so/libart.so}"
diff --git a/test/133-static-invoke-super/src/Main.java b/test/133-static-invoke-super/src/Main.java
new file mode 100644
index 0000000..7cfd099
--- /dev/null
+++ b/test/133-static-invoke-super/src/Main.java
@@ -0,0 +1,63 @@
+
+public class Main {
+ static class SuperClass {
+ protected static int getVar(int w) {
+ return w & 0xF;
+ }
+ }
+ static class SubClass extends SuperClass {
+ final int getVarDirect(int w) {
+ return w & 0xF;
+ }
+ public void testDirect(int max) {
+ for (int i = 0; i < max; ++i) {
+ getVarDirect(max);
+ }
+ }
+ public void testStatic(int max) {
+ for (int i = 0; i < max; ++i) {
+ getVar(max);
+ }
+ }
+ }
+
+ static public void main(String[] args) throws Exception {
+ boolean timing = (args.length >= 1) && args[0].equals("--timing");
+ run(timing);
+ }
+
+ static int testBasis(int interations) {
+ (new SubClass()).testDirect(interations);
+ return interations;
+ }
+
+ static int testStatic(int interations) {
+ (new SubClass()).testStatic(interations);
+ return interations;
+ }
+
+ static public void run(boolean timing) {
+ long time0 = System.nanoTime();
+ int count1 = testBasis(50000000);
+ long time1 = System.nanoTime();
+ int count2 = testStatic(50000000);
+ long time2 = System.nanoTime();
+
+ System.out.println("basis: performed " + count1 + " iterations");
+ System.out.println("test1: performed " + count2 + " iterations");
+
+ double basisMsec = (time1 - time0) / (double) count1 / 1000000;
+ double msec1 = (time2 - time1) / (double) count2 / 1000000;
+
+ if (msec1 < basisMsec * 5) {
+ System.out.println("Timing is acceptable.");
+ } else {
+ System.out.println("Iterations are taking too long!");
+ timing = true;
+ }
+ if (timing) {
+ System.out.printf("basis time: %.3g msec\n", basisMsec);
+ System.out.printf("test1: %.3g msec per iteration\n", msec1);
+ }
+ }
+}
diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java
index e7dbe24..7ce2868 100644
--- a/test/422-type-conversion/src/Main.java
+++ b/test/422-type-conversion/src/Main.java
@@ -62,6 +62,18 @@
}
}
+ public static void assertFloatIsNaN(float result) {
+ if (!Float.isNaN(result)) {
+ throw new Error("Expected: NaN, found: " + result);
+ }
+ }
+
+ public static void assertDoubleIsNaN(double result) {
+ if (!Double.isNaN(result)) {
+ throw new Error("Expected: NaN, found: " + result);
+ }
+ }
+
public static void main(String[] args) {
// Generate, compile and check int-to-long Dex instructions.
@@ -94,6 +106,21 @@
// Generate, compile and check float-to-int Dex instructions.
floatToInt();
+ // Generate, compile and check float-to-long Dex instructions.
+ floatToLong();
+
+ // Generate, compile and check float-to-double Dex instructions.
+ floatToDouble();
+
+ // Generate, compile and check double-to-int Dex instructions.
+ doubleToInt();
+
+ // Generate, compile and check double-to-long Dex instructions.
+ doubleToLong();
+
+ // Generate, compile and check double-to-float Dex instructions.
+ doubleToFloat();
+
// Generate, compile and check int-to-byte Dex instructions.
shortToByte();
intToByte();
@@ -342,6 +369,136 @@
assertIntEquals(-2147483648, $opt$FloatToInt(Float.NEGATIVE_INFINITY));
}
+ private static void floatToLong() {
+ assertLongEquals(1L, $opt$FloatToLong(1F));
+ assertLongEquals(0L, $opt$FloatToLong(0F));
+ assertLongEquals(0L, $opt$FloatToLong(-0F));
+ assertLongEquals(-1L, $opt$FloatToLong(-1F));
+ assertLongEquals(51L, $opt$FloatToLong(51F));
+ assertLongEquals(-51L, $opt$FloatToLong(-51F));
+ assertLongEquals(0L, $opt$FloatToLong(0.5F));
+ assertLongEquals(0L, $opt$FloatToLong(0.4999999F));
+ assertLongEquals(0L, $opt$FloatToLong(-0.4999999F));
+ assertLongEquals(0L, $opt$FloatToLong(-0.5F));
+ assertLongEquals(42L, $opt$FloatToLong(42.199F));
+ assertLongEquals(-42L, $opt$FloatToLong(-42.199F));
+ assertLongEquals(2147483648L, $opt$FloatToLong(2147483647F)); // 2^31 - 1
+ assertLongEquals(-2147483648L, $opt$FloatToLong(-2147483647F)); // -(2^31 - 1)
+ assertLongEquals(-2147483648L, $opt$FloatToLong(-2147483648F)); // -(2^31)
+ assertLongEquals(2147483648L, $opt$FloatToLong(2147483648F)); // (2^31)
+ assertLongEquals(-2147483648L, $opt$FloatToLong(-2147483649F)); // -(2^31 + 1)
+ assertLongEquals(9223372036854775807L, $opt$FloatToLong(9223372036854775807F)); // 2^63 - 1
+ assertLongEquals(-9223372036854775808L, $opt$FloatToLong(-9223372036854775807F)); // -(2^63 - 1)
+ assertLongEquals(-9223372036854775808L, $opt$FloatToLong(-9223372036854775808F)); // -(2^63)
+ assertLongEquals(0L, $opt$FloatToLong(Float.NaN));
+ assertLongEquals(9223372036854775807L, $opt$FloatToLong(Float.POSITIVE_INFINITY));
+ assertLongEquals(-9223372036854775808L, $opt$FloatToLong(Float.NEGATIVE_INFINITY));
+ }
+
+ private static void floatToDouble() {
+ assertDoubleEquals(1D, $opt$FloatToDouble(1F));
+ assertDoubleEquals(0D, $opt$FloatToDouble(0F));
+ assertDoubleEquals(0D, $opt$FloatToDouble(-0F));
+ assertDoubleEquals(-1D, $opt$FloatToDouble(-1F));
+ assertDoubleEquals(51D, $opt$FloatToDouble(51F));
+ assertDoubleEquals(-51D, $opt$FloatToDouble(-51F));
+ assertDoubleEquals(0.5D, $opt$FloatToDouble(0.5F));
+ assertDoubleEquals(0.49999991059303284D, $opt$FloatToDouble(0.4999999F));
+ assertDoubleEquals(-0.49999991059303284D, $opt$FloatToDouble(-0.4999999F));
+ assertDoubleEquals(-0.5D, $opt$FloatToDouble(-0.5F));
+ assertDoubleEquals(42.19900131225586D, $opt$FloatToDouble(42.199F));
+ assertDoubleEquals(-42.19900131225586D, $opt$FloatToDouble(-42.199F));
+ assertDoubleEquals(2147483648D, $opt$FloatToDouble(2147483647F)); // 2^31 - 1
+ assertDoubleEquals(-2147483648D, $opt$FloatToDouble(-2147483647F)); // -(2^31 - 1)
+ assertDoubleEquals(-2147483648D, $opt$FloatToDouble(-2147483648F)); // -(2^31)
+ assertDoubleEquals(2147483648D, $opt$FloatToDouble(2147483648F)); // (2^31)
+ assertDoubleEquals(-2147483648D, $opt$FloatToDouble(-2147483649F)); // -(2^31 + 1)
+ assertDoubleEquals(9223372036854775807D, $opt$FloatToDouble(9223372036854775807F)); // 2^63 - 1
+ assertDoubleEquals(-9223372036854775807D, $opt$FloatToDouble(-9223372036854775807F)); // -(2^63 - 1)
+ assertDoubleEquals(-9223372036854775808D, $opt$FloatToDouble(-9223372036854775808F)); // -(2^63)
+ assertDoubleIsNaN($opt$FloatToDouble(Float.NaN));
+ assertDoubleEquals(Double.POSITIVE_INFINITY, $opt$FloatToDouble(Float.POSITIVE_INFINITY));
+ assertDoubleEquals(Double.NEGATIVE_INFINITY, $opt$FloatToDouble(Float.NEGATIVE_INFINITY));
+ }
+
+ private static void doubleToInt() {
+ assertIntEquals(1, $opt$DoubleToInt(1D));
+ assertIntEquals(0, $opt$DoubleToInt(0D));
+ assertIntEquals(0, $opt$DoubleToInt(-0D));
+ assertIntEquals(-1, $opt$DoubleToInt(-1D));
+ assertIntEquals(51, $opt$DoubleToInt(51D));
+ assertIntEquals(-51, $opt$DoubleToInt(-51D));
+ assertIntEquals(0, $opt$DoubleToInt(0.5D));
+ assertIntEquals(0, $opt$DoubleToInt(0.4999999D));
+ assertIntEquals(0, $opt$DoubleToInt(-0.4999999D));
+ assertIntEquals(0, $opt$DoubleToInt(-0.5D));
+ assertIntEquals(42, $opt$DoubleToInt(42.199D));
+ assertIntEquals(-42, $opt$DoubleToInt(-42.199D));
+ assertIntEquals(2147483647, $opt$DoubleToInt(2147483647D)); // 2^31 - 1
+ assertIntEquals(-2147483647, $opt$DoubleToInt(-2147483647D)); // -(2^31 - 1)
+ assertIntEquals(-2147483648, $opt$DoubleToInt(-2147483648D)); // -(2^31)
+ assertIntEquals(2147483647, $opt$DoubleToInt(2147483648D)); // (2^31)
+ assertIntEquals(-2147483648, $opt$DoubleToInt(-2147483649D)); // -(2^31 + 1)
+ assertIntEquals(2147483647, $opt$DoubleToInt(9223372036854775807D)); // 2^63 - 1
+ assertIntEquals(-2147483648, $opt$DoubleToInt(-9223372036854775807D)); // -(2^63 - 1)
+ assertIntEquals(-2147483648, $opt$DoubleToInt(-9223372036854775808D)); // -(2^63)
+ assertIntEquals(0, $opt$DoubleToInt(Double.NaN));
+ assertIntEquals(2147483647, $opt$DoubleToInt(Double.POSITIVE_INFINITY));
+ assertIntEquals(-2147483648, $opt$DoubleToInt(Double.NEGATIVE_INFINITY));
+ }
+
+ private static void doubleToLong() {
+ assertLongEquals(1L, $opt$DoubleToLong(1D));
+ assertLongEquals(0L, $opt$DoubleToLong(0D));
+ assertLongEquals(0L, $opt$DoubleToLong(-0D));
+ assertLongEquals(-1L, $opt$DoubleToLong(-1D));
+ assertLongEquals(51L, $opt$DoubleToLong(51D));
+ assertLongEquals(-51L, $opt$DoubleToLong(-51D));
+ assertLongEquals(0L, $opt$DoubleToLong(0.5D));
+ assertLongEquals(0L, $opt$DoubleToLong(0.4999999D));
+ assertLongEquals(0L, $opt$DoubleToLong(-0.4999999D));
+ assertLongEquals(0L, $opt$DoubleToLong(-0.5D));
+ assertLongEquals(42L, $opt$DoubleToLong(42.199D));
+ assertLongEquals(-42L, $opt$DoubleToLong(-42.199D));
+ assertLongEquals(2147483647L, $opt$DoubleToLong(2147483647D)); // 2^31 - 1
+ assertLongEquals(-2147483647L, $opt$DoubleToLong(-2147483647D)); // -(2^31 - 1)
+ assertLongEquals(-2147483648L, $opt$DoubleToLong(-2147483648D)); // -(2^31)
+ assertLongEquals(2147483648L, $opt$DoubleToLong(2147483648D)); // (2^31)
+ assertLongEquals(-2147483649L, $opt$DoubleToLong(-2147483649D)); // -(2^31 + 1)
+ assertLongEquals(9223372036854775807L, $opt$DoubleToLong(9223372036854775807D)); // 2^63 - 1
+ assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(-9223372036854775807D)); // -(2^63 - 1)
+ assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(-9223372036854775808D)); // -(2^63)
+ assertLongEquals(0L, $opt$DoubleToLong(Double.NaN));
+ assertLongEquals(9223372036854775807L, $opt$DoubleToLong(Double.POSITIVE_INFINITY));
+ assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(Double.NEGATIVE_INFINITY));
+ }
+
+ private static void doubleToFloat() {
+ assertFloatEquals(1F, $opt$DoubleToFloat(1D));
+ assertFloatEquals(0F, $opt$DoubleToFloat(0D));
+ assertFloatEquals(0F, $opt$DoubleToFloat(-0D));
+ assertFloatEquals(-1F, $opt$DoubleToFloat(-1D));
+ assertFloatEquals(51F, $opt$DoubleToFloat(51D));
+ assertFloatEquals(-51F, $opt$DoubleToFloat(-51D));
+ assertFloatEquals(0.5F, $opt$DoubleToFloat(0.5D));
+ assertFloatEquals(0.4999999F, $opt$DoubleToFloat(0.4999999D));
+ assertFloatEquals(-0.4999999F, $opt$DoubleToFloat(-0.4999999D));
+ assertFloatEquals(-0.5F, $opt$DoubleToFloat(-0.5D));
+ assertFloatEquals(42.199F, $opt$DoubleToFloat(42.199D));
+ assertFloatEquals(-42.199F, $opt$DoubleToFloat(-42.199D));
+ assertFloatEquals(2147483648F, $opt$DoubleToFloat(2147483647D)); // 2^31 - 1
+ assertFloatEquals(-2147483648F, $opt$DoubleToFloat(-2147483647D)); // -(2^31 - 1)
+ assertFloatEquals(-2147483648F, $opt$DoubleToFloat(-2147483648D)); // -(2^31)
+ assertFloatEquals(2147483648F, $opt$DoubleToFloat(2147483648D)); // (2^31)
+ assertFloatEquals(-2147483648F, $opt$DoubleToFloat(-2147483649D)); // -(2^31 + 1)
+ assertFloatEquals(9223372036854775807F, $opt$DoubleToFloat(9223372036854775807D)); // 2^63 - 1
+ assertFloatEquals(-9223372036854775807F, $opt$DoubleToFloat(-9223372036854775807D)); // -(2^63 - 1)
+ assertFloatEquals(-9223372036854775808F, $opt$DoubleToFloat(-9223372036854775808D)); // -(2^63)
+ assertFloatIsNaN($opt$DoubleToFloat(Float.NaN));
+ assertFloatEquals(Float.POSITIVE_INFINITY, $opt$DoubleToFloat(Double.POSITIVE_INFINITY));
+ assertFloatEquals(Float.NEGATIVE_INFINITY, $opt$DoubleToFloat(Double.NEGATIVE_INFINITY));
+ }
+
private static void shortToByte() {
assertByteEquals((byte)1, $opt$ShortToByte((short)1));
assertByteEquals((byte)0, $opt$ShortToByte((short)0));
@@ -470,48 +627,63 @@
// These methods produce int-to-long Dex instructions.
- static long $opt$ByteToLong(byte a) { return a; }
- static long $opt$ShortToLong(short a) { return a; }
- static long $opt$IntToLong(int a) { return a; }
- static long $opt$CharToLong(int a) { return a; }
+ static long $opt$ByteToLong(byte a) { return (long)a; }
+ static long $opt$ShortToLong(short a) { return (long)a; }
+ static long $opt$IntToLong(int a) { return (long)a; }
+ static long $opt$CharToLong(int a) { return (long)a; }
// These methods produce int-to-float Dex instructions.
- static float $opt$ByteToFloat(byte a) { return a; }
- static float $opt$ShortToFloat(short a) { return a; }
- static float $opt$IntToFloat(int a) { return a; }
- static float $opt$CharToFloat(char a) { return a; }
+ static float $opt$ByteToFloat(byte a) { return (float)a; }
+ static float $opt$ShortToFloat(short a) { return (float)a; }
+ static float $opt$IntToFloat(int a) { return (float)a; }
+ static float $opt$CharToFloat(char a) { return (float)a; }
// These methods produce int-to-double Dex instructions.
- static double $opt$ByteToDouble(byte a) { return a; }
- static double $opt$ShortToDouble(short a) { return a; }
- static double $opt$IntToDouble(int a) { return a; }
- static double $opt$CharToDouble(int a) { return a; }
+ static double $opt$ByteToDouble(byte a) { return (double)a; }
+ static double $opt$ShortToDouble(short a) { return (double)a; }
+ static double $opt$IntToDouble(int a) { return (double)a; }
+ static double $opt$CharToDouble(int a) { return (double)a; }
// These methods produce long-to-int Dex instructions.
- static int $opt$LongToInt(long a){ return (int)a; }
- static int $opt$LongLiteralToInt(){ return (int)42L; }
+ static int $opt$LongToInt(long a) { return (int)a; }
+ static int $opt$LongLiteralToInt() { return (int)42L; }
// This method produces a long-to-float Dex instruction.
- static float $opt$LongToFloat(long a){ return (float)a; }
+ static float $opt$LongToFloat(long a) { return (float)a; }
// This method produces a long-to-double Dex instruction.
- static double $opt$LongToDouble(long a){ return (double)a; }
+ static double $opt$LongToDouble(long a) { return (double)a; }
// This method produces a float-to-int Dex instruction.
- static int $opt$FloatToInt(float a){ return (int)a; }
+ static int $opt$FloatToInt(float a) { return (int)a; }
+
+ // This method produces a float-to-long Dex instruction.
+ static long $opt$FloatToLong(float a){ return (long)a; }
+
+ // This method produces a float-to-double Dex instruction.
+ static double $opt$FloatToDouble(float a) { return (double)a; }
+
+ // This method produces a double-to-int Dex instruction.
+ static int $opt$DoubleToInt(double a){ return (int)a; }
+
+ // This method produces a double-to-long Dex instruction.
+ static long $opt$DoubleToLong(double a){ return (long)a; }
+
+ // This method produces a double-to-float Dex instruction.
+ static float $opt$DoubleToFloat(double a) { return (float)a; }
// These methods produce int-to-byte Dex instructions.
- static byte $opt$ShortToByte(short a){ return (byte)a; }
- static byte $opt$IntToByte(int a){ return (byte)a; }
- static byte $opt$CharToByte(char a){ return (byte)a; }
+ static byte $opt$ShortToByte(short a) { return (byte)a; }
+ static byte $opt$IntToByte(int a) { return (byte)a; }
+ static byte $opt$CharToByte(char a) { return (byte)a; }
// These methods produce int-to-short Dex instructions.
- static short $opt$ByteToShort(byte a){ return (short)a; }
- static short $opt$IntToShort(int a){ return (short)a; }
- static short $opt$CharToShort(char a){ return (short)a; }
+ static short $opt$ByteToShort(byte a) { return (short)a; }
+ static short $opt$IntToShort(int a) { return (short)a; }
+ static short $opt$CharToShort(char a) { return (short)a; }
// These methods produce int-to-char Dex instructions.
- static char $opt$ByteToChar(byte a){ return (char)a; }
- static char $opt$ShortToChar(short a){ return (char)a; }
- static char $opt$IntToChar(int a){ return (char)a; }
+ static char $opt$ByteToChar(byte a) { return (char)a; }
+ static char $opt$ShortToChar(short a) { return (char)a; }
+ static char $opt$IntToChar(int a) { return (char)a; }
}
diff --git a/test/428-optimizing-arith-rem/src/Main.java b/test/428-optimizing-arith-rem/src/Main.java
index 46bd3c6..3f77318 100644
--- a/test/428-optimizing-arith-rem/src/Main.java
+++ b/test/428-optimizing-arith-rem/src/Main.java
@@ -16,49 +16,7 @@
public class Main {
- public static void expectEquals(int expected, int result) {
- if (expected != result) {
- throw new Error("Expected: " + expected + ", found: " + result);
- }
- }
-
- public static void expectEquals(long expected, long result) {
- if (expected != result) {
- throw new Error("Expected: " + expected + ", found: " + result);
- }
- }
-
- public static void expectDivisionByZero(int value) {
- try {
- $opt$Rem(value, 0);
- throw new Error("Expected RuntimeException when modulo by 0");
- } catch (java.lang.RuntimeException e) {
- }
- try {
- $opt$RemZero(value);
- throw new Error("Expected RuntimeException when modulo by 0");
- } catch (java.lang.RuntimeException e) {
- }
- }
-
- public static void expectDivisionByZero(long value) {
- try {
- $opt$Rem(value, 0L);
- throw new Error("Expected RuntimeException when modulo by 0");
- } catch (java.lang.RuntimeException e) {
- }
- try {
- $opt$RemZero(value);
- throw new Error("Expected RuntimeException when modulo by 0");
- } catch (java.lang.RuntimeException e) {
- }
- }
-
public static void main(String[] args) {
- rem();
- }
-
- public static void rem() {
remInt();
remLong();
}
@@ -115,22 +73,22 @@
expectEquals(-7L, $opt$Rem(-7L, 9L));
expectEquals(-7L, $opt$Rem(-7L, -9L));
- expectEquals(0L, $opt$Rem(Integer.MAX_VALUE, 1L));
- expectEquals(0L, $opt$Rem(Integer.MAX_VALUE, -1L));
- expectEquals(0L, $opt$Rem(Integer.MIN_VALUE, 1L));
- expectEquals(0L, $opt$Rem(Integer.MIN_VALUE, -1L)); // no overflow
- expectEquals(-1L, $opt$Rem(Integer.MIN_VALUE, Integer.MAX_VALUE));
- expectEquals(Integer.MAX_VALUE, $opt$Rem(Integer.MAX_VALUE, Integer.MIN_VALUE));
+ expectEquals(0L, $opt$Rem(Long.MAX_VALUE, 1L));
+ expectEquals(0L, $opt$Rem(Long.MAX_VALUE, -1L));
+ expectEquals(0L, $opt$Rem(Long.MIN_VALUE, 1L));
+ expectEquals(0L, $opt$Rem(Long.MIN_VALUE, -1L)); // no overflow
+ expectEquals(-1L, $opt$Rem(Long.MIN_VALUE, Long.MAX_VALUE));
+ expectEquals(Long.MAX_VALUE, $opt$Rem(Long.MAX_VALUE, Long.MIN_VALUE));
expectEquals(0L, $opt$Rem(0L, 7L));
- expectEquals(0L, $opt$Rem(0L, Integer.MAX_VALUE));
- expectEquals(0L, $opt$Rem(0L, Integer.MIN_VALUE));
+ expectEquals(0L, $opt$Rem(0L, Long.MAX_VALUE));
+ expectEquals(0L, $opt$Rem(0L, Long.MIN_VALUE));
expectDivisionByZero(0L);
expectDivisionByZero(1L);
expectDivisionByZero(5L);
- expectDivisionByZero(Integer.MAX_VALUE);
- expectDivisionByZero(Integer.MIN_VALUE);
+ expectDivisionByZero(Long.MAX_VALUE);
+ expectDivisionByZero(Long.MIN_VALUE);
}
static int $opt$Rem(int a, int b) {
@@ -157,4 +115,43 @@
static long $opt$RemZero(long a) {
return a % 0L;
}
+
+ public static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void expectEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void expectDivisionByZero(int value) {
+ try {
+ $opt$Rem(value, 0);
+ throw new Error("Expected RuntimeException when modulo by 0");
+ } catch (java.lang.RuntimeException e) {
+ }
+ try {
+ $opt$RemZero(value);
+ throw new Error("Expected RuntimeException when modulo by 0");
+ } catch (java.lang.RuntimeException e) {
+ }
+ }
+
+ public static void expectDivisionByZero(long value) {
+ try {
+ $opt$Rem(value, 0L);
+ throw new Error("Expected RuntimeException when modulo by 0");
+ } catch (java.lang.RuntimeException e) {
+ }
+ try {
+ $opt$RemZero(value);
+ throw new Error("Expected RuntimeException when modulo by 0");
+ } catch (java.lang.RuntimeException e) {
+ }
+ }
+
}
diff --git a/test/436-rem-float/expected.txt b/test/436-rem-float/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/436-rem-float/expected.txt
diff --git a/test/436-rem-float/info.txt b/test/436-rem-float/info.txt
new file mode 100644
index 0000000..b023f59
--- /dev/null
+++ b/test/436-rem-float/info.txt
@@ -0,0 +1 @@
+Tests for floating point modulo (rem) operation.
diff --git a/test/436-rem-float/src/Main.java b/test/436-rem-float/src/Main.java
new file mode 100644
index 0000000..cc6341a
--- /dev/null
+++ b/test/436-rem-float/src/Main.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ public static void main(String[] args) {
+ remFloat();
+ remDouble();
+ }
+
+ private static void remFloat() {
+ expectApproxEquals(1.98F, $opt$Rem(1.98F, 2F));
+ expectApproxEquals(0F, $opt$Rem(2F, 0.5F));
+ expectApproxEquals(0.09999F, $opt$Rem(1.0F, 0.1F));
+ expectApproxEquals(1.9F, $opt$Rem(6.5F, 2.3F));
+ expectApproxEquals(0.48F, $opt$Rem(1.98F, 1.5F));
+ expectApproxEquals(0.9999F, $opt$Rem(0.9999F, 1.222F));
+ expectApproxEquals(0.9999F, $opt$Rem(0.9999F, 1.0001F));
+ expectApproxEquals(-1.98F, $opt$Rem(-1.98F, 2F));
+ expectApproxEquals(-0F, $opt$Rem(-2F, 0.5F));
+ expectApproxEquals(-0.09999F, $opt$Rem(-1.0F, 0.1F));
+ expectApproxEquals(-1.9F, $opt$Rem(-6.5F, 2.3F));
+ expectApproxEquals(-0.48F, $opt$Rem(-1.98F, 1.5F));
+ expectApproxEquals(-0.9999F, $opt$Rem(-0.9999F, 1.222F));
+ expectApproxEquals(-0.9999F, $opt$Rem(-0.9999F, 1.0001F));
+ expectApproxEquals(1.98F, $opt$Rem(1.98F, -2F));
+ expectApproxEquals(0F, $opt$Rem(2F, -0.5F));
+ expectApproxEquals(0.09999F, $opt$Rem(1.0F, -0.1F));
+ expectApproxEquals(1.9F, $opt$Rem(6.5F, -2.3F));
+ expectApproxEquals(0.48F, $opt$Rem(1.98F, -1.5F));
+ expectApproxEquals(0.9999F, $opt$Rem(0.9999F, -1.222F));
+ expectApproxEquals(0.9999F, $opt$Rem(0.9999F, -1.0001F));
+ expectApproxEquals(-1.98F, $opt$Rem(-1.98F, -2F));
+ expectApproxEquals(-0F, $opt$Rem(-2F, -0.5F));
+ expectApproxEquals(-0.09999F, $opt$Rem(-1.0F, -0.1F));
+ expectApproxEquals(-1.9F, $opt$Rem(-6.5F, -2.3F));
+ expectApproxEquals(-0.48F, $opt$Rem(-1.98F, -1.5F));
+ expectApproxEquals(-0.9999F, $opt$Rem(-0.9999F, -1.222F));
+ expectApproxEquals(-0.9999F, $opt$Rem(-0.9999F, -1.0001F));
+
+ expectApproxEquals(1.68267e-18F, $opt$Rem(61615.2F, -2.48699e-17F));
+ expectApproxEquals(-8.63819e-09F, $opt$Rem(-1.73479e+14F, 3.11154e-08F));
+ expectApproxEquals(1.10911e-12F, $opt$Rem(338122F, 4.57572e-12F));
+
+ expectApproxEquals(2F, $opt$RemConst(6F));
+ expectApproxEquals(2F, $opt$Rem(5.1F, 3.1F));
+ expectApproxEquals(2.1F, $opt$Rem(5.1F, 3F));
+ expectApproxEquals(-2F, $opt$Rem(-5.1F, 3.1F));
+ expectApproxEquals(-2.1F, $opt$Rem(-5.1F, -3F));
+ expectApproxEquals(2F, $opt$Rem(6F, 4F));
+ expectApproxEquals(2F, $opt$Rem(6F, -4F));
+ expectApproxEquals(0F, $opt$Rem(6F, 3F));
+ expectApproxEquals(0F, $opt$Rem(6F, -3F));
+ expectApproxEquals(0F, $opt$Rem(6F, 1F));
+ expectApproxEquals(0F, $opt$Rem(6F, -1F));
+ expectApproxEquals(-1F, $opt$Rem(-7F, 3F));
+ expectApproxEquals(-1F, $opt$Rem(-7F, -3F));
+ expectApproxEquals(0F, $opt$Rem(6F, 6F));
+ expectApproxEquals(0F, $opt$Rem(-6F, -6F));
+ expectApproxEquals(7F, $opt$Rem(7F, 9F));
+ expectApproxEquals(7F, $opt$Rem(7F, -9F));
+ expectApproxEquals(-7F, $opt$Rem(-7F, 9F));
+ expectApproxEquals(-7F, $opt$Rem(-7F, -9F));
+ expectApproxEquals(0F, $opt$Rem(Float.MAX_VALUE, 1F));
+ expectApproxEquals(0F, $opt$Rem(Float.MAX_VALUE, -1F));
+ expectApproxEquals(0F, $opt$Rem(Float.MIN_VALUE, 1F));
+ expectApproxEquals(0F, $opt$Rem(Float.MIN_VALUE, -1F));
+ expectApproxEquals(0F, $opt$Rem(0F, 7F));
+ expectApproxEquals(0F, $opt$Rem(0F, Float.MAX_VALUE));
+ expectApproxEquals(0F, $opt$Rem(0F, Float.MIN_VALUE));
+ expectApproxEquals(0F, $opt$Rem(0F, Float.POSITIVE_INFINITY));
+ expectApproxEquals(0F, $opt$Rem(0F, Float.NEGATIVE_INFINITY));
+ expectApproxEquals(4F, $opt$Rem(4F, Float.POSITIVE_INFINITY));
+ expectApproxEquals(4F, $opt$Rem(4F, Float.NEGATIVE_INFINITY));
+ expectApproxEquals(-4F, $opt$Rem(-4F, Float.POSITIVE_INFINITY));
+ expectApproxEquals(-4F, $opt$Rem(-4F, Float.NEGATIVE_INFINITY));
+ expectApproxEquals(0F, $opt$Rem(Float.MIN_NORMAL, Float.MIN_VALUE));
+ expectApproxEquals(0F, $opt$Rem(Float.MIN_NORMAL, Float.MIN_NORMAL));
+ expectApproxEquals(0F, $opt$Rem(Float.MIN_VALUE, Float.MIN_VALUE));
+ expectApproxEquals(0F, $opt$Rem(Float.MAX_VALUE, Float.MIN_VALUE));
+ expectApproxEquals(0F, $opt$Rem(Float.MAX_VALUE, Float.MAX_VALUE));
+ expectApproxEquals(0F, $opt$Rem(Float.MAX_VALUE, Float.MIN_NORMAL));
+ expectApproxEquals(Float.MIN_NORMAL, $opt$Rem(Float.MIN_NORMAL, Float.MAX_VALUE));
+ expectApproxEquals(Float.MIN_NORMAL, $opt$Rem(Float.MIN_NORMAL, Float.NEGATIVE_INFINITY));
+ expectApproxEquals(Float.MIN_NORMAL, $opt$Rem(Float.MIN_NORMAL, Float.POSITIVE_INFINITY));
+ expectApproxEquals(Float.MIN_VALUE, $opt$Rem(Float.MIN_VALUE, Float.MAX_VALUE));
+ expectApproxEquals(Float.MIN_VALUE, $opt$Rem(Float.MIN_VALUE, Float.MIN_NORMAL));
+ expectApproxEquals(Float.MIN_VALUE, $opt$Rem(Float.MIN_VALUE, Float.NEGATIVE_INFINITY));
+ expectApproxEquals(Float.MIN_VALUE, $opt$Rem(Float.MIN_VALUE, Float.POSITIVE_INFINITY));
+ expectApproxEquals(Float.MAX_VALUE, $opt$Rem(Float.MAX_VALUE, Float.NEGATIVE_INFINITY));
+ expectApproxEquals(Float.MAX_VALUE, $opt$Rem(Float.MAX_VALUE, Float.POSITIVE_INFINITY));
+
+ expectNaN($opt$Rem(Float.NaN, 3F));
+ expectNaN($opt$Rem(3F, Float.NaN));
+ expectNaN($opt$Rem(3F, 0F));
+ expectNaN($opt$Rem(1F, 0F));
+ expectNaN($opt$Rem(-1F, 0F));
+ expectNaN($opt$Rem(Float.NEGATIVE_INFINITY, Float.MIN_VALUE));
+ expectNaN($opt$Rem(Float.NEGATIVE_INFINITY, Float.MAX_VALUE));
+ expectNaN($opt$Rem(Float.NEGATIVE_INFINITY, Float.MIN_NORMAL));
+ expectNaN($opt$Rem(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));
+ expectNaN($opt$Rem(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY));
+ expectNaN($opt$Rem(Float.POSITIVE_INFINITY, Float.MIN_VALUE));
+ expectNaN($opt$Rem(Float.POSITIVE_INFINITY, Float.MAX_VALUE));
+ expectNaN($opt$Rem(Float.POSITIVE_INFINITY, Float.MIN_NORMAL));
+ expectNaN($opt$Rem(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY));
+ expectNaN($opt$Rem(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));
+ }
+
+ private static void remDouble() {
+ expectApproxEquals(1.98D, $opt$Rem(1.98D, 2D));
+ expectApproxEquals(0D, $opt$Rem(2D, 0.5D));
+ expectApproxEquals(0.09999D, $opt$Rem(1.0D, 0.1D));
+ expectApproxEquals(1.9D, $opt$Rem(6.5D, 2.3D));
+ expectApproxEquals(0.48D, $opt$Rem(1.98D, 1.5D));
+ expectApproxEquals(0.9999D, $opt$Rem(0.9999D, 1.222D));
+ expectApproxEquals(0.9999D, $opt$Rem(0.9999D, 1.0001D));
+ expectApproxEquals(-1.98D, $opt$Rem(-1.98D, 2D));
+ expectApproxEquals(-0D, $opt$Rem(-2D, 0.5D));
+ expectApproxEquals(-0.09999D, $opt$Rem(-1.0D, 0.1D));
+ expectApproxEquals(-1.9D, $opt$Rem(-6.5D, 2.3D));
+ expectApproxEquals(-0.48D, $opt$Rem(-1.98D, 1.5D));
+ expectApproxEquals(-0.9999D, $opt$Rem(-0.9999D, 1.222D));
+ expectApproxEquals(-0.9999D, $opt$Rem(-0.9999D, 1.0001D));
+ expectApproxEquals(1.98D, $opt$Rem(1.98D, -2D));
+ expectApproxEquals(0D, $opt$Rem(2D, -0.5D));
+ expectApproxEquals(0.09999D, $opt$Rem(1.0D, -0.1D));
+ expectApproxEquals(1.9D, $opt$Rem(6.5D, -2.3D));
+ expectApproxEquals(0.48D, $opt$Rem(1.98D, -1.5D));
+ expectApproxEquals(0.9999D, $opt$Rem(0.9999D, -1.222D));
+ expectApproxEquals(0.9999D, $opt$Rem(0.9999D, -1.0001D));
+ expectApproxEquals(-1.98D, $opt$Rem(-1.98D, -2D));
+ expectApproxEquals(-0D, $opt$Rem(-2D, -0.5D));
+ expectApproxEquals(-0.09999D, $opt$Rem(-1.0D, -0.1D));
+ expectApproxEquals(-1.9D, $opt$Rem(-6.5D, -2.3D));
+ expectApproxEquals(-0.48D, $opt$Rem(-1.98D, -1.5D));
+ expectApproxEquals(-0.9999D, $opt$Rem(-0.9999D, -1.222D));
+ expectApproxEquals(-0.9999D, $opt$Rem(-0.9999D, -1.0001D));
+
+ expectApproxEquals(2D, $opt$RemConst(6D));
+ expectApproxEquals(2D, $opt$Rem(5.1D, 3.1D));
+ expectApproxEquals(2.1D, $opt$Rem(5.1D, 3D));
+ expectApproxEquals(-2D, $opt$Rem(-5.1D, 3.1D));
+ expectApproxEquals(-2.1D, $opt$Rem(-5.1D, -3D));
+ expectApproxEquals(2D, $opt$Rem(6D, 4D));
+ expectApproxEquals(2D, $opt$Rem(6D, -4D));
+ expectApproxEquals(0D, $opt$Rem(6D, 3D));
+ expectApproxEquals(0D, $opt$Rem(6D, -3D));
+ expectApproxEquals(0D, $opt$Rem(6D, 1D));
+ expectApproxEquals(0D, $opt$Rem(6D, -1D));
+ expectApproxEquals(-1D, $opt$Rem(-7D, 3D));
+ expectApproxEquals(-1D, $opt$Rem(-7D, -3D));
+ expectApproxEquals(0D, $opt$Rem(6D, 6D));
+ expectApproxEquals(0D, $opt$Rem(-6D, -6D));
+ expectApproxEquals(7D, $opt$Rem(7D, 9D));
+ expectApproxEquals(7D, $opt$Rem(7D, -9D));
+ expectApproxEquals(-7D, $opt$Rem(-7D, 9D));
+ expectApproxEquals(-7D, $opt$Rem(-7D, -9D));
+ expectApproxEquals(0D, $opt$Rem(Double.MAX_VALUE, 1D));
+ expectApproxEquals(0D, $opt$Rem(Double.MAX_VALUE, -1D));
+ expectApproxEquals(0D, $opt$Rem(Double.MIN_VALUE, 1D));
+ expectApproxEquals(0D, $opt$Rem(Double.MIN_VALUE, -1D));
+ expectApproxEquals(0D, $opt$Rem(0D, 7D));
+ expectApproxEquals(0D, $opt$Rem(0D, Double.MAX_VALUE));
+ expectApproxEquals(0D, $opt$Rem(0D, Double.MIN_VALUE));
+ expectApproxEquals(0D, $opt$Rem(0D, Double.POSITIVE_INFINITY));
+ expectApproxEquals(0D, $opt$Rem(0D, Double.NEGATIVE_INFINITY));
+ expectApproxEquals(4D, $opt$Rem(4D, Double.POSITIVE_INFINITY));
+ expectApproxEquals(4D, $opt$Rem(4D, Double.NEGATIVE_INFINITY));
+ expectApproxEquals(-4D, $opt$Rem(-4D, Double.POSITIVE_INFINITY));
+ expectApproxEquals(-4D, $opt$Rem(-4D, Double.NEGATIVE_INFINITY));
+ expectApproxEquals(0D, $opt$Rem(Double.MIN_NORMAL, Double.MIN_VALUE));
+ expectApproxEquals(0D, $opt$Rem(Double.MIN_NORMAL, Double.MIN_NORMAL));
+ expectApproxEquals(0D, $opt$Rem(Double.MIN_VALUE, Double.MIN_VALUE));
+ expectApproxEquals(0D, $opt$Rem(Double.MAX_VALUE, Double.MIN_VALUE));
+ expectApproxEquals(0D, $opt$Rem(Double.MAX_VALUE, Double.MAX_VALUE));
+ expectApproxEquals(0D, $opt$Rem(Double.MAX_VALUE, Double.MIN_NORMAL));
+ expectApproxEquals(Double.MIN_NORMAL, $opt$Rem(Double.MIN_NORMAL, Double.MAX_VALUE));
+ expectApproxEquals(Double.MIN_NORMAL, $opt$Rem(Double.MIN_NORMAL, Double.NEGATIVE_INFINITY));
+ expectApproxEquals(Double.MIN_NORMAL, $opt$Rem(Double.MIN_NORMAL, Double.POSITIVE_INFINITY));
+ expectApproxEquals(Double.MIN_VALUE, $opt$Rem(Double.MIN_VALUE, Double.MAX_VALUE));
+ expectApproxEquals(Double.MIN_VALUE, $opt$Rem(Double.MIN_VALUE, Double.MIN_NORMAL));
+ expectApproxEquals(Double.MIN_VALUE, $opt$Rem(Double.MIN_VALUE, Double.NEGATIVE_INFINITY));
+ expectApproxEquals(Double.MIN_VALUE, $opt$Rem(Double.MIN_VALUE, Double.POSITIVE_INFINITY));
+ expectApproxEquals(Double.MAX_VALUE, $opt$Rem(Double.MAX_VALUE, Double.NEGATIVE_INFINITY));
+ expectApproxEquals(Double.MAX_VALUE, $opt$Rem(Double.MAX_VALUE, Double.POSITIVE_INFINITY));
+
+ expectNaN($opt$Rem(Double.NaN, 3D));
+ expectNaN($opt$Rem(3D, Double.NaN));
+ expectNaN($opt$Rem(3D, 0D));
+ expectNaN($opt$Rem(1D, 0D));
+ expectNaN($opt$Rem(-1D, 0D));
+ expectNaN($opt$Rem(Double.NEGATIVE_INFINITY, Double.MIN_VALUE));
+ expectNaN($opt$Rem(Double.NEGATIVE_INFINITY, Double.MAX_VALUE));
+ expectNaN($opt$Rem(Double.NEGATIVE_INFINITY, Double.MIN_NORMAL));
+ expectNaN($opt$Rem(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY));
+ expectNaN($opt$Rem(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY));
+ expectNaN($opt$Rem(Double.POSITIVE_INFINITY, Double.MIN_VALUE));
+ expectNaN($opt$Rem(Double.POSITIVE_INFINITY, Double.MAX_VALUE));
+ expectNaN($opt$Rem(Double.POSITIVE_INFINITY, Double.MIN_NORMAL));
+ expectNaN($opt$Rem(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY));
+ expectNaN($opt$Rem(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
+ }
+
+ static float $opt$Rem(float a, float b) {
+ return a % b;
+ }
+
+ static float $opt$RemConst(float a) {
+ return a % 4F;
+ }
+
+ static double $opt$Rem(double a, double b) {
+ return a % b;
+ }
+
+ static double $opt$RemConst(double a) {
+ return a % 4D;
+ }
+
+ public static void expectApproxEquals(float a, float b) {
+ float maxDelta = 0.00001F;
+ boolean aproxEquals = (a > b) ? ((a - b) < maxDelta) : ((b - a) < maxDelta);
+ if (!aproxEquals) {
+ throw new Error("Expected: " + a + ", found: " + b
+ + ", with delta: " + maxDelta + " " + (a - b));
+ }
+ }
+
+ public static void expectApproxEquals(double a, double b) {
+ double maxDelta = 0.00001D;
+ boolean aproxEquals = (a > b) ? ((a - b) < maxDelta) : ((b - a) < maxDelta);
+ if (!aproxEquals) {
+ throw new Error("Expected: " + a + ", found: "
+ + b + ", with delta: " + maxDelta + " " + (a - b));
+ }
+ }
+
+ public static void expectNaN(float a) {
+ if (a == a) {
+ throw new Error("Expected NaN: " + a);
+ }
+ }
+
+ public static void expectNaN(double a) {
+ if (a == a) {
+ throw new Error("Expected NaN: " + a);
+ }
+ }
+
+}
diff --git a/test/436-shift-constant/expected.txt b/test/436-shift-constant/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/436-shift-constant/expected.txt
diff --git a/test/436-shift-constant/info.txt b/test/436-shift-constant/info.txt
new file mode 100644
index 0000000..dc20646
--- /dev/null
+++ b/test/436-shift-constant/info.txt
@@ -0,0 +1 @@
+Regression tests for shift instructions and constants larger than 8bits.
diff --git a/test/436-shift-constant/src/Main.java b/test/436-shift-constant/src/Main.java
new file mode 100644
index 0000000..e69f64b
--- /dev/null
+++ b/test/436-shift-constant/src/Main.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ assertEquals(0x80000000, doShiftInt(1));
+ assertEquals(0x8000000000000000L, doShiftLong(1L));
+ }
+
+ public static int doShiftInt(int value) {
+ return value << 0xFFFF;
+ }
+
+ public static long doShiftLong(long value) {
+ return value << 0xFFFF;
+ }
+
+ public static void assertEquals(int a, int b) {
+ if (a != b) {
+ throw new Error("Expected " + a + ", got " + b);
+ }
+ }
+
+ public static void assertEquals(long a, long b) {
+ if (a != b) {
+ throw new Error("Expected " + a + ", got " + b);
+ }
+ }
+}
diff --git a/test/437-inline/expected.txt b/test/437-inline/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/437-inline/expected.txt
diff --git a/test/437-inline/info.txt b/test/437-inline/info.txt
new file mode 100644
index 0000000..6487a21
--- /dev/null
+++ b/test/437-inline/info.txt
@@ -0,0 +1 @@
+Tests inlining in the compiler.
diff --git a/test/437-inline/src/Main.java b/test/437-inline/src/Main.java
new file mode 100644
index 0000000..daabe4e
--- /dev/null
+++ b/test/437-inline/src/Main.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ if ($opt$inline$returnInt() != 4) {
+ throw new Error();
+ }
+
+ if ($opt$inline$returnParameter(42) != 42) {
+ throw new Error();
+ }
+
+ if ($opt$inline$returnWide() != 12L) {
+ throw new Error();
+ }
+
+ if ($opt$inline$returnWideParameter(0x100000001L) != 0x100000001L) {
+ throw new Error();
+ }
+
+ if ($opt$inline$returnReferenceParameter(Main.class) != Main.class) {
+ throw new Error();
+ }
+
+ $opt$inline$returnVoid();
+ $opt$inline$returnVoidWithOneParameter(32);
+
+ if ($opt$inline$returnAdd(42, 1) != 43) {
+ throw new Error();
+ }
+
+ if ($opt$inline$returnSub(42, 1) != 41) {
+ throw new Error();
+ }
+
+ // Some architectures used to not be able to allocate registers with
+ // floating point operations. This call is a regression test that we don't
+ // try inlining methods with floats in it on such architectures. The
+ // compiler used to crash after inlining a method it cannot allocate
+ // registers for.
+ tryInlineFloat();
+ }
+
+ public static int tryInlineFloat() {
+ return useFloatMethod();
+ }
+
+ public static float staticFloat = 42.0f;
+
+ public static int useFloatMethod() {
+ return (int)staticFloat;
+ }
+
+ public static int $opt$inline$returnParameter(int a) {
+ return a;
+ }
+
+ public static int $opt$inline$returnAdd(int a, int b) {
+ return a + b;
+ }
+
+ public static int $opt$inline$returnSub(int a, int b) {
+ return a - b;
+ }
+
+ public static int $opt$inline$returnInt() {
+ return 4;
+ }
+
+ public static long $opt$inline$returnWideParameter(long a) {
+ return a;
+ }
+
+ public static long $opt$inline$returnWide() {
+ return 12L;
+ }
+
+ public static Object $opt$inline$returnReferenceParameter(Object o) {
+ return o;
+ }
+
+ public static void $opt$inline$returnVoid() {
+ return;
+ }
+
+ public static void $opt$inline$returnVoidWithOneParameter(int a) {
+ return;
+ }
+}
diff --git a/test/438-volatile/expected.txt b/test/438-volatile/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/438-volatile/expected.txt
diff --git a/test/438-volatile/info.txt b/test/438-volatile/info.txt
new file mode 100644
index 0000000..7a4c81a
--- /dev/null
+++ b/test/438-volatile/info.txt
@@ -0,0 +1 @@
+Tests basic operations (set/get) on volatiles.
diff --git a/test/438-volatile/src/Main.java b/test/438-volatile/src/Main.java
new file mode 100644
index 0000000..a870e4c
--- /dev/null
+++ b/test/438-volatile/src/Main.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ static volatile long long_volatile;
+ static volatile double double_volatile;
+
+ public static void main(String[] args) {
+ checkVolatileUpdate(0L);
+ checkVolatileUpdate(Long.MAX_VALUE);
+ checkVolatileUpdate(Long.MIN_VALUE);
+
+ checkVolatileUpdate(0.0);
+ checkVolatileUpdate(Double.MAX_VALUE);
+ checkVolatileUpdate(-Double.MAX_VALUE);
+ }
+
+ public static long $opt$update(long a) {
+ long_volatile = a;
+ return long_volatile;
+ }
+
+ public static double $opt$update(double a) {
+ double_volatile = a;
+ return double_volatile;
+ }
+
+ public static void checkVolatileUpdate(long value) {
+ if (value != $opt$update(value)) {
+ throw new RuntimeException("Volatile update failed for long:" + value);
+ }
+ }
+
+ public static void checkVolatileUpdate(double value) {
+ if (value != $opt$update(value)) {
+ throw new RuntimeException("Volatile update failed for double:" + value);
+ }
+ }
+
+}
diff --git a/test/439-npe/expected.txt b/test/439-npe/expected.txt
new file mode 100644
index 0000000..271d40d
--- /dev/null
+++ b/test/439-npe/expected.txt
@@ -0,0 +1,18 @@
+$opt$setObjectField
+$opt$setIntField
+$opt$setFloatField
+$opt$setLongField
+$opt$setDoubleField
+$opt$setByteField
+$opt$setBooleanField
+$opt$setCharField
+$opt$setShortField
+$opt$getObjectField
+$opt$getIntField
+$opt$getFloatField
+$opt$getLongField
+$opt$getDoubleField
+$opt$getByteField
+$opt$getBooleanField
+$opt$getCharField
+$opt$getShortField
diff --git a/test/439-npe/info.txt b/test/439-npe/info.txt
new file mode 100644
index 0000000..d15ab2c
--- /dev/null
+++ b/test/439-npe/info.txt
@@ -0,0 +1,2 @@
+More tests for NullPointerExceptions to complement 122-npe.
+They check set/get to volatile fields.
diff --git a/test/439-npe/src/Main.java b/test/439-npe/src/Main.java
new file mode 100644
index 0000000..40c2645
--- /dev/null
+++ b/test/439-npe/src/Main.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ private volatile Object objectField;
+ private volatile int intField;
+ private volatile float floatField;
+ private volatile long longField;
+ private volatile double doubleField;
+ private volatile byte byteField;
+ private volatile boolean booleanField;
+ private volatile char charField;
+ private volatile short shortField;
+
+ public static void $opt$setObjectField(Main m) {
+ m.objectField = null;
+ }
+
+ public static void $opt$setIntField(Main m) {
+ m.intField = 0;
+ }
+
+ public static void $opt$setFloatField(Main m) {
+ m.floatField = 0;
+ }
+
+ public static void $opt$setLongField(Main m) {
+ m.longField = 0;
+ }
+
+ public static void $opt$setDoubleField(Main m) {
+ m.doubleField = 0;
+ }
+
+ public static void $opt$setByteField(Main m) {
+ m.byteField = 0;
+ }
+
+ public static void $opt$setBooleanField(Main m) {
+ m.booleanField = false;
+ }
+
+ public static void $opt$setCharField(Main m) {
+ m.charField = 0;
+ }
+
+ public static void $opt$setShortField(Main m) {
+ m.shortField = 0;
+ }
+
+ public static Object $opt$getObjectField(Main m) {
+ return m.objectField;
+ }
+
+ public static int $opt$getIntField(Main m) {
+ return m.intField;
+ }
+
+ public static float $opt$getFloatField(Main m) {
+ return m.floatField;
+ }
+
+ public static long $opt$getLongField(Main m) {
+ return m.longField;
+ }
+
+ public static double $opt$getDoubleField(Main m) {
+ return m.doubleField;
+ }
+
+ public static byte $opt$getByteField(Main m) {
+ return m.byteField;
+ }
+
+ public static boolean $opt$getBooleanField(Main m) {
+ return m.booleanField;
+ }
+
+ public static char $opt$getCharField(Main m) {
+ return m.charField;
+ }
+
+ public static short $opt$getShortField(Main m) {
+ return m.shortField;
+ }
+
+ public static void main(String[] args) {
+ int methodLine = 30;
+ int thisLine = 103;
+ try {
+ $opt$setObjectField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 2, methodLine, "$opt$setObjectField");
+ }
+ try {
+ $opt$setIntField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 6, methodLine += 4, "$opt$setIntField");
+ }
+ try {
+ $opt$setFloatField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 6, methodLine += 4, "$opt$setFloatField");
+ }
+ try {
+ $opt$setLongField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 6, methodLine += 4, "$opt$setLongField");
+ }
+ try {
+ $opt$setDoubleField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 6, methodLine += 4, "$opt$setDoubleField");
+ }
+ try {
+ $opt$setByteField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 6, methodLine += 4, "$opt$setByteField");
+ }
+ try {
+ $opt$setBooleanField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 6, methodLine += 4, "$opt$setBooleanField");
+ }
+ try {
+ $opt$setCharField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 6, methodLine += 4, "$opt$setCharField");
+ }
+ try {
+ $opt$setShortField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 6, methodLine += 4, "$opt$setShortField");
+ }
+ try {
+ $opt$getObjectField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 6, methodLine += 4, "$opt$getObjectField");
+ }
+ try {
+ $opt$getIntField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 6, methodLine += 4, "$opt$getIntField");
+ }
+ try {
+ $opt$getFloatField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 6, methodLine += 4, "$opt$getFloatField");
+ }
+ try {
+ $opt$getLongField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 6, methodLine += 4, "$opt$getLongField");
+ }
+ try {
+ $opt$getDoubleField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 6, methodLine += 4, "$opt$getDoubleField");
+ }
+ try {
+ $opt$getByteField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 6, methodLine += 4, "$opt$getByteField");
+ }
+ try {
+ $opt$getBooleanField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 6, methodLine += 4, "$opt$getBooleanField");
+ }
+ try {
+ $opt$getCharField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 6, methodLine += 4, "$opt$getCharField");
+ }
+ try {
+ $opt$getShortField(null);
+ throw new RuntimeException("Failed to throw NullPointerException.");
+ } catch (NullPointerException npe) {
+ check(npe, thisLine += 6, methodLine += 4, "$opt$getShortField");
+ }
+ }
+
+ static void check(NullPointerException npe, int mainLine, int medthodLine, String methodName) {
+ System.out.println(methodName);
+ StackTraceElement[] trace = npe.getStackTrace();
+ checkElement(trace[0], "Main", methodName, "Main.java", medthodLine);
+ checkElement(trace[1], "Main", "main", "Main.java", mainLine);
+ }
+
+ static void checkElement(StackTraceElement element,
+ String declaringClass, String methodName,
+ String fileName, int lineNumber) {
+ assertEquals(declaringClass, element.getClassName());
+ assertEquals(methodName, element.getMethodName());
+ assertEquals(fileName, element.getFileName());
+ assertEquals(lineNumber, element.getLineNumber());
+ }
+
+ static void assertEquals(Object expected, Object actual) {
+ if (!expected.equals(actual)) {
+ String msg = "Expected \"" + expected + "\" but got \"" + actual + "\"";
+ throw new AssertionError(msg);
+ }
+ }
+
+ static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError("Expected " + expected + " got " + actual);
+ }
+ }
+
+}
diff --git a/test/439-swap-double/expected.txt b/test/439-swap-double/expected.txt
new file mode 100644
index 0000000..019c901
--- /dev/null
+++ b/test/439-swap-double/expected.txt
@@ -0,0 +1,4 @@
+-26.0
+-24.0
+-22.0
+-20.0
diff --git a/test/439-swap-double/info.txt b/test/439-swap-double/info.txt
new file mode 100644
index 0000000..23447d2
--- /dev/null
+++ b/test/439-swap-double/info.txt
@@ -0,0 +1,2 @@
+Test for the optimizing compiler's parallel swap support in
+the presence of register pairs (in this case, doubles on ARM).
diff --git a/test/439-swap-double/src/Main.java b/test/439-swap-double/src/Main.java
new file mode 100644
index 0000000..da11577
--- /dev/null
+++ b/test/439-swap-double/src/Main.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Test for the optimizing compiler's parallel swap support in
+// the presence of register pairs (in this case, doubles on ARM).
+public class Main {
+ public static void main(String[] args) {
+ new Main().foo();
+ }
+
+ public void foo() {
+ // Do multiple calls to force swapping of registers. Note that
+ // this depends on the calling convention, as a stack-only convention
+ // may not need the swapping.
+ callWithDoubles(a, b, c, d, e, f, g);
+ callWithDoubles(b, c, d, e, f, g, a);
+ callWithDoubles(c, d, e, f, g, a, b);
+ callWithDoubles(d, e, f, g, a, b, c);
+ }
+
+ public static void callWithDoubles(
+ double a, double b, double c, double d, double e, double f, double g) {
+ System.out.println(a - b - c - d - e - f - g);
+ }
+
+ double a = 1.0;
+ double b = 2.0;
+ double c = 3.0;
+ double d = 4.0;
+ double e = 5.0;
+ double f = 6.0;
+ double g = 7.0;
+}
diff --git a/test/440-stmp/expected.txt b/test/440-stmp/expected.txt
new file mode 100644
index 0000000..e995b05
--- /dev/null
+++ b/test/440-stmp/expected.txt
@@ -0,0 +1 @@
+-118.0
diff --git a/test/440-stmp/info.txt b/test/440-stmp/info.txt
new file mode 100644
index 0000000..c4a7bf1
--- /dev/null
+++ b/test/440-stmp/info.txt
@@ -0,0 +1,3 @@
+Regression test for optimizing, that used to consider
+a S/D register a temp, while it conflicted with the
+hard-float calling convention.
diff --git a/test/440-stmp/src/Main.java b/test/440-stmp/src/Main.java
new file mode 100644
index 0000000..2dd10f8
--- /dev/null
+++ b/test/440-stmp/src/Main.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+class Main {
+ public static void main(String[] args) {
+ new Main().bar();
+ }
+
+ public void bar() {
+ // Use up all available D registers on ARM.
+ baz(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
+ }
+
+ public static void baz(float a, float b, float c, float d, float e, float f, float g,
+ float h, float i, float j, float k, float l, float m, float n, float o) {
+ System.out.println(a - b - c - d - e - f - g - h - i - j - k - l - m - n - o);
+ }
+
+ float a = 1.0f;
+ float b = 2.0f;
+ float c = 3.0f;
+ float d = 4.0f;
+ float e = 5.0f;
+ float f = 6.0f;
+ float g = 7.0f;
+ float h = 8.0f;
+ float i = 9.0f;
+ float j = 10.0f;
+ float k = 11.0f;
+ float l = 12.0f;
+ float m = 13.0f;
+ float n = 14.0f;
+ float o = 15.0f;
+}
diff --git a/test/441-checker-inliner/expected.txt b/test/441-checker-inliner/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/441-checker-inliner/expected.txt
diff --git a/test/441-checker-inliner/info.txt b/test/441-checker-inliner/info.txt
new file mode 100644
index 0000000..66a3270
--- /dev/null
+++ b/test/441-checker-inliner/info.txt
@@ -0,0 +1 @@
+Tests inlining in the optimizing compiler.
diff --git a/test/441-checker-inliner/src/Main.java b/test/441-checker-inliner/src/Main.java
new file mode 100644
index 0000000..631b140
--- /dev/null
+++ b/test/441-checker-inliner/src/Main.java
@@ -0,0 +1,242 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public class Main {
+
+ // CHECK-START: void Main.InlineVoid() inliner (before)
+ // CHECK-DAG: [[Const42:i\d+]] IntConstant 42
+ // CHECK-DAG: InvokeStaticOrDirect
+ // CHECK-DAG: InvokeStaticOrDirect [ [[Const42]] ]
+
+ // CHECK-START: void Main.InlineVoid() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ public static void InlineVoid() {
+ returnVoid();
+ returnVoidWithOneParameter(42);
+ }
+
+ // CHECK-START: int Main.InlineParameter(int) inliner (before)
+ // CHECK-DAG: [[Param:i\d+]] ParameterValue
+ // CHECK-DAG: [[Result:i\d+]] InvokeStaticOrDirect [ [[Param]] ]
+ // CHECK-DAG: Return [ [[Result]] ]
+
+ // CHECK-START: int Main.InlineParameter(int) inliner (after)
+ // CHECK-DAG: [[Param:i\d+]] ParameterValue
+ // CHECK-DAG: Return [ [[Param]] ]
+
+ public static int InlineParameter(int a) {
+ return returnParameter(a);
+ }
+
+ // CHECK-START: long Main.InlineWideParameter(long) inliner (before)
+ // CHECK-DAG: [[Param:j\d+]] ParameterValue
+ // CHECK-DAG: [[Result:j\d+]] InvokeStaticOrDirect [ [[Param]] ]
+ // CHECK-DAG: Return [ [[Result]] ]
+
+ // CHECK-START: long Main.InlineWideParameter(long) inliner (after)
+ // CHECK-DAG: [[Param:j\d+]] ParameterValue
+ // CHECK-DAG: Return [ [[Param]] ]
+
+ public static long InlineWideParameter(long a) {
+ return returnWideParameter(a);
+ }
+
+ // CHECK-START: java.lang.Object Main.InlineReferenceParameter(java.lang.Object) inliner (before)
+ // CHECK-DAG: [[Param:l\d+]] ParameterValue
+ // CHECK-DAG: [[Result:l\d+]] InvokeStaticOrDirect [ [[Param]] ]
+ // CHECK-DAG: Return [ [[Result]] ]
+
+ // CHECK-START: java.lang.Object Main.InlineReferenceParameter(java.lang.Object) inliner (after)
+ // CHECK-DAG: [[Param:l\d+]] ParameterValue
+ // CHECK-DAG: Return [ [[Param]] ]
+
+ public static Object InlineReferenceParameter(Object o) {
+ return returnReferenceParameter(o);
+ }
+
+ // CHECK-START: int Main.InlineInt() inliner (before)
+ // CHECK-DAG: [[Result:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Result]] ]
+
+ // CHECK-START: int Main.InlineInt() inliner (after)
+ // CHECK-DAG: [[Const4:i\d+]] IntConstant 4
+ // CHECK-DAG: Return [ [[Const4]] ]
+
+ public static int InlineInt() {
+ return returnInt();
+ }
+
+ // CHECK-START: long Main.InlineWide() inliner (before)
+ // CHECK-DAG: [[Result:j\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Result]] ]
+
+ // CHECK-START: long Main.InlineWide() inliner (after)
+ // CHECK-DAG: [[Const8:j\d+]] LongConstant 8
+ // CHECK-DAG: Return [ [[Const8]] ]
+
+ public static long InlineWide() {
+ return returnWide();
+ }
+
+ // CHECK-START: int Main.InlineAdd() inliner (before)
+ // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
+ // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
+ // CHECK-DAG: [[Result:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Result]] ]
+
+ // CHECK-START: int Main.InlineAdd() inliner (after)
+ // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
+ // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
+ // CHECK-DAG: [[Add:i\d+]] Add [ [[Const3]] [[Const5]] ]
+ // CHECK-DAG: Return [ [[Add]] ]
+
+ public static int InlineAdd() {
+ return returnAdd(3, 5);
+ }
+
+ // CHECK-START: int Main.InlineFieldAccess() inliner (before)
+ // CHECK-DAG: [[After:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[After]] ]
+
+ // CHECK-START: int Main.InlineFieldAccess() inliner (after)
+ // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
+ // CHECK-DAG: [[Before:i\d+]] StaticFieldGet
+ // CHECK-DAG: [[After:i\d+]] Add [ [[Before]] [[Const1]] ]
+ // CHECK-DAG: StaticFieldSet [ {{l\d+}} [[After]] ]
+ // CHECK-DAG: Return [ [[After]] ]
+
+ // CHECK-START: int Main.InlineFieldAccess() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ public static int InlineFieldAccess() {
+ return incCounter();
+ }
+
+ // CHECK-START: int Main.InlineWithControlFlow(boolean) inliner (before)
+ // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
+ // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
+ // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
+ // CHECK-DAG: [[Add:i\d+]] InvokeStaticOrDirect [ [[Const1]] [[Const3]] ]
+ // CHECK-DAG: [[Sub:i\d+]] InvokeStaticOrDirect [ [[Const5]] [[Const3]] ]
+ // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Add]] [[Sub]] ]
+ // CHECK-DAG: Return [ [[Phi]] ]
+
+ // CHECK-START: int Main.InlineWithControlFlow(boolean) inliner (after)
+ // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
+ // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
+ // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
+ // CHECK-DAG: [[Add:i\d+]] Add [ [[Const1]] [[Const3]] ]
+ // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Const5]] [[Const3]] ]
+ // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Add]] [[Sub]] ]
+ // CHECK-DAG: Return [ [[Phi]] ]
+
+ public static int InlineWithControlFlow(boolean cond) {
+ int x, const1, const3, const5;
+ const1 = 1;
+ const3 = 3;
+ const5 = 5;
+ if (cond) {
+ x = returnAdd(const1, const3);
+ } else {
+ x = returnSub(const5, const3);
+ }
+ return x;
+ }
+
+
+ private static void returnVoid() {
+ return;
+ }
+
+ private static void returnVoidWithOneParameter(int a) {
+ return;
+ }
+
+ private static int returnParameter(int a) {
+ return a;
+ }
+
+ private static long returnWideParameter(long a) {
+ return a;
+ }
+
+ private static Object returnReferenceParameter(Object o) {
+ return o;
+ }
+
+ private static int returnInt() {
+ return 4;
+ }
+
+ private static long returnWide() {
+ return 8L;
+ }
+
+ private static int returnAdd(int a, int b) {
+ return a + b;
+ }
+
+ private static int returnSub(int a, int b) {
+ return a - b;
+ }
+
+ private static int counter = 42;
+
+ private static int incCounter() {
+ return ++counter;
+ }
+
+ public static void main(String[] args) {
+ InlineVoid();
+
+ if (InlineInt() != 4) {
+ throw new Error();
+ }
+
+ if (InlineWide() != 8L) {
+ throw new Error();
+ }
+
+ if (InlineParameter(42) != 42) {
+ throw new Error();
+ }
+
+ if (InlineWideParameter(0x100000001L) != 0x100000001L) {
+ throw new Error();
+ }
+
+ if (InlineReferenceParameter(Main.class) != Main.class) {
+ throw new Error();
+ }
+
+ if (InlineAdd() != 8) {
+ throw new Error();
+ }
+
+ if (InlineFieldAccess() != 43 || InlineFieldAccess() != 44) {
+ throw new Error();
+ }
+
+ if (InlineWithControlFlow(true) != 4) {
+ throw new Error();
+ }
+
+ if (InlineWithControlFlow(false) != 2) {
+ throw new Error();
+ }
+ }
+}
diff --git a/test/442-checker-constant-folding/expected.txt b/test/442-checker-constant-folding/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/442-checker-constant-folding/expected.txt
diff --git a/test/442-checker-constant-folding/info.txt b/test/442-checker-constant-folding/info.txt
new file mode 100644
index 0000000..5073972
--- /dev/null
+++ b/test/442-checker-constant-folding/info.txt
@@ -0,0 +1 @@
+Tests constant folding in the optimizing compiler.
diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java
new file mode 100644
index 0000000..de2c5c7
--- /dev/null
+++ b/test/442-checker-constant-folding/src/Main.java
@@ -0,0 +1,259 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public class Main {
+
+ /**
+ * Tiny three-register program exercising int constant folding
+ * on negation.
+ */
+
+ // CHECK-START: int Main.IntNegation() constant_folding (before)
+ // CHECK-DAG: [[Const42:i\d+]] IntConstant 42
+ // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Const42]] ]
+ // CHECK-DAG: Return [ [[Neg]] ]
+
+ // CHECK-START: int Main.IntNegation() constant_folding (after)
+ // CHECK-DAG: [[ConstN42:i\d+]] IntConstant -42
+ // CHECK-DAG: Return [ [[ConstN42]] ]
+
+ public static int IntNegation() {
+ int x, y;
+ x = 42;
+ y = -x;
+ return y;
+ }
+
+ /**
+ * Tiny three-register program exercising int constant folding
+ * on addition.
+ */
+
+ // CHECK-START: int Main.IntAddition1() constant_folding (before)
+ // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
+ // CHECK-DAG: [[Const2:i\d+]] IntConstant 2
+ // CHECK-DAG: [[Add:i\d+]] Add [ [[Const1]] [[Const2]] ]
+ // CHECK-DAG: Return [ [[Add]] ]
+
+ // CHECK-START: int Main.IntAddition1() constant_folding (after)
+ // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
+ // CHECK-DAG: Return [ [[Const3]] ]
+
+ public static int IntAddition1() {
+ int a, b, c;
+ a = 1;
+ b = 2;
+ c = a + b;
+ return c;
+ }
+
+ /**
+ * Small three-register program exercising int constant folding
+ * on addition.
+ */
+
+ // CHECK-START: int Main.IntAddition2() constant_folding (before)
+ // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
+ // CHECK-DAG: [[Const2:i\d+]] IntConstant 2
+ // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
+ // CHECK-DAG: [[Const6:i\d+]] IntConstant 6
+ // CHECK-DAG: [[Add1:i\d+]] Add [ [[Const1]] [[Const2]] ]
+ // CHECK-DAG: [[Add2:i\d+]] Add [ [[Const5]] [[Const6]] ]
+ // CHECK-DAG: [[Add3:i\d+]] Add [ [[Add1]] [[Add2]] ]
+ // CHECK-DAG: Return [ [[Add3]] ]
+
+ // CHECK-START: int Main.IntAddition2() constant_folding (after)
+ // CHECK-DAG: [[Const14:i\d+]] IntConstant 14
+ // CHECK-DAG: Return [ [[Const14]] ]
+
+ public static int IntAddition2() {
+ int a, b, c;
+ a = 1;
+ b = 2;
+ a += b;
+ b = 5;
+ c = 6;
+ b += c;
+ c = a + b;
+ return c;
+ }
+
+ /**
+ * Tiny three-register program exercising int constant folding
+ * on subtraction.
+ */
+
+ // CHECK-START: int Main.IntSubtraction() constant_folding (before)
+ // CHECK-DAG: [[Const6:i\d+]] IntConstant 6
+ // CHECK-DAG: [[Const2:i\d+]] IntConstant 2
+ // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Const6]] [[Const2]] ]
+ // CHECK-DAG: Return [ [[Sub]] ]
+
+ // CHECK-START: int Main.IntSubtraction() constant_folding (after)
+ // CHECK-DAG: [[Const4:i\d+]] IntConstant 4
+ // CHECK-DAG: Return [ [[Const4]] ]
+
+ public static int IntSubtraction() {
+ int a, b, c;
+ a = 6;
+ b = 2;
+ c = a - b;
+ return c;
+ }
+
+ /**
+ * Tiny three-register program exercising long constant folding
+ * on addition.
+ */
+
+ // CHECK-START: long Main.LongAddition() constant_folding (before)
+ // CHECK-DAG: [[Const1:j\d+]] LongConstant 1
+ // CHECK-DAG: [[Const2:j\d+]] LongConstant 2
+ // CHECK-DAG: [[Add:j\d+]] Add [ [[Const1]] [[Const2]] ]
+ // CHECK-DAG: Return [ [[Add]] ]
+
+ // CHECK-START: long Main.LongAddition() constant_folding (after)
+ // CHECK-DAG: [[Const3:j\d+]] LongConstant 3
+ // CHECK-DAG: Return [ [[Const3]] ]
+
+ public static long LongAddition() {
+ long a, b, c;
+ a = 1L;
+ b = 2L;
+ c = a + b;
+ return c;
+ }
+
+ /**
+ * Tiny three-register program exercising long constant folding
+ * on subtraction.
+ */
+
+ // CHECK-START: long Main.LongSubtraction() constant_folding (before)
+ // CHECK-DAG: [[Const6:j\d+]] LongConstant 6
+ // CHECK-DAG: [[Const2:j\d+]] LongConstant 2
+ // CHECK-DAG: [[Sub:j\d+]] Sub [ [[Const6]] [[Const2]] ]
+ // CHECK-DAG: Return [ [[Sub]] ]
+
+ // CHECK-START: long Main.LongSubtraction() constant_folding (after)
+ // CHECK-DAG: [[Const4:j\d+]] LongConstant 4
+ // CHECK-DAG: Return [ [[Const4]] ]
+
+ public static long LongSubtraction() {
+ long a, b, c;
+ a = 6L;
+ b = 2L;
+ c = a - b;
+ return c;
+ }
+
+ /**
+ * Three-register program with a constant (static) condition.
+ */
+
+ // CHECK-START: int Main.StaticCondition() constant_folding (before)
+ // CHECK-DAG: [[Const7:i\d+]] IntConstant 7
+ // CHECK-DAG: [[Const2:i\d+]] IntConstant 2
+ // CHECK-DAG: [[Cond:z\d+]] GreaterThanOrEqual [ [[Const7]] [[Const2]] ]
+ // CHECK-DAG: If [ [[Cond]] ]
+
+ // CHECK-START: int Main.StaticCondition() constant_folding (after)
+ // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
+ // CHECK-DAG: If [ [[Const1]] ]
+
+ public static int StaticCondition() {
+ int a, b, c;
+ a = 7;
+ b = 2;
+ if (a < b)
+ c = a + b;
+ else
+ c = a - b;
+ return c;
+ }
+
+ /**
+ * Four-variable program with jumps leading to the creation of many
+ * blocks.
+ *
+ * The intent of this test is to ensure that all constant expressions
+ * are actually evaluated at compile-time, thanks to the reverse
+ * (forward) post-order traversal of the the dominator tree.
+ */
+
+ // CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (before)
+ // CHECK-DAG: [[Const2:i\d+]] IntConstant 2
+ // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
+ // CHECK-DAG: [[Add:i\d+]] Add [ [[Const5]] [[Const2]] ]
+ // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Const5]] [[Const2]] ]
+ // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Add]] [[Sub]] ]
+ // CHECK-DAG: Return [ [[Phi]] ]
+
+ // CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (after)
+ // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
+ // CHECK-DAG: [[Const7:i\d+]] IntConstant 7
+ // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const7]] [[Const3]] ]
+ // CHECK-DAG: Return [ [[Phi]] ]
+
+ public static int JumpsAndConditionals(boolean cond) {
+ int a, b, c;
+ a = 5;
+ b = 2;
+ if (cond)
+ c = a + b;
+ else
+ c = a - b;
+ return c;
+ }
+
+ public static void main(String[] args) {
+ if (IntNegation() != -42) {
+ throw new Error();
+ }
+
+ if (IntAddition1() != 3) {
+ throw new Error();
+ }
+
+ if (IntAddition2() != 14) {
+ throw new Error();
+ }
+
+ if (IntSubtraction() != 4) {
+ throw new Error();
+ }
+
+ if (LongAddition() != 3L) {
+ throw new Error();
+ }
+
+ if (LongSubtraction() != 4L) {
+ throw new Error();
+ }
+
+ if (StaticCondition() != 5) {
+ throw new Error();
+ }
+
+ if (JumpsAndConditionals(true) != 7) {
+ throw new Error();
+ }
+
+ if (JumpsAndConditionals(false) != 3) {
+ throw new Error();
+ }
+ }
+}
diff --git a/test/443-not-bool-inline/expected.txt b/test/443-not-bool-inline/expected.txt
new file mode 100644
index 0000000..3ee3849
--- /dev/null
+++ b/test/443-not-bool-inline/expected.txt
@@ -0,0 +1 @@
+Hello World 2
diff --git a/test/443-not-bool-inline/info.txt b/test/443-not-bool-inline/info.txt
new file mode 100644
index 0000000..31f2321
--- /dev/null
+++ b/test/443-not-bool-inline/info.txt
@@ -0,0 +1,2 @@
+Regression test for optimizing, who used a wrong instruction
+for simplifying Equals(foo, false);
diff --git a/test/443-not-bool-inline/src/Main.java b/test/443-not-bool-inline/src/Main.java
new file mode 100644
index 0000000..3a6f3be
--- /dev/null
+++ b/test/443-not-bool-inline/src/Main.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ // For some reason, dx wants != for generating if-eq.
+ if (falseField != false) {
+ System.out.println("Hello World 1");
+ }
+
+ if (trueField != false) {
+ System.out.println("Hello World 2");
+ }
+ }
+
+ static boolean falseField = false;
+ static boolean trueField = true;
+}
diff --git a/test/444-checker-nce/expected.txt b/test/444-checker-nce/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/444-checker-nce/expected.txt
diff --git a/test/444-checker-nce/info.txt b/test/444-checker-nce/info.txt
new file mode 100644
index 0000000..78ad0b9
--- /dev/null
+++ b/test/444-checker-nce/info.txt
@@ -0,0 +1 @@
+Tests for NullCheck elimination.
diff --git a/test/444-checker-nce/src/Main.java b/test/444-checker-nce/src/Main.java
new file mode 100644
index 0000000..9fb9c46
--- /dev/null
+++ b/test/444-checker-nce/src/Main.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ // CHECK-START: Main Main.keepTest(Main) instruction_simplifier_after_types (before)
+ // CHECK: NullCheck
+ // CHECK: InvokeStaticOrDirect
+
+ // CHECK-START: Main Main.keepTest(Main) instruction_simplifier_after_types (after)
+ // CHECK: NullCheck
+ // CHECK: InvokeStaticOrDirect
+ public Main keepTest(Main m) {
+ return m.g();
+ }
+
+ // CHECK-START: Main Main.thisTest() instruction_simplifier (before)
+ // CHECK: NullCheck
+ // CHECK: InvokeStaticOrDirect
+
+ // CHECK-START: Main Main.thisTest() instruction_simplifier (after)
+ // CHECK-NOT: NullCheck
+ // CHECK: InvokeStaticOrDirect
+ public Main thisTest() {
+ return g();
+ }
+
+ // CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier (before)
+ // CHECK: NewInstance
+ // CHECK: NullCheck
+ // CHECK: InvokeStaticOrDirect
+ // CHECK: NullCheck
+ // CHECK: InvokeStaticOrDirect
+
+ // CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier (after)
+ // CHECK-NOT: NullCheck
+ public Main newInstanceRemoveTest() {
+ Main m = new Main();
+ return m.g();
+ }
+
+ // CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier (before)
+ // CHECK: NewArray
+ // CHECK: NullCheck
+ // CHECK: ArrayGet
+
+ // CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier (after)
+ // CHECK: NewArray
+ // CHECK-NOT: NullCheck
+ // CHECK: ArrayGet
+ public Main newArrayRemoveTest() {
+ Main[] ms = new Main[1];
+ return ms[0];
+ }
+
+ // CHECK-START: Main Main.ifRemoveTest(boolean) instruction_simplifier_after_types (before)
+ // CHECK: NewInstance
+ // CHECK: NullCheck
+
+ // CHECK-START: Main Main.ifRemoveTest(boolean) instruction_simplifier_after_types (after)
+ // CHECK: NewInstance
+ // CHECK-NOT: NullCheck
+ public Main ifRemoveTest(boolean flag) {
+ Main m = null;
+ if (flag) {
+ m = new Main();
+ } else {
+ m = new Main(1);
+ }
+ return m.g();
+ }
+
+ // CHECK-START: Main Main.ifKeepTest(boolean) instruction_simplifier_after_types (before)
+ // CHECK: NewInstance
+ // CHECK: NullCheck
+
+ // CHECK-START: Main Main.ifKeepTest(boolean) instruction_simplifier_after_types (after)
+ // CHECK: NewInstance
+ // CHECK: NullCheck
+ public Main ifKeepTest(boolean flag) {
+ Main m = null;
+ if (flag) {
+ m = new Main(1);
+ }
+ return m.g();
+ }
+
+ // CHECK-START: Main Main.forRemoveTest(int) instruction_simplifier_after_types (before)
+ // CHECK: NullCheck
+
+ // CHECK-START: Main Main.forRemoveTest(int) instruction_simplifier_after_types (after)
+ // CHECK-NOT: NullCheck
+ public Main forRemoveTest(int count) {
+ Main a = new Main();
+ Main m = new Main();
+ for (int i = 0; i < count; i++) {
+ if (i % 2 == 0) {
+ m = a;
+ }
+ }
+ return m.g();
+ }
+
+ // CHECK-START: Main Main.forKeepTest(int) instruction_simplifier_after_types (before)
+ // CHECK: NullCheck
+
+ // CHECK-START: Main Main.forKeepTest(int) instruction_simplifier_after_types (after)
+ // CHECK: NullCheck
+ public Main forKeepTest(int count) {
+ Main a = new Main();
+ Main m = new Main();
+ for (int i = 0; i < count; i++) {
+ if (i % 2 == 0) {
+ m = a;
+ } else {
+ m = null;
+ }
+ }
+ return m.g();
+ }
+
+ // CHECK-START: Main Main.phiFlowRemoveTest(int) instruction_simplifier_after_types (before)
+ // CHECK: NullCheck
+
+ // CHECK-START: Main Main.phiFlowRemoveTest(int) instruction_simplifier_after_types (after)
+ // CHECK-NOT: NullCheck
+ public Main phiFlowRemoveTest(int count) {
+ Main a = new Main();
+ Main m = new Main();
+ for (int i = 0; i < count; i++) {
+ if (i % 2 == 0) {
+ m = a;
+ }
+ }
+ Main n = new Main();
+ for (int i = 0; i < count; i++) {
+ if (i % 3 == 0) {
+ n = m;
+ }
+ }
+ return n.g();
+ }
+
+ // CHECK-START: Main Main.phiFlowKeepTest(int) instruction_simplifier_after_types (before)
+ // CHECK: NullCheck
+
+ // CHECK-START: Main Main.phiFlowKeepTest(int) instruction_simplifier_after_types (after)
+ // CHECK: NullCheck
+ public Main phiFlowKeepTest(int count) {
+ Main a = new Main();
+ Main m = new Main();
+ for (int i = 0; i < count; i++) {
+ if (i % 2 == 0) {
+ m = a;
+ } else {
+ m = null;
+ }
+ }
+ Main n = new Main();
+ for (int i = 0; i < count; i++) {
+ if (i % 3 == 0) {
+ n = m;
+ }
+ }
+ return n.g();
+ }
+
+ // CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier (before)
+ // CHECK: NullCheck
+
+ // CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier (after)
+ // CHECK-NOT: NullCheck
+ public Main scopeRemoveTest(int count, Main a) {
+ Main m = null;
+ for (int i = 0; i < count; i++) {
+ if (i % 2 == 0) {
+ m = new Main();
+ m.g();
+ } else {
+ m = a;
+ }
+ }
+ return m;
+ }
+
+ // CHECK-START: Main Main.scopeKeepTest(int, Main) instruction_simplifier_after_types (before)
+ // CHECK: NullCheck
+
+ // CHECK-START: Main Main.scopeKeepTest(int, Main) instruction_simplifier_after_types (after)
+ // CHECK: NullCheck
+ public Main scopeKeepTest(int count, Main a) {
+ Main m = new Main();
+ for (int i = 0; i < count; i++) {
+ if (i % 2 == 0) {
+ m = a;
+ } else {
+ m = a;
+ m.g();
+ }
+ }
+ return m;
+ }
+
+ public Main() {}
+ public Main(int dummy) {}
+
+ private Main g() {
+ // avoids inlining
+ throw new RuntimeException();
+ }
+
+ public static void main(String[] args) {
+ new Main();
+ }
+
+}
diff --git a/test/445-checker-licm/expected.txt b/test/445-checker-licm/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/445-checker-licm/expected.txt
diff --git a/test/445-checker-licm/info.txt b/test/445-checker-licm/info.txt
new file mode 100644
index 0000000..e09d958
--- /dev/null
+++ b/test/445-checker-licm/info.txt
@@ -0,0 +1 @@
+Checker test for testing loop invariant code motion.
diff --git a/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java
new file mode 100644
index 0000000..91ac2ed
--- /dev/null
+++ b/test/445-checker-licm/src/Main.java
@@ -0,0 +1,123 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public class Main {
+
+ // CHECK-START: int Main.div() licm (before)
+ // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+
+ // CHECK-START: int Main.div() licm (after)
+ // CHECK-NOT: Div ( loop_header:{{B\d+}} )
+
+ // CHECK-START: int Main.div() licm (after)
+ // CHECK-DAG: Div ( loop_header:null )
+
+ public static int div() {
+ int result = 0;
+ for (int i = 0; i < 10; ++i) {
+ result += staticField / 42;
+ }
+ return result;
+ }
+
+ // CHECK-START: int Main.innerDiv() licm (before)
+ // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+
+ // CHECK-START: int Main.innerDiv() licm (after)
+ // CHECK-NOT: Div ( loop_header:{{B\d+}} )
+
+ // CHECK-START: int Main.innerDiv() licm (after)
+ // CHECK-DAG: Div ( loop_header:null )
+
+ public static int innerDiv() {
+ int result = 0;
+ for (int i = 0; i < 10; ++i) {
+ for (int j = 0; j < 10; ++j) {
+ result += staticField / 42;
+ }
+ }
+ return result;
+ }
+
+ // CHECK-START: int Main.innerDiv2() licm (before)
+ // CHECK-DAG: Mul ( loop_header:{{B4}} )
+
+ // CHECK-START: int Main.innerDiv2() licm (after)
+ // CHECK-DAG: Mul ( loop_header:{{B2}} )
+
+ public static int innerDiv2() {
+ int result = 0;
+ for (int i = 0; i < 10; ++i) {
+ for (int j = 0; j < 10; ++j) {
+ // The operation has been hoisted out of the inner loop.
+ // Note that we depend on the compiler's block numbering to
+ // check if it has been moved.
+ result += staticField * i;
+ }
+ }
+ return result;
+ }
+
+ // CHECK-START: int Main.innerDiv3(int, int) licm (before)
+ // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+
+ // CHECK-START: int Main.innerDiv3(int, int) licm (after)
+ // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+
+ public static int innerDiv3(int a, int b) {
+ int result = 0;
+ while (b < 5) {
+ // a might be null, so we can't hoist the operation.
+ result += staticField / a;
+ b++;
+ }
+ return result;
+ }
+
+ // CHECK-START: int Main.arrayLength(int[]) licm (before)
+ // CHECK-DAG: [[NullCheck:l\d+]] NullCheck ( loop_header:{{B\d+}} )
+ // CHECK-DAG: ArrayLength [ [[NullCheck]] ] ( loop_header:{{B\d+}} )
+
+ // CHECK-START: int Main.arrayLength(int[]) licm (after)
+ // CHECK-NOT: NullCheck ( loop_header:{{B\d+}} )
+ // CHECK-NOT: ArrayLength ( loop_header:{{B\d+}} )
+
+ // CHECK-START: int Main.arrayLength(int[]) licm (after)
+ // CHECK-DAG: [[NullCheck:l\d+]] NullCheck ( loop_header:null )
+ // CHECK-DAG: ArrayLength [ [[NullCheck]] ] ( loop_header:null )
+
+ public static int arrayLength(int[] array) {
+ int result = 0;
+ for (int i = 0; i < array.length; ++i) {
+ result += array[i];
+ }
+ return result;
+ }
+
+ public static int staticField = 42;
+
+ public static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void main(String[] args) {
+ assertEquals(10, div());
+ assertEquals(100, innerDiv());
+ assertEquals(12, arrayLength(new int[] { 4, 8 }));
+ }
+}
diff --git a/test/446-checker-inliner2/expected.txt b/test/446-checker-inliner2/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/446-checker-inliner2/expected.txt
diff --git a/test/446-checker-inliner2/info.txt b/test/446-checker-inliner2/info.txt
new file mode 100644
index 0000000..66a3270
--- /dev/null
+++ b/test/446-checker-inliner2/info.txt
@@ -0,0 +1 @@
+Tests inlining in the optimizing compiler.
diff --git a/test/446-checker-inliner2/src/Main.java b/test/446-checker-inliner2/src/Main.java
new file mode 100644
index 0000000..ecf071e
--- /dev/null
+++ b/test/446-checker-inliner2/src/Main.java
@@ -0,0 +1,72 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public class Main {
+
+ // CHECK-START: int Main.inlineInstanceCall(Main) inliner (before)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: int Main.inlineInstanceCall(Main) inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ // CHECK-START: int Main.inlineInstanceCall(Main) inliner (after)
+ // CHECK-DAG: [[Field:i\d+]] InstanceFieldGet
+ // CHECK-DAG: Return [ [[Field]] ]
+
+ public static int inlineInstanceCall(Main m) {
+ return m.foo();
+ }
+
+ private int foo() {
+ return field;
+ }
+
+ int field = 42;
+
+ // CHECK-START: int Main.inlineNestedCall() inliner (before)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: int Main.inlineNestedCall() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ // CHECK-START: int Main.inlineNestedCall() inliner (after)
+ // CHECK-DAG: [[Const38:i\d+]] IntConstant 38
+ // CHECK-DAG: Return [ [[Const38]] ]
+
+ public static int inlineNestedCall() {
+ return nestedCall();
+ }
+
+ public static int nestedCall() {
+ return bar();
+ }
+
+ public static int bar() {
+ return 38;
+ }
+
+ public static void main(String[] args) {
+ if (inlineInstanceCall(new Main()) != 42) {
+ throw new Error("Expected 42");
+ }
+
+ if (inlineNestedCall() != 38) {
+ throw new Error("Expected 38");
+ }
+ }
+}
diff --git a/test/447-checker-inliner3/expected.txt b/test/447-checker-inliner3/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/447-checker-inliner3/expected.txt
diff --git a/test/447-checker-inliner3/info.txt b/test/447-checker-inliner3/info.txt
new file mode 100644
index 0000000..66a3270
--- /dev/null
+++ b/test/447-checker-inliner3/info.txt
@@ -0,0 +1 @@
+Tests inlining in the optimizing compiler.
diff --git a/test/447-checker-inliner3/src/Main.java b/test/447-checker-inliner3/src/Main.java
new file mode 100644
index 0000000..db4b236
--- /dev/null
+++ b/test/447-checker-inliner3/src/Main.java
@@ -0,0 +1,77 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public class Main {
+
+ // CHECK-START: int Main.inlineIfThenElse() inliner (before)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: int Main.inlineIfThenElse() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ public static int inlineIfThenElse() {
+ return foo(true);
+ }
+
+ private static int foo(boolean value) {
+ if (value) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ // CHECK-START: int Main.inlineInLoop() inliner (before)
+ // CHECK-DAG: InvokeStaticOrDirect
+
+ // CHECK-START: int Main.inlineInLoop() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ public static int inlineInLoop() {
+ int result = 0;
+ for (int i = 0; i < 32; ++i) {
+ result += foo(i % 2 == 0);
+ }
+ return result;
+ }
+
+ // CHECK-START: int Main.inlineInLoopHeader() inliner (before)
+ // CHECK-DAG: InvokeStaticOrDirect
+
+ // CHECK-START: int Main.inlineInLoopHeader() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ public static int inlineInLoopHeader() {
+ int result = 0;
+ for (int i = 0; i < foo(i % 2 == 0); ++i) {
+ result += 42;
+ }
+ return result;
+ }
+
+ public static void main(String[] args) {
+ if (inlineIfThenElse() != 1) {
+ throw new Error("Expected 1");
+ }
+ if (inlineInLoop() != 16) {
+ throw new Error("Expected 16");
+ }
+ if (inlineInLoopHeader() != 42) {
+ throw new Error("Expected 16");
+ }
+ }
+}
diff --git a/test/448-multiple-returns/expected.txt b/test/448-multiple-returns/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/448-multiple-returns/expected.txt
diff --git a/test/448-multiple-returns/info.txt b/test/448-multiple-returns/info.txt
new file mode 100644
index 0000000..cdd354b
--- /dev/null
+++ b/test/448-multiple-returns/info.txt
@@ -0,0 +1,2 @@
+Tests inlining of a pattern not generated by DX: multiple
+returns in a single method.
diff --git a/test/448-multiple-returns/smali/MultipleReturns.smali b/test/448-multiple-returns/smali/MultipleReturns.smali
new file mode 100644
index 0000000..23815d8
--- /dev/null
+++ b/test/448-multiple-returns/smali/MultipleReturns.smali
@@ -0,0 +1,45 @@
+# 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.
+
+.class public LMultipleReturns;
+
+.super Ljava/lang/Object;
+
+.method public static caller()I
+ .registers 1
+ invoke-static {}, LMultipleReturns;->$opt$CalleeReturnVoid()V
+ invoke-static {}, LMultipleReturns;->$opt$CalleeReturnInt()I
+ move-result v0
+ return v0
+.end method
+
+.method public static $opt$CalleeReturnVoid()V
+ .registers 2
+ const/4 v0, 0x0
+ const/4 v1, 0x1
+ if-eq v1, v0, :else
+ return-void
+ :else
+ return-void
+.end method
+
+.method public static $opt$CalleeReturnInt()I
+ .registers 2
+ const/4 v0, 0x0
+ const/4 v1, 0x1
+ if-eq v1, v0, :else
+ return v0
+ :else
+ return v1
+.end method
diff --git a/test/448-multiple-returns/src/Main.java b/test/448-multiple-returns/src/Main.java
new file mode 100644
index 0000000..4050ed1
--- /dev/null
+++ b/test/448-multiple-returns/src/Main.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("MultipleReturns");
+ Method m = c.getMethod("caller");
+ int result = (Integer)m.invoke(null);
+ if (result != 0) {
+ throw new Error("Expected 0, got " + result);
+ }
+ }
+}
diff --git a/test/449-checker-bce/expected.txt b/test/449-checker-bce/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/449-checker-bce/expected.txt
diff --git a/test/449-checker-bce/info.txt b/test/449-checker-bce/info.txt
new file mode 100644
index 0000000..0a08808
--- /dev/null
+++ b/test/449-checker-bce/info.txt
@@ -0,0 +1 @@
+Checker test for testing array bounds check elimination.
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
new file mode 100644
index 0000000..5a0e13b
--- /dev/null
+++ b/test/449-checker-bce/src/Main.java
@@ -0,0 +1,96 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public class Main {
+
+ // CHECK-START: int Main.sieve(int) BCE (before)
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArrayGet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+
+ // CHECK-START: int Main.sieve(int) BCE (after)
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArrayGet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+
+ static int sieve(int size) {
+ int primeCount = 0;
+ boolean[] flags = new boolean[size + 1];
+ for (int i = 1; i < size; i++) flags[i] = true; // Can eliminate.
+ for (int i = 2; i < size; i++) {
+ if (flags[i]) { // Can eliminate.
+ primeCount++;
+ for (int k = i + 1; k <= size; k += i)
+ flags[k - 1] = false; // Can't eliminate yet due to (k+i) may overflow.
+ }
+ }
+ return primeCount;
+ }
+
+ // CHECK-START: void Main.narrow(int[], int) BCE (before)
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+
+ // CHECK-START: void Main.narrow(int[], int) BCE (after)
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+
+ static void narrow(int array[], int offset) {
+ if (offset < 0) {
+ return;
+ }
+ if (offset < array.length) {
+ // offset is in range [0, array.length-1].
+ // Bounds check can be eliminated.
+ array[offset] = 1;
+
+ int biased_offset1 = offset + 1;
+ // biased_offset1 is in range [1, array.length].
+ if (biased_offset1 < array.length) {
+ // biased_offset1 is in range [1, array.length-1].
+ // Bounds check can be eliminated.
+ array[biased_offset1] = 1;
+ }
+
+ int biased_offset2 = offset + 0x70000000;
+ // biased_offset2 is in range [0x70000000, array.length-1+0x70000000].
+ // It may overflow and be negative.
+ if (biased_offset2 < array.length) {
+ // Even with this test, biased_offset2 can be negative so we can't
+ // eliminate this bounds check.
+ array[biased_offset2] = 1;
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ sieve(20);
+ }
+}
diff --git a/test/704-multiply-accumulate/expected.txt b/test/704-multiply-accumulate/expected.txt
new file mode 100644
index 0000000..76f5a5a
--- /dev/null
+++ b/test/704-multiply-accumulate/expected.txt
@@ -0,0 +1 @@
+Done!
diff --git a/test/704-multiply-accumulate/info.txt b/test/704-multiply-accumulate/info.txt
new file mode 100644
index 0000000..a12fd44
--- /dev/null
+++ b/test/704-multiply-accumulate/info.txt
@@ -0,0 +1 @@
+Tests for multiply accumulate operations.
diff --git a/test/704-multiply-accumulate/src/Main.java b/test/704-multiply-accumulate/src/Main.java
new file mode 100644
index 0000000..7404b9b
--- /dev/null
+++ b/test/704-multiply-accumulate/src/Main.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ static int imax = Integer.MAX_VALUE;
+ static int imin = Integer.MIN_VALUE;
+ static long lmax = Long.MAX_VALUE;
+ static long lmin = Long.MIN_VALUE;
+ static CA ca;
+
+ public static void expectEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void test_int() {
+ int result = 0;
+ int a = imax;
+ int b = imin;
+ int c = 10;
+ int d = c;
+ int tmp = 0;
+ int [] ia = new int[5];
+ for (int i = 0; i < 100; i++) {
+ tmp = i*c;
+ result += i*i;
+ result = i - tmp;
+ }
+ expectEquals(result, -891);
+
+ result = c*c + (result - c);
+ expectEquals(result, -801);
+
+ result = a + a*a;
+ expectEquals(result, -2147483648);
+
+ result = b + b*b;
+ expectEquals(result, -2147483648);
+
+ result = b - a*a;
+ expectEquals(result, 2147483647);
+
+ result = d*d;
+ d++;
+ result += result;
+ expectEquals(result, 200);
+
+ result = c*c;
+ tmp++;
+ result += result;
+ expectEquals(result, 200);
+
+ result = 0;
+ try {
+ result = c*c;
+ ia[c] = d; // array out of bound.
+ result += d;
+ } catch (Exception e) {
+ }
+ expectEquals(result, 100);
+
+ CA obj = new CA();
+ result = a*c + obj.ia;
+ expectEquals(result, 2);
+
+ result = 0;
+ obj = ca;
+ try {
+ result = a*c;
+ tmp = obj.ia;
+ result = result + tmp;
+ } catch (Exception e) {
+ }
+ expectEquals(result, -10);
+ }
+
+ public static void test_long() {
+ long result = 0;
+ long a = lmax;
+ long b = lmin;
+ long c = 10;
+ long d = c;
+ long tmp = 0;
+ int [] ia = new int[5];
+ for (long i = 0; i < 100; i++) {
+ tmp = i*c;
+ result += i*i;
+ result = i - tmp;
+ }
+ expectEquals(result, -891L);
+
+ result = c*c + (result - c);
+ expectEquals(result, -801L);
+
+ result = a + a*a;
+ expectEquals(result, -9223372036854775808L);
+
+ result = b + b*b;
+ expectEquals(result, -9223372036854775808L);
+
+ result = b - a*a;
+ expectEquals(result, 9223372036854775807L);
+
+ result = d*d;
+ d++;
+ result += result;
+ expectEquals(result, 200L);
+
+ result = c*c;
+ tmp++;
+ result += result;
+ expectEquals(result, 200L);
+
+ result = 0;
+ int index = 10;
+ try {
+ result = c*c;
+ ia[index] = 10; // array out of bound.
+ result += d;
+ } catch (Exception e) {
+ }
+ expectEquals(result, 100L);
+
+ CA obj = new CA();
+ result = a*c + obj.la;
+ expectEquals(result, 113L);
+
+ result = 0;
+ obj = ca;
+ try {
+ result = a*c;
+ tmp = obj.la;
+ result = result + tmp;
+ } catch (Exception e) {
+ }
+ expectEquals(result, -10L);
+ }
+
+ public static void main(String[] args) {
+ test_int();
+ test_long();
+ System.out.println("Done!");
+ }
+
+}
+
+class CA {
+ public int ia = 12;
+ public long la = 123L;
+}
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index 0f7001f..019dc14 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -8,4 +8,10 @@
invoke-super abstract
BadCaseInOpRegRegReg
CmpLong
+FloatIntConstPassing
+b/18718277
+b/18800943 (1)
+b/18800943 (2)
+MoveExc
+MoveExceptionOnEntry
Done!
diff --git a/test/800-smali/smali/FloatIntConstPassing.smali b/test/800-smali/smali/FloatIntConstPassing.smali
new file mode 100644
index 0000000..a2916c5
--- /dev/null
+++ b/test/800-smali/smali/FloatIntConstPassing.smali
@@ -0,0 +1,29 @@
+.class public LFloatIntConstPassing;
+
+.super Ljava/lang/Object;
+
+.method public static getInt(I)I
+ .registers 2
+ const/4 v0, 1
+ add-int/2addr v0, p0
+ return v0
+.end method
+
+.method public static getFloat(F)F
+ .registers 2
+ const/4 v0, 0
+ mul-float/2addr v0, p0
+ return v0
+.end method
+
+.method public static run()I
+ .registers 3
+ const/4 v0, 1
+ invoke-static {v0}, LFloatIntConstPassing;->getInt(I)I
+ move-result v1
+ invoke-static {v0}, LFloatIntConstPassing;->getFloat(F)F
+ move-result v2
+ float-to-int v2, v2
+ add-int/2addr v1, v2
+ return v1
+.end method
diff --git a/test/800-smali/smali/b_18718277.smali b/test/800-smali/smali/b_18718277.smali
new file mode 100644
index 0000000..b14ad20
--- /dev/null
+++ b/test/800-smali/smali/b_18718277.smali
@@ -0,0 +1,29 @@
+.class public LB18718277;
+
+.super Ljava/lang/Object;
+
+.method public static helper(I)I
+ .locals 1
+ add-int/lit8 v0, p0, 2
+ neg-int v0, v0
+ return v0
+.end method
+
+.method public static getInt()I
+ .registers 2
+ const/4 v1, 3
+ invoke-static {v1}, LB18718277;->helper(I)I
+ move-result v0
+ :outer_loop
+ if-eqz v1, :exit_outer_loop
+ const/4 v0, 0
+ if-eqz v0, :skip_dead_loop
+ :dead_loop
+ add-int/2addr v0, v0
+ if-gez v0, :dead_loop
+ :skip_dead_loop
+ add-int/lit8 v1, v1, -1
+ goto :outer_loop
+ :exit_outer_loop
+ return v0
+.end method
diff --git a/test/800-smali/smali/b_18800943_1.smali b/test/800-smali/smali/b_18800943_1.smali
new file mode 100644
index 0000000..868438e
--- /dev/null
+++ b/test/800-smali/smali/b_18800943_1.smali
@@ -0,0 +1,9 @@
+.class public LB18800943_1;
+.super Ljava/lang/Object;
+
+# This constructor should fail verification as the object is not initialized by a super-call.
+.method public constructor <init>()V
+.registers 1
+ nop
+ return-void
+.end method
diff --git a/test/800-smali/smali/b_18800943_2.smali b/test/800-smali/smali/b_18800943_2.smali
new file mode 100644
index 0000000..6052ada
--- /dev/null
+++ b/test/800-smali/smali/b_18800943_2.smali
@@ -0,0 +1,9 @@
+.class public LB18800943_2;
+.super Ljava/lang/Object;
+
+# This constructor should fail verification as the object is not initialized by a super-call.
+.method public constructor <init>()V
+.registers 1
+ const v0, 0x0
+ return-void
+.end method
diff --git a/test/800-smali/smali/move_exc.smali b/test/800-smali/smali/move_exc.smali
new file mode 100644
index 0000000..4ade4bc
--- /dev/null
+++ b/test/800-smali/smali/move_exc.smali
@@ -0,0 +1,29 @@
+.class public LMoveExc;
+.super Ljava/lang/Object;
+
+
+.method public constructor <init>()V
+.registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public static run()V
+.registers 6
+:Label1
+ const v1, 15
+ const v2, 0
+ div-int v0, v1, v2
+
+:Label2
+ goto :Label4
+
+:Label3
+ move-exception v3
+ throw v3
+
+:Label4
+ return-void
+
+.catchall {:Label1 .. :Label2} :Label3
+.end method
diff --git a/test/800-smali/smali/move_exception_on_entry.smali b/test/800-smali/smali/move_exception_on_entry.smali
new file mode 100644
index 0000000..e7da2e3
--- /dev/null
+++ b/test/800-smali/smali/move_exception_on_entry.smali
@@ -0,0 +1,30 @@
+.class public LMoveExceptionOnEntry;
+
+.super Ljava/lang/Object;
+
+# Test that we cannot have a catch-handler with move-exception at the beginning of a method.
+
+.method public static moveExceptionOnEntry(I)I
+.registers 4
+:Label1
+ move-exception v2
+ const v1, 100
+ move v0, p0
+ add-int/lit8 p0, p0, 1
+
+:Label2
+ invoke-static {v0}, LMoveExceptionOnEntry;->foo(I)V
+
+:Label3
+ return v1
+
+.catchall {:Label2 .. :Label3} :Label1
+.end method
+
+.method public static foo(I)I
+.registers 4
+:Label1
+ return-void
+
+.end method
+
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index f2c1ab5..b23896d 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -50,20 +50,33 @@
// Create the test cases.
testCases = new LinkedList<TestCase>();
testCases.add(new TestCase("PackedSwitch", "PackedSwitch", "packedSwitch",
- new Object[]{123}, null, 123));
+ new Object[]{123}, null, 123));
testCases.add(new TestCase("b/17790197", "B17790197", "getInt", null, null, 100));
- testCases.add(new TestCase("b/17978759", "B17978759", "test", null, new VerifyError(), null));
+ testCases.add(new TestCase("b/17978759", "B17978759", "test", null, new VerifyError(),
+ null));
testCases.add(new TestCase("FloatBadArgReg", "FloatBadArgReg", "getInt",
- new Object[]{100}, null, 100));
+ new Object[]{100}, null, 100));
testCases.add(new TestCase("negLong", "negLong", "negLong", null, null, 122142L));
testCases.add(new TestCase("sameFieldNames", "sameFieldNames", "getInt", null, null, 7));
testCases.add(new TestCase("b/18380491", "B18380491ConcreteClass", "foo",
- new Object[]{42}, null, 42));
+ new Object[]{42}, null, 42));
testCases.add(new TestCase("invoke-super abstract", "B18380491ConcreteClass", "foo",
- new Object[]{0}, new AbstractMethodError(), null));
- testCases.add(new TestCase("BadCaseInOpRegRegReg", "BadCaseInOpRegRegReg", "getInt", null, null, 2));
+ new Object[]{0}, new AbstractMethodError(), null));
+ testCases.add(new TestCase("BadCaseInOpRegRegReg", "BadCaseInOpRegRegReg", "getInt", null,
+ null, 2));
testCases.add(new TestCase("CmpLong", "CmpLong", "run", null, null, 0));
+ testCases.add(new TestCase("FloatIntConstPassing", "FloatIntConstPassing", "run", null,
+ null, 2));
+ testCases.add(new TestCase("b/18718277", "B18718277", "getInt", null, null, 0));
+ testCases.add(new TestCase("b/18800943 (1)", "B18800943_1", "n_a", null, new VerifyError(),
+ 0));
+ testCases.add(new TestCase("b/18800943 (2)", "B18800943_2", "n_a", null, new VerifyError(),
+ 0));
+ testCases.add(new TestCase("MoveExc", "MoveExc", "run", null, new ArithmeticException(),
+ null));
+ testCases.add(new TestCase("MoveExceptionOnEntry", "MoveExceptionOnEntry",
+ "moveExceptionOnEntry", new Object[]{0}, new VerifyError(), null));
}
public void runTests() {
diff --git a/test/802-deoptimization/expected.txt b/test/802-deoptimization/expected.txt
new file mode 100644
index 0000000..d5f1f08
--- /dev/null
+++ b/test/802-deoptimization/expected.txt
@@ -0,0 +1 @@
+CatchHandlerOnEntryWithoutMoveException OK
diff --git a/test/802-deoptimization/info.txt b/test/802-deoptimization/info.txt
new file mode 100644
index 0000000..104d40f
--- /dev/null
+++ b/test/802-deoptimization/info.txt
@@ -0,0 +1 @@
+Tests related to deoptimization
diff --git a/test/802-deoptimization/smali/catch_handler_on_entry.smali b/test/802-deoptimization/smali/catch_handler_on_entry.smali
new file mode 100644
index 0000000..836101e
--- /dev/null
+++ b/test/802-deoptimization/smali/catch_handler_on_entry.smali
@@ -0,0 +1,29 @@
+.class public LCatchHandlerOnEntry;
+
+.super Ljava/lang/Object;
+
+# Test we can execute a method starting with a catch handler (without
+# move-exception instruction). This method must be called with parameter
+# initialized to 0.
+#
+# We execute the catch handler (Label1) for the first time with p0 == 0.
+# We save its value in v0, increment p0 to 1 and execute the div-int
+# instruction (Label2) which throws an ArithmeticException (division by zero).
+# That exception is caught by the catch handler so we execute it a second time.
+# Now p0 == 1. When we we execute the div-int instruction, it succeeds and we
+# return its result: this is the initial value of v1 because "v1 = v1 / 1".
+.method public static catchHandlerOnEntry(I)I
+.registers 4
+:Label1
+ const v1, 100
+ move v0, p0
+ add-int/lit8 p0, p0, 1
+
+:Label2
+ invoke-static {v0}, LCatchHandlerOnEntryHelper;->throwExceptionDuringDeopt(I)V
+
+:Label3
+ return v1
+
+.catchall {:Label2 .. :Label3} :Label1
+.end method
diff --git a/test/802-deoptimization/src/CatchHandlerOnEntryHelper.java b/test/802-deoptimization/src/CatchHandlerOnEntryHelper.java
new file mode 100644
index 0000000..9c41abf
--- /dev/null
+++ b/test/802-deoptimization/src/CatchHandlerOnEntryHelper.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+/**
+ * Helper class used by smali test classes.
+ */
+public class CatchHandlerOnEntryHelper {
+
+ public static void throwExceptionDuringDeopt(int i) {
+ if (i == 0) {
+ DeoptimizationController.startDeoptimization();
+ throw new RuntimeException("Test exception");
+ } else {
+ DeoptimizationController.stopDeoptimization();
+ }
+ }
+}
diff --git a/test/802-deoptimization/src/DeoptimizationController.java b/test/802-deoptimization/src/DeoptimizationController.java
new file mode 100644
index 0000000..c926669
--- /dev/null
+++ b/test/802-deoptimization/src/DeoptimizationController.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+/**
+ * Controls deoptimization using dalvik.system.VMDebug class.
+ */
+public class DeoptimizationController {
+ private static File createTempFile() throws Exception {
+ try {
+ return File.createTempFile("test", ".trace");
+ } catch (IOException e) {
+ System.setProperty("java.io.tmpdir", "/data/local/tmp");
+ try {
+ return File.createTempFile("test", ".trace");
+ } catch (IOException e2) {
+ System.setProperty("java.io.tmpdir", "/sdcard");
+ return File.createTempFile("test", ".trace");
+ }
+ }
+ }
+
+ public static void startDeoptimization() {
+ try {
+ File tempFile = createTempFile();
+ tempFile.deleteOnExit();
+ String tempFileName = tempFile.getPath();
+
+ VMDebug.startMethodTracing(tempFileName, 0, 0, false, 1000);
+ if (VMDebug.getMethodTracingMode() == 0) {
+ throw new IllegalStateException("Not tracing.");
+ }
+ } catch (Exception exc) {
+ exc.printStackTrace(System.err);
+ }
+ }
+
+ public static void stopDeoptimization() {
+ try {
+ VMDebug.stopMethodTracing();
+ if (VMDebug.getMethodTracingMode() != 0) {
+ throw new IllegalStateException("Still tracing.");
+ }
+ } catch (Exception exc) {
+ exc.printStackTrace(System.err);
+ }
+ }
+
+ private static class VMDebug {
+ private static final Method startMethodTracingMethod;
+ private static final Method stopMethodTracingMethod;
+ private static final Method getMethodTracingModeMethod;
+
+ static {
+ try {
+ Class<?> c = Class.forName("dalvik.system.VMDebug");
+ startMethodTracingMethod = c.getDeclaredMethod("startMethodTracing", String.class,
+ Integer.TYPE, Integer.TYPE, Boolean.TYPE, Integer.TYPE);
+ stopMethodTracingMethod = c.getDeclaredMethod("stopMethodTracing");
+ getMethodTracingModeMethod = c.getDeclaredMethod("getMethodTracingMode");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void startMethodTracing(String filename, int bufferSize, int flags,
+ boolean samplingEnabled, int intervalUs) throws Exception {
+ startMethodTracingMethod.invoke(null, filename, bufferSize, flags, samplingEnabled,
+ intervalUs);
+ }
+ public static void stopMethodTracing() throws Exception {
+ stopMethodTracingMethod.invoke(null);
+ }
+ public static int getMethodTracingMode() throws Exception {
+ return (int) getMethodTracingModeMethod.invoke(null);
+ }
+ }
+}
diff --git a/test/802-deoptimization/src/Main.java b/test/802-deoptimization/src/Main.java
new file mode 100644
index 0000000..c8780de
--- /dev/null
+++ b/test/802-deoptimization/src/Main.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+ private static final int EXPECTED_RESULT = 100;
+ private static final int PARAMETER_VALUE = 0;
+
+ public static void main(String[] args) throws Throwable {
+ testCatchHandlerOnEntryWithoutMoveException();
+ }
+
+ /**
+ * Tests we correctly execute a method starting with a catch handler without
+ * move-exception instruction when throwing an exception during deoptimization.
+ */
+ private static void testCatchHandlerOnEntryWithoutMoveException() throws Throwable {
+ Class<?> c = Class.forName("CatchHandlerOnEntry");
+ Method m = c.getMethod("catchHandlerOnEntry", int.class);
+ Object result = m.invoke(null, new Object[]{PARAMETER_VALUE});
+ int intResult = ((Integer) result).intValue();
+ if (intResult == EXPECTED_RESULT) {
+ System.out.println("CatchHandlerOnEntryWithoutMoveException OK");
+ } else {
+ System.out.println("CatchHandlerOnEntryWithoutMoveException KO: result==" + intResult);
+ }
+ }
+}
+
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index c9c0475..e64df5c 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -55,7 +55,6 @@
LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.libarttest.mk
- include external/libcxx/libcxx.mk
ifeq ($$(art_target_or_host),target)
$(call set-target-local-clang-vars)
$(call set-target-local-cflags-vars,debug)
diff --git a/test/Android.libnativebridgetest.mk b/test/Android.libnativebridgetest.mk
index 1b20e69..452278a 100644
--- a/test/Android.libnativebridgetest.mk
+++ b/test/Android.libnativebridgetest.mk
@@ -47,7 +47,6 @@
LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.libnativebridgetest.mk
- include external/libcxx/libcxx.mk
ifeq ($$(art_target_or_host),target)
$(call set-target-local-clang-vars)
$(call set-target-local-cflags-vars,debug)
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index b85685b..a8f2001 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -166,7 +166,8 @@
# Tests that are timing sensitive and flaky on heavily loaded systems.
TEST_ART_TIMING_SENSITIVE_RUN_TESTS := \
053-wait-some \
- 055-enum-performance
+ 055-enum-performance \
+ 133-static-invoke-super
# disable timing sensitive tests on "dist" builds.
ifdef dist_goal
@@ -179,7 +180,8 @@
# Note 116-nodex2oat is not broken per-se it just doesn't (and isn't meant to) work with --prebuild.
TEST_ART_BROKEN_PREBUILD_RUN_TESTS := \
- 116-nodex2oat
+ 116-nodex2oat \
+ 118-noimage-dex2oat
ifneq (,$(filter prebuild,$(PREBUILD_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),prebuild, \
@@ -203,7 +205,9 @@
# Note 117-nopatchoat is not broken per-se it just doesn't work (and isn't meant to) without
# --prebuild --relocate
TEST_ART_BROKEN_NO_RELOCATE_TESTS := \
- 117-nopatchoat
+ 117-nopatchoat \
+ 118-noimage-dex2oat \
+ 119-noimage-patchoat
ifneq (,$(filter no-relocate,$(RELOCATE_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -214,9 +218,7 @@
TEST_ART_BROKEN_NO_RELOCATE_TESTS :=
# Tests that are broken with GC stress.
-TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \
- 004-SignalTest \
- 114-ParallelGC
+TEST_ART_BROKEN_GCSTRESS_RUN_TESTS :=
ifneq (,$(filter gcstress,$(GC_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -231,6 +233,14 @@
$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),115-native-bridge, \
$(ALL_ADDRESS_SIZES))
+# 130-hprof dumps the heap and runs hprof-conv to check whether the file is somewhat readable. This
+# is only possible on the host.
+# TODO: Turn off all the other combinations, this is more about testing actual ART code. A gtest is
+# very hard to write here, as (for a complete test) JDWP must be set up.
+ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
+ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \
+ $(PICTEST_TYPES),130-hprof,$(ALL_ADDRESS_SIZES))
+
# All these tests check that we have sane behavior if we don't have a patchoat or dex2oat.
# Therefore we shouldn't run them in situations where we actually don't have these since they
# explicitly test for them. These all also assume we have an image.
@@ -276,6 +286,7 @@
117-nopatchoat \
118-noimage-dex2oat \
119-noimage-patchoat \
+ 131-structural-change \
ifneq (,$(filter ndebug,$(RUN_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),ndebug,$(PREBUILD_TYPES), \
@@ -296,33 +307,22 @@
TEST_ART_BROKEN_DEFAULT_RUN_TESTS :=
+# Tests known to be broken for the optimizing compiler on 32-bit targets due to
+# inability to allocate registers for methods with long values.
+TEST_ART_BROKEN_OPTIMIZING_32_RUN_TESTS := \
+ 441-checker-inliner \
+ 442-checker-constant-folding \
+
+ifneq (,$(filter optimizing,$(COMPILER_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ optimizing,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES),$(PICTEST_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_32_RUN_TESTS),32)
+endif
+
+TEST_ART_BROKEN_OPTIMIZING_32_RUN_TESTS :=
+
# Known broken tests for the arm64 optimizing compiler backend.
-TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS := \
- 003-omnibus-opcodes \
- 004-ReferenceMap \
- 005-annotations \
- 009-instanceof \
- 010-instance \
- 023-many-interfaces \
- 044-proxy \
- 045-reflect-array \
- 046-reflect \
- 047-returns \
- 062-character-encodings \
- 063-process-manager \
- 068-classloader \
- 069-field-type \
- 071-dexfile \
- 106-exceptions2 \
- 107-int-math2 \
- 201-built-in-exception-detail-messages \
- 407-arrays \
- 412-new-array \
- 422-instanceof \
- 424-checkcast \
- 427-bounds \
- 430-live-register-slow-path \
- 800-smali \
+TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS :=
ifneq (,$(filter optimizing,$(COMPILER_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -333,8 +333,9 @@
TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS :=
# Known broken tests for the optimizing compiler.
-TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS := \
- 099-vmdebug \ # b/18098594
+TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS :=
+TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS += 099-vmdebug # b/18098594
+TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS += 802-deoptimization # b/18547544
ifneq (,$(filter optimizing,$(COMPILER_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -342,6 +343,14 @@
$(IMAGE_TYPES),$(PICTEST_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
endif
+# If ART_USE_OPTIMIZING_COMPILER is set to true, then the default core.art has been
+# compiled with the optimizing compiler.
+ifeq ($(ART_USE_OPTIMIZING_COMPILER),true)
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ default,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES),$(PICTEST_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS :=
@@ -599,7 +608,7 @@
endif
$$(run_test_rule_name): PRIVATE_RUN_TEST_OPTIONS := $$(run_test_options)
.PHONY: $$(run_test_rule_name)
-$$(run_test_rule_name): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $(HOST_OUT_EXECUTABLES)/smali $(HOST_OUT_EXECUTABLES)/dexmerger $$(prereq_rule)
+$$(run_test_rule_name): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $(HOST_OUT_EXECUTABLES)/smali $(HOST_OUT_EXECUTABLES)/dexmerger $(HOST_OUT_EXECUTABLES)/hprof-conv $$(prereq_rule)
$(hide) $$(call ART_TEST_SKIP,$$@) && \
DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) \
SMALI=$(abspath $(HOST_OUT_EXECUTABLES)/smali) \
diff --git a/test/Transaction/Transaction.java b/test/Transaction/Transaction.java
index 9ca7fbf..00e1fbb 100644
--- a/test/Transaction/Transaction.java
+++ b/test/Transaction/Transaction.java
@@ -25,13 +25,57 @@
}
}
- static class BlacklistedClass {
+ static class FinalizableAbortClass {
+ public static AbortHelperClass finalizableObject;
static {
- NativeSupport.native_call();
+ finalizableObject = new AbortHelperClass();
}
}
- static class NativeSupport {
- public static native void native_call();
+ static class NativeCallAbortClass {
+ static {
+ AbortHelperClass.nativeMethod();
+ }
+ }
+
+ static class SynchronizedNativeCallAbortClass {
+ static {
+ synchronized (SynchronizedNativeCallAbortClass.class) {
+ AbortHelperClass.nativeMethod();
+ }
+ }
+ }
+
+ static class CatchNativeCallAbortClass {
+ static {
+ try {
+ AbortHelperClass.nativeMethod();
+ } catch (Throwable e) {
+ // ignore exception.
+ }
+ }
+ }
+
+ static class MultipleNativeCallAbortClass {
+ static {
+ // Call native method but catch the transaction exception.
+ try {
+ AbortHelperClass.nativeMethod();
+ } catch (Throwable e) {
+ // ignore exception.
+ }
+
+ // Call another native method.
+ AbortHelperClass.nativeMethod2();
+ }
+ }
+
+ // Helper class to abort transaction: finalizable class with natve methods.
+ static class AbortHelperClass {
+ public void finalize() throws Throwable {
+ super.finalize();
+ }
+ public static native void nativeMethod();
+ public static native void nativeMethod2();
}
}
diff --git a/test/etc/default-build b/test/etc/default-build
index 6731ad3..58c9564 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -17,6 +17,22 @@
# Stop if something fails.
set -e
+DX_FLAGS=""
+
+while true; do
+ if [ "x$1" = "x--dx-option" ]; then
+ shift
+ option="$1"
+ DX_FLAGS="${DX_FLAGS} $option"
+ shift
+ elif expr "x$1" : "x--" >/dev/null 2>&1; then
+ echo "unknown $0 option: $1" 1>&2
+ exit 1
+ else
+ break
+ fi
+done
+
if [ -e classes.dex ]; then
zip $TEST_NAME.jar classes.dex
exit 0
@@ -30,7 +46,8 @@
fi
if [ ${NEED_DEX} = "true" ]; then
- ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
+ ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \
+ --dump-width=1000 ${DX_FLAGS} classes
fi
if [ -d smali ]; then
@@ -43,7 +60,8 @@
mkdir classes-ex
${JAVAC} -d classes-ex -cp classes `find src-ex -name '*.java'`
if [ ${NEED_DEX} = "true" ]; then
- ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes-ex.dex --dump-width=1000 classes-ex
+ ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes-ex.dex \
+ --dump-width=1000 ${DX_FLAGS} classes-ex
# quick shuffle so that the stored name is "classes.dex"
mv classes.dex classes-1.dex
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 5c0f83f..907218a 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -10,7 +10,7 @@
ANDROID_ROOT="/system"
ARCHITECTURES_32="(arm|x86|mips|none)"
-ARCHITECTURES_64="(arm64|x86_64|none)"
+ARCHITECTURES_64="(arm64|x86_64|mips64|none)"
ARCHITECTURES_PATTERN="${ARCHITECTURES_32}"
BOOT_IMAGE=""
COMPILE_FLAGS=""
@@ -37,7 +37,8 @@
RELOCATE="y"
SECONDARY_DEX=""
TIME_OUT="y"
-TIME_OUT_VALUE=5m
+# Value in minutes.
+TIME_OUT_VALUE=10
USE_GDB="n"
USE_JVM="n"
VERIFY="y"
@@ -308,6 +309,9 @@
$DALVIKVM_BOOT_OPT \
-cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX $MAIN"
+# Remove whitespace.
+dex2oat_cmdline=$(echo $dex2oat_cmdline)
+dalvikvm_cmdline=$(echo $dalvikvm_cmdline)
if [ "$HOST" = "n" ]; then
adb root > /dev/null
@@ -377,7 +381,13 @@
if [ "$TIME_OUT" = "y" ]; then
# Add timeout command if time out is desired.
- cmdline="timeout $TIME_OUT_VALUE $cmdline"
+ #
+ # Note: We use nested timeouts. The inner timeout sends SIGRTMIN+2 (usually 36) to ART, which
+ # will induce a full thread dump before abort. However, dumping threads might deadlock,
+ # so the outer timeout sends the regular SIGTERM after an additional minute to ensure
+ # termination (without dumping all threads).
+ TIME_PLUS_ONE=$(($TIME_OUT_VALUE + 1))
+ cmdline="timeout ${TIME_PLUS_ONE}m timeout -s SIGRTMIN+2 ${TIME_OUT_VALUE}m $cmdline"
fi
if [ "$DEV_MODE" = "y" ]; then
diff --git a/test/run-test b/test/run-test
index 2abc1fa..8c47663 100755
--- a/test/run-test
+++ b/test/run-test
@@ -39,6 +39,7 @@
else
tmp_dir="${TMPDIR}/$USER/${test_dir}"
fi
+checker="${progdir}/../tools/checker.py"
export JAVA="java"
export JAVAC="javac -g"
@@ -74,8 +75,10 @@
check_cmd="check"
output="output.txt"
build_output="build-output.txt"
+cfg_output="cfg-output.txt"
lib="libartd.so"
run_args="--quiet"
+build_args=""
prebuild_mode="yes"
target_mode="yes"
@@ -258,6 +261,9 @@
elif [ "x$1" = "x--always-clean" ]; then
always_clean="yes"
shift
+ elif [ "x$1" = "x--dex2oat-swap" ]; then
+ run_args="${run_args} --dex2oat-swap"
+ shift
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
usage="yes"
@@ -278,7 +284,8 @@
mkdir -p $tmp_dir
if [ "$basic_verify" = "true" ]; then
- run_args="${run_args} --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify"
+ # Set HspaceCompactForOOMMinIntervalMs to zero to run hspace compaction for OOM more frequently in tests.
+ run_args="${run_args} --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify --runtime-option -XX:HspaceCompactForOOMMinIntervalMs=0"
fi
if [ "$gc_verify" = "true" ]; then
run_args="${run_args} --runtime-option -Xgc:preverify_rosalloc --runtime-option -Xgc:postverify_rosalloc"
@@ -294,7 +301,7 @@
# Try to map the suffix64 flag and what we find in ${ANDROID_PRODUCT_OUT}/data/art-test to an architecture name.
function guess_arch_name() {
grep32bit=`ls ${ANDROID_PRODUCT_OUT}/data/art-test | grep -E '^(arm|x86|mips)$'`
- grep64bit=`ls ${ANDROID_PRODUCT_OUT}/data/art-test | grep -E '^(arm64|x86_64)$'`
+ grep64bit=`ls ${ANDROID_PRODUCT_OUT}/data/art-test | grep -E '^(arm64|x86_64|mips64)$'`
if [ "x${suffix64}" = "x64" ]; then
target_arch_name=${grep64bit}
else
@@ -452,6 +459,7 @@
echo " --gcverify Run with gc verification"
echo " --always-clean Delete the test files even if the test fails."
echo " --android-root [path] The path on target for the android root. (/system by default)."
+ echo " --dex2oat-swap Use a dex2oat swap file."
) 1>&2
exit 1
fi
@@ -498,6 +506,21 @@
export TEST_NAME=`basename ${test_dir}`
+# Tests named '<number>-checker-*' will also have their CFGs verified with
+# Checker when compiled with Optimizing on host.
+if [[ "$TEST_NAME" =~ ^[0-9]+-checker- ]]; then
+ # Build Checker DEX files without dx's optimizations so the input to dex2oat
+ # better resembles the Java source. We always build the DEX the same way, even
+ # if Checker is not invoked and the test only runs the program.
+ build_args="${build_args} --dx-option --no-optimize"
+
+ if [ "$runtime" = "art" -a "$image_suffix" = "-optimizing" -a "$target_mode" = "no" ]; then
+ run_checker="yes"
+ run_args="${run_args} -Xcompiler-option --dump-cfg=$tmp_dir/$cfg_output \
+ -Xcompiler-option -j1"
+ fi
+fi
+
# To cause tests to fail fast, limit the file sizes created by dx, dex2oat and ART output to 2MB.
file_size_limit=2048
if echo "$test_dir" | grep 089; then
@@ -513,24 +536,37 @@
good_build="yes"
good_run="yes"
if [ "$dev_mode" = "yes" ]; then
- "./${build}" 2>&1
+ "./${build}" $build_args 2>&1
build_exit="$?"
echo "build exit status: $build_exit" 1>&2
if [ "$build_exit" = '0' ]; then
echo "${test_dir}: running..." 1>&2
"./${run}" $run_args "$@" 2>&1
run_exit="$?"
- echo "run exit status: $run_exit" 1>&2
+
if [ "$run_exit" = "0" ]; then
- good="yes"
+ if [ "$run_checker" = "yes" ]; then
+ "$checker" "$cfg_output" "$tmp_dir" 2>&1
+ checker_exit="$?"
+ if [ "$checker_exit" = "0" ]; then
+ good="yes"
+ fi
+ echo "checker exit status: $checker_exit" 1>&2
+ else
+ good="yes"
+ fi
fi
+ echo "run exit status: $run_exit" 1>&2
fi
elif [ "$update_mode" = "yes" ]; then
- "./${build}" >"$build_output" 2>&1
+ "./${build}" $build_args >"$build_output" 2>&1
build_exit="$?"
if [ "$build_exit" = '0' ]; then
echo "${test_dir}: running..." 1>&2
"./${run}" $run_args "$@" >"$output" 2>&1
+ if [ "$run_checker" = "yes" ]; then
+ "$checker" -q "$cfg_output" "$tmp_dir" >> "$output" 2>&1
+ fi
sed -e 's/[[:cntrl:]]$//g' < "$output" >"${td_expected}"
good="yes"
else
@@ -539,7 +575,7 @@
fi
elif [ "$build_only" = "yes" ]; then
good="yes"
- "./${build}" >"$build_output" 2>&1
+ "./${build}" $build_args >"$build_output" 2>&1
build_exit="$?"
if [ "$build_exit" '!=' '0' ]; then
cp "$build_output" "$output"
@@ -554,7 +590,7 @@
find $tmp_dir -mindepth 1 ! -regex ".*/\(.*jar\|$output\|$expected\)" | xargs rm -rf
exit 0
else
- "./${build}" >"$build_output" 2>&1
+ "./${build}" $build_args >"$build_output" 2>&1
build_exit="$?"
if [ "$build_exit" = '0' ]; then
echo "${test_dir}: running..." 1>&2
@@ -563,6 +599,15 @@
if [ "$run_exit" != "0" ]; then
echo "run exit status: $run_exit" 1>&2
good_run="no"
+ elif [ "$run_checker" = "yes" ]; then
+ "$checker" -q "$cfg_output" "$tmp_dir" >> "$output" 2>&1
+ checker_exit="$?"
+ if [ "$checker_exit" != "0" ]; then
+ echo "checker exit status: $checker_exit" 1>&2
+ good_run="no"
+ else
+ good_run="yes"
+ fi
else
good_run="yes"
fi
diff --git a/tools/art b/tools/art
index af96b47..2408f9f 100644
--- a/tools/art
+++ b/tools/art
@@ -1,5 +1,3 @@
-#!/bin/bash
-#
# Copyright (C) 2011 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +12,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# This script is used on host and device. It uses a common subset
+# shell dialect that should work on the host (e.g. bash), and
+# Android (e.g. mksh).
+
function follow_links() {
if [ z"$BASH_SOURCE" != z ]; then
file="$BASH_SOURCE"
@@ -28,7 +30,8 @@
}
function find_libdir() {
- if [ "$(readlink "$ANDROID_ROOT/bin/$DALVIKVM")" = "dalvikvm64" ]; then
+ # Use realpath instead of readlink because Android does not have a readlink.
+ if [ "$(realpath "$ANDROID_ROOT/bin/$DALVIKVM")" = "$(realpath "$ANDROID_ROOT/bin/dalvikvm64")" ]; then
echo "lib64"
else
echo "lib"
@@ -70,16 +73,22 @@
PROG_NAME="$(follow_links)"
PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
ANDROID_ROOT=$PROG_DIR/..
-ANDROID_DATA=$PWD/android-data$$
LIBDIR=$(find_libdir)
LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBDIR
+DELETE_ANDROID_DATA=false
+# If ANDROID_DATA is the system ANDROID_DATA or is not set, use our own,
+# and ensure we delete it at the end.
+if [ "$ANDROID_DATA" = "/data" ] || [ "$ANDROID_DATA" = "" ]; then
+ ANDROID_DATA=$PWD/android-data$$
+ mkdir -p $ANDROID_DATA/dalvik-cache/{arm,arm64,x86,x86_64}
+ DELETE_ANDROID_DATA=true
+fi
if [ z"$PERF" != z ]; then
invoke_with="perf record -o $ANDROID_DATA/perf.data -e cycles:u $invoke_with"
fi
-mkdir -p $ANDROID_DATA/dalvik-cache/{arm,arm64,x86,x86_64}
ANDROID_DATA=$ANDROID_DATA \
ANDROID_ROOT=$ANDROID_ROOT \
LD_LIBRARY_PATH=$LD_LIBRARY_PATH \
@@ -97,7 +106,9 @@
fi
echo "Perf data saved in: $ANDROID_DATA/perf.data"
else
- rm -rf $ANDROID_DATA
+ if [ "$DELETE_ANDROID_DATA" = "true" ]; then
+ rm -rf $ANDROID_DATA
+ fi
fi
exit $EXIT_STATUS
diff --git a/tools/checker.py b/tools/checker.py
new file mode 100755
index 0000000..0bce236
--- /dev/null
+++ b/tools/checker.py
@@ -0,0 +1,777 @@
+#!/usr/bin/env python2
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# Checker is a testing tool which compiles a given test file and compares the
+# state of the control-flow graph before and after each optimization pass
+# against a set of assertions specified alongside the tests.
+#
+# Tests are written in Java, turned into DEX and compiled with the Optimizing
+# compiler. "Check lines" are assertions formatted as comments of the Java file.
+# They begin with prefix 'CHECK' followed by a pattern that the engine attempts
+# to match in the compiler-generated output.
+#
+# Assertions are tested in groups which correspond to the individual compiler
+# passes. Each group of check lines therefore must start with a 'CHECK-START'
+# header which specifies the output group it should be tested against. The group
+# name must exactly match one of the groups recognized in the output (they can
+# be listed with the '--list-groups' command-line flag).
+#
+# Matching of check lines is carried out in the order of appearance in the
+# source file. There are three types of check lines:
+# - CHECK: Must match an output line which appears in the output group
+# later than lines matched against any preceeding checks. Output
+# lines must therefore match the check lines in the same order.
+# These are referred to as "in-order" checks in the code.
+# - CHECK-DAG: Must match an output line which appears in the output group
+# later than lines matched against any preceeding in-order checks.
+# In other words, the order of output lines does not matter
+# between consecutive DAG checks.
+# - CHECK-NOT: Must not match any output line which appears in the output group
+# later than lines matched against any preceeding checks and
+# earlier than lines matched against any subsequent checks.
+# Surrounding non-negative checks (or boundaries of the group)
+# therefore create a scope within which the assertion is verified.
+#
+# Check-line patterns are treated as plain text rather than regular expressions
+# but are whitespace agnostic.
+#
+# Actual regex patterns can be inserted enclosed in '{{' and '}}' brackets. If
+# curly brackets need to be used inside the body of the regex, they need to be
+# enclosed in round brackets. For example, the pattern '{{foo{2}}}' will parse
+# the invalid regex 'foo{2', but '{{(fo{2})}}' will match 'foo'.
+#
+# Regex patterns can be named and referenced later. A new variable is defined
+# with '[[name:regex]]' and can be referenced with '[[name]]'. Variables are
+# only valid within the scope of the defining group. Within a group they cannot
+# be redefined or used undefined.
+#
+# Example:
+# The following assertions can be placed in a Java source file:
+#
+# // CHECK-START: int MyClass.MyMethod() constant_folding (after)
+# // CHECK: [[ID:i[0-9]+]] IntConstant {{11|22}}
+# // CHECK: Return [ [[ID]] ]
+#
+# The engine will attempt to match the check lines against the output of the
+# group named on the first line. Together they verify that the CFG after
+# constant folding returns an integer constant with value either 11 or 22.
+#
+
+from __future__ import print_function
+import argparse
+import os
+import re
+import shutil
+import sys
+import tempfile
+
+class Logger(object):
+
+ class Level(object):
+ NoOutput, Error, Info = range(3)
+
+ class Color(object):
+ Default, Blue, Gray, Purple, Red = range(5)
+
+ @staticmethod
+ def terminalCode(color, out=sys.stdout):
+ if not out.isatty():
+ return ''
+ elif color == Logger.Color.Blue:
+ return '\033[94m'
+ elif color == Logger.Color.Gray:
+ return '\033[37m'
+ elif color == Logger.Color.Purple:
+ return '\033[95m'
+ elif color == Logger.Color.Red:
+ return '\033[91m'
+ else:
+ return '\033[0m'
+
+ Verbosity = Level.Info
+
+ @staticmethod
+ def log(text, level=Level.Info, color=Color.Default, newLine=True, out=sys.stdout):
+ if level <= Logger.Verbosity:
+ text = Logger.Color.terminalCode(color, out) + text + \
+ Logger.Color.terminalCode(Logger.Color.Default, out)
+ if newLine:
+ print(text, file=out)
+ else:
+ print(text, end="", file=out)
+ out.flush()
+
+ @staticmethod
+ def fail(msg, file=None, line=-1):
+ location = ""
+ if file:
+ location += file + ":"
+ if line > 0:
+ location += str(line) + ":"
+ if location:
+ location += " "
+
+ Logger.log(location, Logger.Level.Error, color=Logger.Color.Gray, newLine=False, out=sys.stderr)
+ Logger.log("error: ", Logger.Level.Error, color=Logger.Color.Red, newLine=False, out=sys.stderr)
+ Logger.log(msg, Logger.Level.Error, out=sys.stderr)
+ sys.exit(msg)
+
+ @staticmethod
+ def startTest(name):
+ Logger.log("TEST ", color=Logger.Color.Purple, newLine=False)
+ Logger.log(name + "... ", newLine=False)
+
+ @staticmethod
+ def testPassed():
+ Logger.log("PASS", color=Logger.Color.Blue)
+
+ @staticmethod
+ def testFailed(msg, file=None, line=-1):
+ Logger.log("FAIL", color=Logger.Color.Red)
+ Logger.fail(msg, file, line)
+
+class CommonEqualityMixin:
+ """Mixin for class equality as equality of the fields."""
+ def __eq__(self, other):
+ return (isinstance(other, self.__class__)
+ and self.__dict__ == other.__dict__)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __repr__(self):
+ return "<%s: %s>" % (type(self).__name__, str(self.__dict__))
+
+
+class CheckElement(CommonEqualityMixin):
+ """Single element of the check line."""
+
+ class Variant(object):
+ """Supported language constructs."""
+ Text, Pattern, VarRef, VarDef, Separator = range(5)
+
+ rStartOptional = r"("
+ rEndOptional = r")?"
+
+ rName = r"([a-zA-Z][a-zA-Z0-9]*)"
+ rRegex = r"(.+?)"
+ rPatternStartSym = r"(\{\{)"
+ rPatternEndSym = r"(\}\})"
+ rVariableStartSym = r"(\[\[)"
+ rVariableEndSym = r"(\]\])"
+ rVariableSeparator = r"(:)"
+
+ regexPattern = rPatternStartSym + rRegex + rPatternEndSym
+ regexVariable = rVariableStartSym + \
+ rName + \
+ (rStartOptional + rVariableSeparator + rRegex + rEndOptional) + \
+ rVariableEndSym
+
+ def __init__(self, variant, name, pattern):
+ self.variant = variant
+ self.name = name
+ self.pattern = pattern
+
+ @staticmethod
+ def newSeparator():
+ return CheckElement(CheckElement.Variant.Separator, None, None)
+
+ @staticmethod
+ def parseText(text):
+ return CheckElement(CheckElement.Variant.Text, None, re.escape(text))
+
+ @staticmethod
+ def parsePattern(patternElem):
+ return CheckElement(CheckElement.Variant.Pattern, None, patternElem[2:-2])
+
+ @staticmethod
+ def parseVariable(varElem):
+ colonPos = varElem.find(":")
+ if colonPos == -1:
+ # Variable reference
+ name = varElem[2:-2]
+ return CheckElement(CheckElement.Variant.VarRef, name, None)
+ else:
+ # Variable definition
+ name = varElem[2:colonPos]
+ body = varElem[colonPos+1:-2]
+ return CheckElement(CheckElement.Variant.VarDef, name, body)
+
+class CheckLine(CommonEqualityMixin):
+ """Representation of a single assertion in the check file formed of one or
+ more regex elements. Matching against an output line is successful only
+ if all regex elements can be matched in the given order."""
+
+ class Variant(object):
+ """Supported types of assertions."""
+ InOrder, DAG, Not = range(3)
+
+ def __init__(self, content, variant=Variant.InOrder, fileName=None, lineNo=-1):
+ self.fileName = fileName
+ self.lineNo = lineNo
+ self.content = content.strip()
+
+ self.variant = variant
+ self.lineParts = self.__parse(self.content)
+ if not self.lineParts:
+ Logger.fail("Empty check line", self.fileName, self.lineNo)
+
+ if self.variant == CheckLine.Variant.Not:
+ for elem in self.lineParts:
+ if elem.variant == CheckElement.Variant.VarDef:
+ Logger.fail("CHECK-NOT lines cannot define variables", self.fileName, self.lineNo)
+
+ def __eq__(self, other):
+ return (isinstance(other, self.__class__) and
+ self.variant == other.variant and
+ self.lineParts == other.lineParts)
+
+ # Returns True if the given Match object was at the beginning of the line.
+ def __isMatchAtStart(self, match):
+ return (match is not None) and (match.start() == 0)
+
+ # Takes in a list of Match objects and returns the minimal start point among
+ # them. If there aren't any successful matches it returns the length of
+ # the searched string.
+ def __firstMatch(self, matches, string):
+ starts = map(lambda m: len(string) if m is None else m.start(), matches)
+ return min(starts)
+
+ # This method parses the content of a check line stripped of the initial
+ # comment symbol and the CHECK keyword.
+ def __parse(self, line):
+ lineParts = []
+ # Loop as long as there is something to parse.
+ while line:
+ # Search for the nearest occurrence of the special markers.
+ matchWhitespace = re.search(r"\s+", line)
+ matchPattern = re.search(CheckElement.regexPattern, line)
+ matchVariable = re.search(CheckElement.regexVariable, line)
+
+ # If one of the above was identified at the current position, extract them
+ # from the line, parse them and add to the list of line parts.
+ if self.__isMatchAtStart(matchWhitespace):
+ # A whitespace in the check line creates a new separator of line parts.
+ # This allows for ignored output between the previous and next parts.
+ line = line[matchWhitespace.end():]
+ lineParts.append(CheckElement.newSeparator())
+ elif self.__isMatchAtStart(matchPattern):
+ pattern = line[0:matchPattern.end()]
+ line = line[matchPattern.end():]
+ lineParts.append(CheckElement.parsePattern(pattern))
+ elif self.__isMatchAtStart(matchVariable):
+ var = line[0:matchVariable.end()]
+ line = line[matchVariable.end():]
+ lineParts.append(CheckElement.parseVariable(var))
+ else:
+ # If we're not currently looking at a special marker, this is a plain
+ # text match all the way until the first special marker (or the end
+ # of the line).
+ firstMatch = self.__firstMatch([ matchWhitespace, matchPattern, matchVariable ], line)
+ text = line[0:firstMatch]
+ line = line[firstMatch:]
+ lineParts.append(CheckElement.parseText(text))
+ return lineParts
+
+ # Returns the regex pattern to be matched in the output line. Variable
+ # references are substituted with their current values provided in the
+ # 'varState' argument.
+ # An exception is raised if a referenced variable is undefined.
+ def __generatePattern(self, linePart, varState):
+ if linePart.variant == CheckElement.Variant.VarRef:
+ try:
+ return re.escape(varState[linePart.name])
+ except KeyError:
+ Logger.testFailed("Use of undefined variable \"" + linePart.name + "\"",
+ self.fileName, self.lineNo)
+ else:
+ return linePart.pattern
+
+ def __isSeparated(self, outputLine, matchStart):
+ return (matchStart == 0) or (outputLine[matchStart - 1:matchStart].isspace())
+
+ # Attempts to match the check line against a line from the output file with
+ # the given initial variable values. It returns the new variable state if
+ # successful and None otherwise.
+ def match(self, outputLine, initialVarState):
+ # Do the full matching on a shadow copy of the variable state. If the
+ # matching fails half-way, we will not need to revert the state.
+ varState = dict(initialVarState)
+
+ matchStart = 0
+ isAfterSeparator = True
+
+ # Now try to parse all of the parts of the check line in the right order.
+ # Variable values are updated on-the-fly, meaning that a variable can
+ # be referenced immediately after its definition.
+ for part in self.lineParts:
+ if part.variant == CheckElement.Variant.Separator:
+ isAfterSeparator = True
+ continue
+
+ # Find the earliest match for this line part.
+ pattern = self.__generatePattern(part, varState)
+ while True:
+ match = re.search(pattern, outputLine[matchStart:])
+ if (match is None) or (not isAfterSeparator and not self.__isMatchAtStart(match)):
+ return None
+ matchEnd = matchStart + match.end()
+ matchStart += match.start()
+
+ # Check if this is a valid match if we expect a whitespace separator
+ # before the matched text. Otherwise loop and look for another match.
+ if not isAfterSeparator or self.__isSeparated(outputLine, matchStart):
+ break
+ else:
+ matchStart += 1
+
+ if part.variant == CheckElement.Variant.VarDef:
+ if part.name in varState:
+ Logger.testFailed("Multiple definitions of variable \"" + part.name + "\"",
+ self.fileName, self.lineNo)
+ varState[part.name] = outputLine[matchStart:matchEnd]
+
+ matchStart = matchEnd
+ isAfterSeparator = False
+
+ # All parts were successfully matched. Return the new variable state.
+ return varState
+
+
+class CheckGroup(CommonEqualityMixin):
+ """Represents a named collection of check lines which are to be matched
+ against an output group of the same name."""
+
+ def __init__(self, name, lines, fileName=None, lineNo=-1):
+ self.fileName = fileName
+ self.lineNo = lineNo
+
+ if not name:
+ Logger.fail("Check group does not have a name", self.fileName, self.lineNo)
+ if not lines:
+ Logger.fail("Check group does not have a body", self.fileName, self.lineNo)
+
+ self.name = name
+ self.lines = lines
+
+ def __eq__(self, other):
+ return (isinstance(other, self.__class__) and
+ self.name == other.name and
+ self.lines == other.lines)
+
+ def __headAndTail(self, list):
+ return list[0], list[1:]
+
+ # Splits a list of check lines at index 'i' such that lines[i] is the first
+ # element whose variant is not equal to the given parameter.
+ def __splitByVariant(self, lines, variant):
+ i = 0
+ while i < len(lines) and lines[i].variant == variant:
+ i += 1
+ return lines[:i], lines[i:]
+
+ # Extracts the first sequence of check lines which are independent of each
+ # other's match location, i.e. either consecutive DAG lines or a single
+ # InOrder line. Any Not lines preceeding this sequence are also extracted.
+ def __nextIndependentChecks(self, checkLines):
+ notChecks, checkLines = self.__splitByVariant(checkLines, CheckLine.Variant.Not)
+ if not checkLines:
+ return notChecks, [], []
+
+ head, tail = self.__headAndTail(checkLines)
+ if head.variant == CheckLine.Variant.InOrder:
+ return notChecks, [head], tail
+ else:
+ assert head.variant == CheckLine.Variant.DAG
+ independentChecks, checkLines = self.__splitByVariant(checkLines, CheckLine.Variant.DAG)
+ return notChecks, independentChecks, checkLines
+
+ # If successful, returns the line number of the first output line matching the
+ # check line and the updated variable state. Otherwise returns -1 and None,
+ # respectively. The 'lineFilter' parameter can be used to supply a list of
+ # line numbers (counting from 1) which should be skipped.
+ def __findFirstMatch(self, checkLine, outputLines, startLineNo, lineFilter, varState):
+ matchLineNo = startLineNo
+ for outputLine in outputLines:
+ if matchLineNo not in lineFilter:
+ newVarState = checkLine.match(outputLine, varState)
+ if newVarState is not None:
+ return matchLineNo, newVarState
+ matchLineNo += 1
+ return -1, None
+
+ # Matches the given positive check lines against the output in order of
+ # appearance. Variable state is propagated but the scope of the search remains
+ # the same for all checks. Each output line can only be matched once.
+ # If all check lines are matched, the resulting variable state is returned
+ # together with the remaining output. The function also returns output lines
+ # which appear before either of the matched lines so they can be tested
+ # against Not checks.
+ def __matchIndependentChecks(self, checkLines, outputLines, startLineNo, varState):
+ # If no checks are provided, skip over the entire output.
+ if not checkLines:
+ return outputLines, [], startLineNo + len(outputLines), varState
+
+ # Keep track of which lines have been matched.
+ matchedLines = []
+
+ # Find first unused output line which matches each check line.
+ for checkLine in checkLines:
+ matchLineNo, varState = \
+ self.__findFirstMatch(checkLine, outputLines, startLineNo, matchedLines, varState)
+ if varState is None:
+ Logger.testFailed("Could not match check line \"" + checkLine.content + "\" " +
+ "starting from output line " + str(startLineNo),
+ self.fileName, checkLine.lineNo)
+ matchedLines.append(matchLineNo)
+
+ # Return new variable state and the output lines which lie outside the
+ # match locations of this independent group.
+ minMatchLineNo = min(matchedLines)
+ maxMatchLineNo = max(matchedLines)
+ preceedingLines = outputLines[:minMatchLineNo - startLineNo]
+ remainingLines = outputLines[maxMatchLineNo - startLineNo + 1:]
+ return preceedingLines, remainingLines, maxMatchLineNo + 1, varState
+
+ # Makes sure that the given check lines do not match any of the given output
+ # lines. Variable state does not change.
+ def __matchNotLines(self, checkLines, outputLines, startLineNo, varState):
+ for checkLine in checkLines:
+ assert checkLine.variant == CheckLine.Variant.Not
+ matchLineNo, matchVarState = \
+ self.__findFirstMatch(checkLine, outputLines, startLineNo, [], varState)
+ if matchVarState is not None:
+ Logger.testFailed("CHECK-NOT line \"" + checkLine.content + "\" matches output line " + \
+ str(matchLineNo), self.fileName, checkLine.lineNo)
+
+ # Matches the check lines in this group against an output group. It is
+ # responsible for running the checks in the right order and scope, and
+ # for propagating the variable state between the check lines.
+ def match(self, outputGroup):
+ varState = {}
+ checkLines = self.lines
+ outputLines = outputGroup.body
+ startLineNo = outputGroup.lineNo
+
+ while checkLines:
+ # Extract the next sequence of location-independent checks to be matched.
+ notChecks, independentChecks, checkLines = self.__nextIndependentChecks(checkLines)
+
+ # Match the independent checks.
+ notOutput, outputLines, newStartLineNo, newVarState = \
+ self.__matchIndependentChecks(independentChecks, outputLines, startLineNo, varState)
+
+ # Run the Not checks against the output lines which lie between the last
+ # two independent groups or the bounds of the output.
+ self.__matchNotLines(notChecks, notOutput, startLineNo, varState)
+
+ # Update variable state.
+ startLineNo = newStartLineNo
+ varState = newVarState
+
+class OutputGroup(CommonEqualityMixin):
+ """Represents a named part of the test output against which a check group of
+ the same name is to be matched."""
+
+ def __init__(self, name, body, fileName=None, lineNo=-1):
+ if not name:
+ Logger.fail("Output group does not have a name", fileName, lineNo)
+ if not body:
+ Logger.fail("Output group does not have a body", fileName, lineNo)
+
+ self.name = name
+ self.body = body
+ self.lineNo = lineNo
+
+ def __eq__(self, other):
+ return (isinstance(other, self.__class__) and
+ self.name == other.name and
+ self.body == other.body)
+
+
+class FileSplitMixin(object):
+ """Mixin for representing text files which need to be split into smaller
+ chunks before being parsed."""
+
+ def _parseStream(self, stream):
+ lineNo = 0
+ allGroups = []
+ currentGroup = None
+
+ for line in stream:
+ lineNo += 1
+ line = line.strip()
+ if not line:
+ continue
+
+ # Let the child class process the line and return information about it.
+ # The _processLine method can modify the content of the line (or delete it
+ # entirely) and specify whether it starts a new group.
+ processedLine, newGroupName = self._processLine(line, lineNo)
+ if newGroupName is not None:
+ currentGroup = (newGroupName, [], lineNo)
+ allGroups.append(currentGroup)
+ if processedLine is not None:
+ if currentGroup is not None:
+ currentGroup[1].append(processedLine)
+ else:
+ self._exceptionLineOutsideGroup(line, lineNo)
+
+ # Finally, take the generated line groups and let the child class process
+ # each one before storing the final outcome.
+ return list(map(lambda group: self._processGroup(group[0], group[1], group[2]), allGroups))
+
+
+class CheckFile(FileSplitMixin):
+ """Collection of check groups extracted from the input test file."""
+
+ def __init__(self, prefix, checkStream, fileName=None):
+ self.fileName = fileName
+ self.prefix = prefix
+ self.groups = self._parseStream(checkStream)
+
+ # Attempts to parse a check line. The regex searches for a comment symbol
+ # followed by the CHECK keyword, given attribute and a colon at the very
+ # beginning of the line. Whitespaces are ignored.
+ def _extractLine(self, prefix, line):
+ rIgnoreWhitespace = r"\s*"
+ rCommentSymbols = [r"//", r"#"]
+ regexPrefix = rIgnoreWhitespace + \
+ r"(" + r"|".join(rCommentSymbols) + r")" + \
+ rIgnoreWhitespace + \
+ prefix + r":"
+
+ # The 'match' function succeeds only if the pattern is matched at the
+ # beginning of the line.
+ match = re.match(regexPrefix, line)
+ if match is not None:
+ return line[match.end():].strip()
+ else:
+ return None
+
+ # This function is invoked on each line of the check file and returns a pair
+ # which instructs the parser how the line should be handled. If the line is to
+ # be included in the current check group, it is returned in the first value.
+ # If the line starts a new check group, the name of the group is returned in
+ # the second value.
+ def _processLine(self, line, lineNo):
+ # Lines beginning with 'CHECK-START' start a new check group.
+ startLine = self._extractLine(self.prefix + "-START", line)
+ if startLine is not None:
+ return None, startLine
+
+ # Lines starting only with 'CHECK' are matched in order.
+ plainLine = self._extractLine(self.prefix, line)
+ if plainLine is not None:
+ return (plainLine, CheckLine.Variant.InOrder, lineNo), None
+
+ # 'CHECK-DAG' lines are no-order assertions.
+ dagLine = self._extractLine(self.prefix + "-DAG", line)
+ if dagLine is not None:
+ return (dagLine, CheckLine.Variant.DAG, lineNo), None
+
+ # 'CHECK-NOT' lines are no-order negative assertions.
+ notLine = self._extractLine(self.prefix + "-NOT", line)
+ if notLine is not None:
+ return (notLine, CheckLine.Variant.Not, lineNo), None
+
+ # Other lines are ignored.
+ return None, None
+
+ def _exceptionLineOutsideGroup(self, line, lineNo):
+ Logger.fail("Check line not inside a group", self.fileName, lineNo)
+
+ # Constructs a check group from the parser-collected check lines.
+ def _processGroup(self, name, lines, lineNo):
+ checkLines = list(map(lambda line: CheckLine(line[0], line[1], self.fileName, line[2]), lines))
+ return CheckGroup(name, checkLines, self.fileName, lineNo)
+
+ def match(self, outputFile):
+ for checkGroup in self.groups:
+ # TODO: Currently does not handle multiple occurrences of the same group
+ # name, e.g. when a pass is run multiple times. It will always try to
+ # match a check group against the first output group of the same name.
+ outputGroup = outputFile.findGroup(checkGroup.name)
+ if outputGroup is None:
+ Logger.fail("Group \"" + checkGroup.name + "\" not found in the output",
+ self.fileName, checkGroup.lineNo)
+ Logger.startTest(checkGroup.name)
+ checkGroup.match(outputGroup)
+ Logger.testPassed()
+
+
+class OutputFile(FileSplitMixin):
+ """Representation of the output generated by the test and split into groups
+ within which the checks are performed.
+
+ C1visualizer format is parsed with a state machine which differentiates
+ between the 'compilation' and 'cfg' blocks. The former marks the beginning
+ of a method. It is parsed for the method's name but otherwise ignored. Each
+ subsequent CFG block represents one stage of the compilation pipeline and
+ is parsed into an output group named "<method name> <pass name>".
+ """
+
+ class ParsingState:
+ OutsideBlock, InsideCompilationBlock, StartingCfgBlock, InsideCfgBlock = range(4)
+
+ def __init__(self, outputStream, fileName=None):
+ self.fileName = fileName
+
+ # Initialize the state machine
+ self.lastMethodName = None
+ self.state = OutputFile.ParsingState.OutsideBlock
+ self.groups = self._parseStream(outputStream)
+
+ # This function is invoked on each line of the output file and returns a pair
+ # which instructs the parser how the line should be handled. If the line is to
+ # be included in the current group, it is returned in the first value. If the
+ # line starts a new output group, the name of the group is returned in the
+ # second value.
+ def _processLine(self, line, lineNo):
+ if self.state == OutputFile.ParsingState.StartingCfgBlock:
+ # Previous line started a new 'cfg' block which means that this one must
+ # contain the name of the pass (this is enforced by C1visualizer).
+ if re.match("name\s+\"[^\"]+\"", line):
+ # Extract the pass name, prepend it with the name of the method and
+ # return as the beginning of a new group.
+ self.state = OutputFile.ParsingState.InsideCfgBlock
+ return (None, self.lastMethodName + " " + line.split("\"")[1])
+ else:
+ Logger.fail("Expected output group name", self.fileName, lineNo)
+
+ elif self.state == OutputFile.ParsingState.InsideCfgBlock:
+ if line == "end_cfg":
+ self.state = OutputFile.ParsingState.OutsideBlock
+ return (None, None)
+ else:
+ return (line, None)
+
+ elif self.state == OutputFile.ParsingState.InsideCompilationBlock:
+ # Search for the method's name. Format: method "<name>"
+ if re.match("method\s+\"[^\"]*\"", line):
+ methodName = line.split("\"")[1].strip()
+ if not methodName:
+ Logger.fail("Empty method name in output", self.fileName, lineNo)
+ self.lastMethodName = methodName
+ elif line == "end_compilation":
+ self.state = OutputFile.ParsingState.OutsideBlock
+ return (None, None)
+
+ else:
+ assert self.state == OutputFile.ParsingState.OutsideBlock
+ if line == "begin_cfg":
+ # The line starts a new group but we'll wait until the next line from
+ # which we can extract the name of the pass.
+ if self.lastMethodName is None:
+ Logger.fail("Expected method header", self.fileName, lineNo)
+ self.state = OutputFile.ParsingState.StartingCfgBlock
+ return (None, None)
+ elif line == "begin_compilation":
+ self.state = OutputFile.ParsingState.InsideCompilationBlock
+ return (None, None)
+ else:
+ Logger.fail("Output line not inside a group", self.fileName, lineNo)
+
+ # Constructs an output group from the parser-collected output lines.
+ def _processGroup(self, name, lines, lineNo):
+ return OutputGroup(name, lines, self.fileName, lineNo + 1)
+
+ def findGroup(self, name):
+ for group in self.groups:
+ if group.name == name:
+ return group
+ return None
+
+
+def ParseArguments():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("tested_file",
+ help="text file the checks should be verified against")
+ parser.add_argument("source_path", nargs="?",
+ help="path to file/folder with checking annotations")
+ parser.add_argument("--check-prefix", dest="check_prefix", default="CHECK", metavar="PREFIX",
+ help="prefix of checks in the test files (default: CHECK)")
+ parser.add_argument("--list-groups", dest="list_groups", action="store_true",
+ help="print a list of all groups found in the tested file")
+ parser.add_argument("--dump-group", dest="dump_group", metavar="GROUP",
+ help="print the contents of an output group")
+ parser.add_argument("-q", "--quiet", action="store_true",
+ help="print only errors")
+ return parser.parse_args()
+
+
+def ListGroups(outputFilename):
+ outputFile = OutputFile(open(outputFilename, "r"))
+ for group in outputFile.groups:
+ Logger.log(group.name)
+
+
+def DumpGroup(outputFilename, groupName):
+ outputFile = OutputFile(open(outputFilename, "r"))
+ group = outputFile.findGroup(groupName)
+ if group:
+ lineNo = group.lineNo
+ maxLineNo = lineNo + len(group.body)
+ lenLineNo = len(str(maxLineNo)) + 2
+ for line in group.body:
+ Logger.log((str(lineNo) + ":").ljust(lenLineNo) + line)
+ lineNo += 1
+ else:
+ Logger.fail("Group \"" + groupName + "\" not found in the output")
+
+
+# Returns a list of files to scan for check annotations in the given path. Path
+# to a file is returned as a single-element list, directories are recursively
+# traversed and all '.java' files returned.
+def FindCheckFiles(path):
+ if not path:
+ Logger.fail("No source path provided")
+ elif os.path.isfile(path):
+ return [ path ]
+ elif os.path.isdir(path):
+ foundFiles = []
+ for root, dirs, files in os.walk(path):
+ for file in files:
+ if os.path.splitext(file)[1] == ".java":
+ foundFiles.append(os.path.join(root, file))
+ return foundFiles
+ else:
+ Logger.fail("Source path \"" + path + "\" not found")
+
+
+def RunChecks(checkPrefix, checkPath, outputFilename):
+ outputBaseName = os.path.basename(outputFilename)
+ outputFile = OutputFile(open(outputFilename, "r"), outputBaseName)
+
+ for checkFilename in FindCheckFiles(checkPath):
+ checkBaseName = os.path.basename(checkFilename)
+ checkFile = CheckFile(checkPrefix, open(checkFilename, "r"), checkBaseName)
+ checkFile.match(outputFile)
+
+
+if __name__ == "__main__":
+ args = ParseArguments()
+
+ if args.quiet:
+ Logger.Verbosity = Logger.Level.Error
+
+ if args.list_groups:
+ ListGroups(args.tested_file)
+ elif args.dump_group:
+ DumpGroup(args.tested_file, args.dump_group)
+ else:
+ RunChecks(args.check_prefix, args.source_path, args.tested_file)
diff --git a/tools/checker_test.py b/tools/checker_test.py
new file mode 100755
index 0000000..667ca90
--- /dev/null
+++ b/tools/checker_test.py
@@ -0,0 +1,474 @@
+#!/usr/bin/env python2
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is a test file which exercises all feautres supported by the domain-
+# specific markup language implemented by Checker.
+
+import checker
+import io
+import unittest
+
+# The parent type of exception expected to be thrown by Checker during tests.
+# It must be specific enough to not cover exceptions thrown due to actual flaws
+# in Checker.
+CheckerException = SystemExit
+
+
+class TestCheckFile_PrefixExtraction(unittest.TestCase):
+ def __tryParse(self, string):
+ checkFile = checker.CheckFile(None, [])
+ return checkFile._extractLine("CHECK", string)
+
+ def test_InvalidFormat(self):
+ self.assertIsNone(self.__tryParse("CHECK"))
+ self.assertIsNone(self.__tryParse(":CHECK"))
+ self.assertIsNone(self.__tryParse("CHECK:"))
+ self.assertIsNone(self.__tryParse("//CHECK"))
+ self.assertIsNone(self.__tryParse("#CHECK"))
+
+ self.assertIsNotNone(self.__tryParse("//CHECK:foo"))
+ self.assertIsNotNone(self.__tryParse("#CHECK:bar"))
+
+ def test_InvalidLabel(self):
+ self.assertIsNone(self.__tryParse("//ACHECK:foo"))
+ self.assertIsNone(self.__tryParse("#ACHECK:foo"))
+
+ def test_NotFirstOnTheLine(self):
+ self.assertIsNone(self.__tryParse("A// CHECK: foo"))
+ self.assertIsNone(self.__tryParse("A # CHECK: foo"))
+ self.assertIsNone(self.__tryParse("// // CHECK: foo"))
+ self.assertIsNone(self.__tryParse("# # CHECK: foo"))
+
+ def test_WhitespaceAgnostic(self):
+ self.assertIsNotNone(self.__tryParse(" //CHECK: foo"))
+ self.assertIsNotNone(self.__tryParse("// CHECK: foo"))
+ self.assertIsNotNone(self.__tryParse(" //CHECK: foo"))
+ self.assertIsNotNone(self.__tryParse("// CHECK: foo"))
+
+
+class TestCheckLine_Parse(unittest.TestCase):
+ def __getPartPattern(self, linePart):
+ if linePart.variant == checker.CheckElement.Variant.Separator:
+ return "\s+"
+ else:
+ return linePart.pattern
+
+ def __getRegex(self, checkLine):
+ return "".join(map(lambda x: "(" + self.__getPartPattern(x) + ")", checkLine.lineParts))
+
+ def __tryParse(self, string):
+ return checker.CheckLine(string)
+
+ def __parsesTo(self, string, expected):
+ self.assertEqual(expected, self.__getRegex(self.__tryParse(string)))
+
+ def __tryParseNot(self, string):
+ return checker.CheckLine(string, checker.CheckLine.Variant.Not)
+
+ def __parsesPattern(self, string, pattern):
+ line = self.__tryParse(string)
+ self.assertEqual(1, len(line.lineParts))
+ self.assertEqual(checker.CheckElement.Variant.Pattern, line.lineParts[0].variant)
+ self.assertEqual(pattern, line.lineParts[0].pattern)
+
+ def __parsesVarRef(self, string, name):
+ line = self.__tryParse(string)
+ self.assertEqual(1, len(line.lineParts))
+ self.assertEqual(checker.CheckElement.Variant.VarRef, line.lineParts[0].variant)
+ self.assertEqual(name, line.lineParts[0].name)
+
+ def __parsesVarDef(self, string, name, body):
+ line = self.__tryParse(string)
+ self.assertEqual(1, len(line.lineParts))
+ self.assertEqual(checker.CheckElement.Variant.VarDef, line.lineParts[0].variant)
+ self.assertEqual(name, line.lineParts[0].name)
+ self.assertEqual(body, line.lineParts[0].pattern)
+
+ def __doesNotParse(self, string, partType):
+ line = self.__tryParse(string)
+ self.assertEqual(1, len(line.lineParts))
+ self.assertNotEqual(partType, line.lineParts[0].variant)
+
+ # Test that individual parts of the line are recognized
+
+ def test_TextOnly(self):
+ self.__parsesTo("foo", "(foo)")
+ self.__parsesTo(" foo ", "(foo)")
+ self.__parsesTo("f$o^o", "(f\$o\^o)")
+
+ def test_TextWithWhitespace(self):
+ self.__parsesTo("foo bar", "(foo)(\s+)(bar)")
+ self.__parsesTo("foo bar", "(foo)(\s+)(bar)")
+
+ def test_RegexOnly(self):
+ self.__parsesPattern("{{a?b.c}}", "a?b.c")
+
+ def test_VarRefOnly(self):
+ self.__parsesVarRef("[[ABC]]", "ABC")
+
+ def test_VarDefOnly(self):
+ self.__parsesVarDef("[[ABC:a?b.c]]", "ABC", "a?b.c")
+
+ def test_TextWithRegex(self):
+ self.__parsesTo("foo{{abc}}bar", "(foo)(abc)(bar)")
+
+ def test_TextWithVar(self):
+ self.__parsesTo("foo[[ABC:abc]]bar", "(foo)(abc)(bar)")
+
+ def test_PlainWithRegexAndWhitespaces(self):
+ self.__parsesTo("foo {{abc}}bar", "(foo)(\s+)(abc)(bar)")
+ self.__parsesTo("foo{{abc}} bar", "(foo)(abc)(\s+)(bar)")
+ self.__parsesTo("foo {{abc}} bar", "(foo)(\s+)(abc)(\s+)(bar)")
+
+ def test_PlainWithVarAndWhitespaces(self):
+ self.__parsesTo("foo [[ABC:abc]]bar", "(foo)(\s+)(abc)(bar)")
+ self.__parsesTo("foo[[ABC:abc]] bar", "(foo)(abc)(\s+)(bar)")
+ self.__parsesTo("foo [[ABC:abc]] bar", "(foo)(\s+)(abc)(\s+)(bar)")
+
+ def test_AllKinds(self):
+ self.__parsesTo("foo [[ABC:abc]]{{def}}bar", "(foo)(\s+)(abc)(def)(bar)")
+ self.__parsesTo("foo[[ABC:abc]] {{def}}bar", "(foo)(abc)(\s+)(def)(bar)")
+ self.__parsesTo("foo [[ABC:abc]] {{def}} bar", "(foo)(\s+)(abc)(\s+)(def)(\s+)(bar)")
+
+ # Test that variables and patterns are parsed correctly
+
+ def test_ValidPattern(self):
+ self.__parsesPattern("{{abc}}", "abc")
+ self.__parsesPattern("{{a[b]c}}", "a[b]c")
+ self.__parsesPattern("{{(a{bc})}}", "(a{bc})")
+
+ def test_ValidRef(self):
+ self.__parsesVarRef("[[ABC]]", "ABC")
+ self.__parsesVarRef("[[A1BC2]]", "A1BC2")
+
+ def test_ValidDef(self):
+ self.__parsesVarDef("[[ABC:abc]]", "ABC", "abc")
+ self.__parsesVarDef("[[ABC:ab:c]]", "ABC", "ab:c")
+ self.__parsesVarDef("[[ABC:a[b]c]]", "ABC", "a[b]c")
+ self.__parsesVarDef("[[ABC:(a[bc])]]", "ABC", "(a[bc])")
+
+ def test_Empty(self):
+ self.__doesNotParse("{{}}", checker.CheckElement.Variant.Pattern)
+ self.__doesNotParse("[[]]", checker.CheckElement.Variant.VarRef)
+ self.__doesNotParse("[[:]]", checker.CheckElement.Variant.VarDef)
+
+ def test_InvalidVarName(self):
+ self.__doesNotParse("[[0ABC]]", checker.CheckElement.Variant.VarRef)
+ self.__doesNotParse("[[AB=C]]", checker.CheckElement.Variant.VarRef)
+ self.__doesNotParse("[[ABC=]]", checker.CheckElement.Variant.VarRef)
+ self.__doesNotParse("[[0ABC:abc]]", checker.CheckElement.Variant.VarDef)
+ self.__doesNotParse("[[AB=C:abc]]", checker.CheckElement.Variant.VarDef)
+ self.__doesNotParse("[[ABC=:abc]]", checker.CheckElement.Variant.VarDef)
+
+ def test_BodyMatchNotGreedy(self):
+ self.__parsesTo("{{abc}}{{def}}", "(abc)(def)")
+ self.__parsesTo("[[ABC:abc]][[DEF:def]]", "(abc)(def)")
+
+ def test_NoVarDefsInNotChecks(self):
+ with self.assertRaises(CheckerException):
+ self.__tryParseNot("[[ABC:abc]]")
+
+class TestCheckLine_Match(unittest.TestCase):
+ def __matchSingle(self, checkString, outputString, varState={}):
+ checkLine = checker.CheckLine(checkString)
+ newVarState = checkLine.match(outputString, varState)
+ self.assertIsNotNone(newVarState)
+ return newVarState
+
+ def __notMatchSingle(self, checkString, outputString, varState={}):
+ checkLine = checker.CheckLine(checkString)
+ self.assertIsNone(checkLine.match(outputString, varState))
+
+ def test_TextAndWhitespace(self):
+ self.__matchSingle("foo", "foo")
+ self.__matchSingle("foo", " foo ")
+ self.__matchSingle("foo", "foo bar")
+ self.__notMatchSingle("foo", "XfooX")
+ self.__notMatchSingle("foo", "zoo")
+
+ self.__matchSingle("foo bar", "foo bar")
+ self.__matchSingle("foo bar", "abc foo bar def")
+ self.__matchSingle("foo bar", "foo foo bar bar")
+
+ self.__matchSingle("foo bar", "foo X bar")
+ self.__notMatchSingle("foo bar", "foo Xbar")
+
+ def test_Pattern(self):
+ self.__matchSingle("foo{{A|B}}bar", "fooAbar")
+ self.__matchSingle("foo{{A|B}}bar", "fooBbar")
+ self.__notMatchSingle("foo{{A|B}}bar", "fooCbar")
+
+ def test_VariableReference(self):
+ self.__matchSingle("foo[[X]]bar", "foobar", {"X": ""})
+ self.__matchSingle("foo[[X]]bar", "fooAbar", {"X": "A"})
+ self.__matchSingle("foo[[X]]bar", "fooBbar", {"X": "B"})
+ self.__notMatchSingle("foo[[X]]bar", "foobar", {"X": "A"})
+ self.__notMatchSingle("foo[[X]]bar", "foo bar", {"X": "A"})
+ with self.assertRaises(CheckerException):
+ self.__matchSingle("foo[[X]]bar", "foobar", {})
+
+ def test_VariableDefinition(self):
+ self.__matchSingle("foo[[X:A|B]]bar", "fooAbar")
+ self.__matchSingle("foo[[X:A|B]]bar", "fooBbar")
+ self.__notMatchSingle("foo[[X:A|B]]bar", "fooCbar")
+
+ env = self.__matchSingle("foo[[X:A.*B]]bar", "fooABbar", {})
+ self.assertEqual(env, {"X": "AB"})
+ env = self.__matchSingle("foo[[X:A.*B]]bar", "fooAxxBbar", {})
+ self.assertEqual(env, {"X": "AxxB"})
+
+ self.__matchSingle("foo[[X:A|B]]bar[[X]]baz", "fooAbarAbaz")
+ self.__matchSingle("foo[[X:A|B]]bar[[X]]baz", "fooBbarBbaz")
+ self.__notMatchSingle("foo[[X:A|B]]bar[[X]]baz", "fooAbarBbaz")
+
+ def test_NoVariableRedefinition(self):
+ with self.assertRaises(CheckerException):
+ self.__matchSingle("[[X:...]][[X]][[X:...]][[X]]", "foofoobarbar")
+
+ def test_EnvNotChangedOnPartialMatch(self):
+ env = {"Y": "foo"}
+ self.__notMatchSingle("[[X:A]]bar", "Abaz", env)
+ self.assertFalse("X" in env.keys())
+
+ def test_VariableContentEscaped(self):
+ self.__matchSingle("[[X:..]]foo[[X]]", ".*foo.*")
+ self.__notMatchSingle("[[X:..]]foo[[X]]", ".*fooAAAA")
+
+
+CheckVariant = checker.CheckLine.Variant
+
+def prepareSingleCheck(line):
+ if isinstance(line, str):
+ return checker.CheckLine(line)
+ else:
+ return checker.CheckLine(line[0], line[1])
+
+def prepareChecks(lines):
+ if isinstance(lines, str):
+ lines = lines.splitlines()
+ return list(map(lambda line: prepareSingleCheck(line), lines))
+
+
+class TestCheckGroup_Match(unittest.TestCase):
+ def __matchMulti(self, checkLines, outputString):
+ checkGroup = checker.CheckGroup("MyGroup", prepareChecks(checkLines))
+ outputGroup = checker.OutputGroup("MyGroup", outputString.splitlines())
+ return checkGroup.match(outputGroup)
+
+ def __notMatchMulti(self, checkString, outputString):
+ with self.assertRaises(CheckerException):
+ self.__matchMulti(checkString, outputString)
+
+ def test_TextAndPattern(self):
+ self.__matchMulti("""foo bar
+ abc {{def}}""",
+ """foo bar
+ abc def""");
+ self.__matchMulti("""foo bar
+ abc {{de.}}""",
+ """=======
+ foo bar
+ =======
+ abc de#
+ =======""");
+ self.__notMatchMulti("""//XYZ: foo bar
+ //XYZ: abc {{def}}""",
+ """=======
+ foo bar
+ =======
+ abc de#
+ =======""");
+
+ def test_Variables(self):
+ self.__matchMulti("""foo[[X:.]]bar
+ abc[[X]]def""",
+ """foo bar
+ abc def""");
+ self.__matchMulti("""foo[[X:([0-9]+)]]bar
+ abc[[X]]def
+ ### [[X]] ###""",
+ """foo1234bar
+ abc1234def
+ ### 1234 ###""");
+
+ def test_Ordering(self):
+ self.__matchMulti([("foo", CheckVariant.InOrder),
+ ("bar", CheckVariant.InOrder)],
+ """foo
+ bar""")
+ self.__notMatchMulti([("foo", CheckVariant.InOrder),
+ ("bar", CheckVariant.InOrder)],
+ """bar
+ foo""")
+ self.__matchMulti([("abc", CheckVariant.DAG),
+ ("def", CheckVariant.DAG)],
+ """abc
+ def""")
+ self.__matchMulti([("abc", CheckVariant.DAG),
+ ("def", CheckVariant.DAG)],
+ """def
+ abc""")
+ self.__matchMulti([("foo", CheckVariant.InOrder),
+ ("abc", CheckVariant.DAG),
+ ("def", CheckVariant.DAG),
+ ("bar", CheckVariant.InOrder)],
+ """foo
+ def
+ abc
+ bar""")
+ self.__notMatchMulti([("foo", CheckVariant.InOrder),
+ ("abc", CheckVariant.DAG),
+ ("def", CheckVariant.DAG),
+ ("bar", CheckVariant.InOrder)],
+ """foo
+ abc
+ bar""")
+ self.__notMatchMulti([("foo", CheckVariant.InOrder),
+ ("abc", CheckVariant.DAG),
+ ("def", CheckVariant.DAG),
+ ("bar", CheckVariant.InOrder)],
+ """foo
+ def
+ bar""")
+
+ def test_NotAssertions(self):
+ self.__matchMulti([("foo", CheckVariant.Not)],
+ """abc
+ def""")
+ self.__notMatchMulti([("foo", CheckVariant.Not)],
+ """abc foo
+ def""")
+ self.__notMatchMulti([("foo", CheckVariant.Not),
+ ("bar", CheckVariant.Not)],
+ """abc
+ def bar""")
+
+ def test_LineOnlyMatchesOnce(self):
+ self.__matchMulti([("foo", CheckVariant.DAG),
+ ("foo", CheckVariant.DAG)],
+ """foo
+ foo""")
+ self.__notMatchMulti([("foo", CheckVariant.DAG),
+ ("foo", CheckVariant.DAG)],
+ """foo
+ bar""")
+
+class TestOutputFile_Parse(unittest.TestCase):
+ def __parsesTo(self, string, expected):
+ if isinstance(string, str):
+ string = unicode(string)
+ outputStream = io.StringIO(string)
+ return self.assertEqual(checker.OutputFile(outputStream).groups, expected)
+
+ def test_NoInput(self):
+ self.__parsesTo(None, [])
+ self.__parsesTo("", [])
+
+ def test_SingleGroup(self):
+ self.__parsesTo("""begin_compilation
+ method "MyMethod"
+ end_compilation
+ begin_cfg
+ name "pass1"
+ foo
+ bar
+ end_cfg""",
+ [ checker.OutputGroup("MyMethod pass1", [ "foo", "bar" ]) ])
+
+ def test_MultipleGroups(self):
+ self.__parsesTo("""begin_compilation
+ name "xyz1"
+ method "MyMethod1"
+ date 1234
+ end_compilation
+ begin_cfg
+ name "pass1"
+ foo
+ bar
+ end_cfg
+ begin_cfg
+ name "pass2"
+ abc
+ def
+ end_cfg""",
+ [ checker.OutputGroup("MyMethod1 pass1", [ "foo", "bar" ]),
+ checker.OutputGroup("MyMethod1 pass2", [ "abc", "def" ]) ])
+
+ self.__parsesTo("""begin_compilation
+ name "xyz1"
+ method "MyMethod1"
+ date 1234
+ end_compilation
+ begin_cfg
+ name "pass1"
+ foo
+ bar
+ end_cfg
+ begin_compilation
+ name "xyz2"
+ method "MyMethod2"
+ date 5678
+ end_compilation
+ begin_cfg
+ name "pass2"
+ abc
+ def
+ end_cfg""",
+ [ checker.OutputGroup("MyMethod1 pass1", [ "foo", "bar" ]),
+ checker.OutputGroup("MyMethod2 pass2", [ "abc", "def" ]) ])
+
+class TestCheckFile_Parse(unittest.TestCase):
+ def __parsesTo(self, string, expected):
+ if isinstance(string, str):
+ string = unicode(string)
+ checkStream = io.StringIO(string)
+ return self.assertEqual(checker.CheckFile("CHECK", checkStream).groups, expected)
+
+ def test_NoInput(self):
+ self.__parsesTo(None, [])
+ self.__parsesTo("", [])
+
+ def test_SingleGroup(self):
+ self.__parsesTo("""// CHECK-START: Example Group
+ // CHECK: foo
+ // CHECK: bar""",
+ [ checker.CheckGroup("Example Group", prepareChecks([ "foo", "bar" ])) ])
+
+ def test_MultipleGroups(self):
+ self.__parsesTo("""// CHECK-START: Example Group1
+ // CHECK: foo
+ // CHECK: bar
+ // CHECK-START: Example Group2
+ // CHECK: abc
+ // CHECK: def""",
+ [ checker.CheckGroup("Example Group1", prepareChecks([ "foo", "bar" ])),
+ checker.CheckGroup("Example Group2", prepareChecks([ "abc", "def" ])) ])
+
+ def test_CheckVariants(self):
+ self.__parsesTo("""// CHECK-START: Example Group
+ // CHECK: foo
+ // CHECK-NOT: bar
+ // CHECK-DAG: abc
+ // CHECK-DAG: def""",
+ [ checker.CheckGroup("Example Group",
+ prepareChecks([ ("foo", CheckVariant.InOrder),
+ ("bar", CheckVariant.Not),
+ ("abc", CheckVariant.DAG),
+ ("def", CheckVariant.DAG) ])) ])
+
+if __name__ == '__main__':
+ checker.Logger.Verbosity = checker.Logger.Level.NoOutput
+ unittest.main()
diff --git a/tools/cpplint.py b/tools/cpplint.py
index c2f6514..4f063d9 100755
--- a/tools/cpplint.py
+++ b/tools/cpplint.py
@@ -3227,9 +3227,16 @@
# virtually indistinguishable from int(x) casts. Likewise, gMock's
# MockCallback takes a template parameter of the form return_type(arg_type),
# which looks much like the cast we're trying to detect.
+ # BEGIN android-added
+ # The C++ 2011 std::function class template exhibits a similar issue.
+ # END android-added
if (match.group(1) is None and # If new operator, then this isn't a cast
not (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or
- Match(r'^\s*MockCallback<.*>', line))):
+ # BEGIN android-changed
+ # Match(r'^\s*MockCallback<.*>', line))):
+ Match(r'^\s*MockCallback<.*>', line) or
+ Match(r'^\s*std::function<.*>', line))):
+ # END android-changed
# Try a bit harder to catch gmock lines: the only place where
# something looks like an old-style cast is where we declare the
# return type of the mocked method, and the only time when we
diff --git a/tools/dexfuzz/Android.mk b/tools/dexfuzz/Android.mk
new file mode 100644
index 0000000..d8f5582
--- /dev/null
+++ b/tools/dexfuzz/Android.mk
@@ -0,0 +1,37 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+# --- dexfuzz.jar ----------------
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAR_MANIFEST := manifest.txt
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE := dexfuzz
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+# --- dexfuzz script ----------------
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := dexfuzz
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/dexfuzz $(ACP)
+ @echo "Copy: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-new-target)
+ $(hide) chmod 755 $@
diff --git a/tools/dexfuzz/README b/tools/dexfuzz/README
new file mode 100644
index 0000000..c4795f2
--- /dev/null
+++ b/tools/dexfuzz/README
@@ -0,0 +1,130 @@
+DexFuzz
+=======
+
+DexFuzz is primarily a tool for fuzzing DEX files. Fuzzing is the introduction of
+subtle changes ("mutations") to a file to produce a new test case. These test cases
+can be used to test the various modes of execution available to ART (Interpreter,
+Quick compiler, Optimizing compiler) to check for bugs in these modes of execution.
+This is done by differential testing - each test file is executed with each mode of
+execution, and any differences between the resulting outputs may be an indication of
+a bug in one of the modes.
+
+For a wider overview of DexFuzz, see:
+
+http://community.arm.com/groups/android-community/blog/2014/11/26/the-art-of-fuzz-testing
+
+In typical operation, you provide DexFuzz with a set of DEX files that are the "seeds"
+for mutation - e.g. some tests taken from the ART test suite - and point it at an
+ADB-connected Android device, and it will fuzz these seed files, and execute the
+resulting new tests on the Android device.
+
+How to run DexFuzz
+==================
+
+1. Build dexfuzz with mmm tools/dexfuzz from within art/.
+2. Make sure you have an Android device connected via ADB, that is capable of
+ having DEX files pushed to it and executed with the dalvikvm command.
+3. Make sure you're in the Android build environment!
+ (That is, . build/envsetup.sh && lunch)
+4. Create a new directory, and place some DEX files in here. These are the seed files
+ that are mutated to form new tests.
+5. Create a directory on your device that mutated test files can be pushed to and
+ executed in, using dalvikvm. For example, /data/art-test/
+6. If you currently have multiple devices connected via ADB, find out the name of
+ your device using "adb devices -l".
+7. Run this command:
+
+dexfuzz --inputs=<seeds dir> --execute --repeat=<attempts> \
+ --dump-output <combination of ISA(s) and and backend(s)>
+
+You MUST specify one of the following ISAs:
+ --arm
+ --arm64
+ --x86
+ --x86_64
+ --mips
+ --mips64
+
+And also at least two of the following backends:
+ --interpreter
+ --quick
+ --optimizing
+
+Note that if you wanted to test both ARM and ARM64 on an ARM64 device, you can use
+--allarm. Also in this case only one backend is needed, if i.e., you wanted to test
+ARM Quick Backend vs. ARM64 Quick Backend.
+
+Some legal examples:
+ --arm --quick --optimizing
+ --x86 --quick --optimizing --interpreter
+ --allarm --quick
+
+Add in --device=<device name, e.g. device:generic> if you want to specify a device.
+Add in --execute-dir=<dir on device> if you want to specify an execution directory.
+ (The default is /data/art-test/)
+
+As the fuzzer works, you'll see output like:
+
+|-----------------------------------------------------------------|
+|Iterations|VerifyFail|MutateFail|Timed Out |Successful|Divergence|
+|-----------------------------------------------------------------|
+| 48 | 37 | 4 | 0 | 6 | 1 |
+
+Iterations - number of attempts we've made to mutate DEX files.
+VerifyFail - the number of mutated files that ended up failing to verify, either
+ on the host, or the target.
+MutateFail - because mutation is a random process, and has attempt thresholds to
+ avoid attempting to mutate a file indefinitely, it is possible that
+ an attempt to mutate a file doesn't actually mutate it. This counts
+ those occurrences.
+Timed Out - mutated files that timed out for one or more backends.
+ Current timeouts are:
+ Quick - 5 seconds
+ Optimizing - 5 seconds
+ Intepreter - 30 seconds
+ (use --short-timeouts to set all backends to 2 seconds.)
+Successful - mutated files that executed and all backends agreed on the resulting
+ output. NB: if all backends crashed with the same output, this would
+ be considered a success - proper detection of crashes is still to come.
+Divergence - mutated files that executed and some backend disagreed about the
+ resulting output. Divergent programs are run multiple times with a
+ single backend, to check if they diverge from themselves, and these are
+ not included in the count. If multiple architectures are being used
+ (ARM/ARM64), and the divergences align with different architectures,
+ these are also not included in the count.
+
+8. Check report.log for the full report, including input file and RNG seed for each
+ test program. This allows you to recreate a bad program with, e.g.:
+
+dexfuzz --input=<input file> --seed=<seed value>
+
+Check dexfuzz --help for the full list of options.
+
+NOTE: DEX files with unicode strings are not fully supported yet, and DEX files with
+JNI elements are not supported at all currently.
+
+Mutation Likelihoods
+====================
+
+Each bytecode mutation has a chance out of 100% of firing. Following is the listing
+of each mutation's probability. If you wish to easily adjust these values, copy
+these values into a file called likelihoods.txt, and run dexfuzz with
+--likelihoods=likelihoods.txt.
+
+ArithOpChanger 75
+BranchShifter 30
+CmpBiasChanger 30
+ConstantValueChanger 70
+ConversionRepeater 50
+FieldFlagChanger 40
+InstructionDeleter 40
+InstructionDuplicator 80
+InstructionSwapper 80
+NewMethodCaller 10
+NonsenseStringPrinter 10
+PoolIndexChanger 30
+RandomInstructionGenerator 30
+SwitchBranchShifter 30
+TryBlockShifter 40
+ValuePrinter 40
+VRegChanger 60
diff --git a/tools/dexfuzz/dexfuzz b/tools/dexfuzz/dexfuzz
new file mode 100755
index 0000000..cd47008
--- /dev/null
+++ b/tools/dexfuzz/dexfuzz
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Wrapper script for calling dexfuzz.jar.
+#
+DEBUG=
+#DEBUG="-Xdebug -Xrunjdwp:transport=dt_socket,address=127.0.0.1:8888,server=y,suspend=n -XX:+HeapDumpOnOutOfMemoryError -ea"
+
+java ${DEBUG} -jar ${ANDROID_HOST_OUT}/framework/dexfuzz.jar "$@"
diff --git a/tools/dexfuzz/manifest.txt b/tools/dexfuzz/manifest.txt
new file mode 100644
index 0000000..9e4c214
--- /dev/null
+++ b/tools/dexfuzz/manifest.txt
@@ -0,0 +1 @@
+Main-Class: dexfuzz.DexFuzz
diff --git a/tools/dexfuzz/src/dexfuzz/DexFuzz.java b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
new file mode 100644
index 0000000..2fb9663
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz;
+
+import dexfuzz.fuzzers.Fuzzer;
+import dexfuzz.fuzzers.FuzzerMultipleExecute;
+import dexfuzz.fuzzers.FuzzerMultipleNoExecute;
+import dexfuzz.fuzzers.FuzzerSingleExecute;
+import dexfuzz.fuzzers.FuzzerSingleNoExecute;
+import dexfuzz.listeners.BaseListener;
+import dexfuzz.listeners.ConsoleLoggerListener;
+import dexfuzz.listeners.LogFileListener;
+import dexfuzz.listeners.MultiplexerListener;
+import dexfuzz.listeners.UniqueProgramTrackerListener;
+import dexfuzz.listeners.UpdatingConsoleListener;
+
+/**
+ * Entrypoint class for dexfuzz.
+ */
+public class DexFuzz {
+ private static int majorVersion = 1;
+ private static int minorVersion = 0;
+ private static int seedChangeVersion = 0;
+
+ /**
+ * Entrypoint to dexfuzz.
+ */
+ public static void main(String[] args) {
+ // Report the version number, which should be incremented every time something will cause
+ // the same input seed to produce a different result than before.
+ Log.always(String.format("DexFuzz v%d.%d.%d",
+ majorVersion, minorVersion, seedChangeVersion));
+ Log.always("");
+
+ if (!Options.readOptions(args)) {
+ Log.error("Failed to validate options.");
+ Options.usage();
+ }
+
+ // Create the Listener, which will listen for events and report them.
+ BaseListener listener = null;
+ if (Options.repeat > 1 && Options.execute) {
+ // Create a Listener that is responsible for multiple Listeners.
+ MultiplexerListener multipleListener = new MultiplexerListener();
+ multipleListener.setup();
+ // Add the live updating listener, but only if we're not printing out lots of logs.
+ if (!Log.likelyToLog()) {
+ multipleListener.addListener(new UpdatingConsoleListener());
+ } else {
+ // If we are dumping out lots of logs, then use the ConsoleLogger instead.
+ multipleListener.addListener(new ConsoleLoggerListener());
+ }
+ // Add the file logging listener.
+ multipleListener.addListener(new LogFileListener(Options.reportLogFile));
+ // Add the unique program tracker.
+ multipleListener.addListener(new UniqueProgramTrackerListener(Options.uniqueDatabaseFile));
+ listener = multipleListener;
+ } else {
+ // Just use the basic listener.
+ listener = new ConsoleLoggerListener();
+ }
+
+ // Create the Fuzzer that uses a particular strategy for fuzzing.
+ Fuzzer fuzzer = null;
+ if ((Options.repeat > 1) && Options.execute) {
+ fuzzer = new FuzzerMultipleExecute(listener);
+ } else if ((Options.repeat > 1) && !Options.execute) {
+ fuzzer = new FuzzerMultipleNoExecute(listener);
+ } else if ((Options.repeat == 1) && Options.execute) {
+ fuzzer = new FuzzerSingleExecute(listener);
+ } else if ((Options.repeat == 1) && !Options.execute) {
+ fuzzer = new FuzzerSingleNoExecute(listener);
+ } else {
+ Log.errorAndQuit("Invalid options provided, desired fuzzer unknown.");
+ }
+ // TODO: Implement FuzzerFindMinimalMutations.
+ // TODO: Implement FuzzerGenerational.
+
+ // Actually run the Fuzzer.
+ fuzzer.run();
+ fuzzer.printTimingInfo();
+ fuzzer.shutdown();
+
+ // Cleanup the Listener.
+ listener.shutdown();
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/ExecutionResult.java b/tools/dexfuzz/src/dexfuzz/ExecutionResult.java
new file mode 100644
index 0000000..3a8c6cb
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/ExecutionResult.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz;
+
+import java.util.List;
+
+/**
+ * Stores the output of an executed command.
+ */
+public class ExecutionResult {
+ public List<String> output;
+ public List<String> error;
+ public int returnValue;
+
+ private String flattenedOutput;
+ private String flattenedOutputWithNewlines;
+ private String flattenedError;
+ private String flattenedErrorWithNewlines;
+ private String flattenedAll;
+
+ private static final int TIMEOUT_RETURN_VALUE = 124;
+ private static final int SIGABORT_RETURN_VALUE = 134;
+
+ /**
+ * Get only the output, with all lines concatenated together, excluding newline characters.
+ */
+ public String getFlattenedOutput() {
+ if (flattenedOutput == null) {
+ StringBuilder builder = new StringBuilder();
+ for (String line : output) {
+ builder.append(line);
+ }
+ flattenedOutput = builder.toString();
+ }
+ return flattenedOutput;
+ }
+
+ /**
+ * Get only the output, with all lines concatenated together, including newline characters.
+ */
+ public String getFlattenedOutputWithNewlines() {
+ if (flattenedOutputWithNewlines == null) {
+ StringBuilder builder = new StringBuilder();
+ for (String line : output) {
+ builder.append(line).append("\n");
+ }
+ flattenedOutputWithNewlines = builder.toString();
+ }
+ return flattenedOutputWithNewlines;
+ }
+
+ /**
+ * Get only the error, with all lines concatenated together, excluding newline characters.
+ */
+ public String getFlattenedError() {
+ if (flattenedError == null) {
+ StringBuilder builder = new StringBuilder();
+ for (String line : error) {
+ builder.append(line);
+ }
+ flattenedError = builder.toString();
+ }
+ return flattenedError;
+ }
+
+ /**
+ * Get only the error, with all lines concatenated together, including newline characters.
+ */
+ public String getFlattenedErrorWithNewlines() {
+ if (flattenedErrorWithNewlines == null) {
+ StringBuilder builder = new StringBuilder();
+ for (String line : error) {
+ builder.append(line).append("\n");
+ }
+ flattenedErrorWithNewlines = builder.toString();
+ }
+ return flattenedErrorWithNewlines;
+ }
+
+ /**
+ * Get both the output and error, concatenated together, excluding newline characters.
+ */
+ public String getFlattenedAll() {
+ if (flattenedAll == null) {
+ flattenedAll = getFlattenedOutput() + getFlattenedError();
+ }
+ return flattenedAll;
+ }
+
+ public boolean isTimeout() {
+ return (returnValue == TIMEOUT_RETURN_VALUE);
+ }
+
+ public boolean isSigabort() {
+ return (returnValue == SIGABORT_RETURN_VALUE);
+ }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/Log.java b/tools/dexfuzz/src/dexfuzz/Log.java
new file mode 100644
index 0000000..853550b
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/Log.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz;
+
+/**
+ * Provides access to the logging facilities of dexfuzz.
+ */
+public class Log {
+ private static LogTag threshold = LogTag.ERROR;
+
+ // Disable the constructor for this class.
+ private Log() { }
+
+ public static enum LogTag {
+ DEBUG,
+ INFO,
+ WARN,
+ ERROR,
+ ALWAYS
+ }
+
+ public static void setLoggingLevel(LogTag tag) {
+ threshold = tag;
+ }
+
+ public static boolean likelyToLog() {
+ return (threshold.ordinal() < LogTag.ERROR.ordinal());
+ }
+
+ public static void debug(String msg) {
+ log(LogTag.DEBUG, msg);
+ }
+
+ public static void info(String msg) {
+ log(LogTag.INFO, msg);
+ }
+
+ public static void warn(String msg) {
+ log(LogTag.WARN, msg);
+ }
+
+ public static void error(String msg) {
+ log(LogTag.ERROR, msg);
+ }
+
+ public static void always(String msg) {
+ System.out.println(msg);
+ }
+
+ private static void log(LogTag tag, String msg) {
+ if (tag.ordinal() >= threshold.ordinal()) {
+ System.out.println("[" + tag.toString() + "] " + msg);
+ }
+ }
+
+ /**
+ * Reports error and then terminates the program.
+ */
+ public static void errorAndQuit(String msg) {
+ error(msg);
+ // TODO: Signal sleeping threads.
+ System.exit(1);
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/MutationStats.java b/tools/dexfuzz/src/dexfuzz/MutationStats.java
new file mode 100644
index 0000000..c65b4f2
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/MutationStats.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A wrapper for a dictionary tracking what mutations have been performed.
+ */
+public class MutationStats {
+
+ public static class StatNotFoundException extends RuntimeException {
+ private static final long serialVersionUID = -7038515184655168470L;
+ }
+
+ private Map<String,Long> stats;
+ private List<String> statsOrder;
+
+ public MutationStats() {
+ stats = new HashMap<String,Long>();
+ statsOrder = new ArrayList<String>();
+ }
+
+ public void incrementStat(String statName) {
+ increaseStat(statName, 1);
+ }
+
+ /**
+ * Increase the named stat by the specified amount.
+ */
+ public void increaseStat(String statName, long amt) {
+ if (!stats.containsKey(statName)) {
+ stats.put(statName, 0L);
+ statsOrder.add(statName);
+ }
+ stats.put(statName, stats.get(statName) + amt);
+ }
+
+ /**
+ * Get a string representing the collected stats - looks like a JSON dictionary.
+ */
+ public String getStatsString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("{");
+ boolean first = true;
+ for (String statName : statsOrder) {
+ if (!first) {
+ builder.append(", ");
+ } else {
+ first = false;
+ }
+ builder.append("\"").append(statName).append("\": ").append(stats.get(statName));
+ }
+ builder.append("}");
+ return builder.toString();
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/Options.java b/tools/dexfuzz/src/dexfuzz/Options.java
new file mode 100644
index 0000000..1ae7b5e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/Options.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz;
+
+import dexfuzz.Log.LogTag;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Stores options for dexfuzz.
+ */
+public class Options {
+ /**
+ * Constructor has been disabled for this class, which should only be used statically.
+ */
+ private Options() { }
+
+ // KEY VALUE OPTIONS
+ public static final List<String> inputFileList = new ArrayList<String>();
+ public static String outputFile = "";
+ public static long rngSeed = -1;
+ public static boolean usingProvidedSeed = false;
+ public static int methodMutations = 3;
+ public static int minMethods = 2;
+ public static int maxMethods = 10;
+ public static final Map<String,Integer> mutationLikelihoods = new HashMap<String,Integer>();
+ public static String executeClass = "Main";
+ public static String deviceName = "";
+ public static boolean usingSpecificDevice = false;
+ public static int repeat = 1;
+ public static String executeDirectory = "/data/art-test";
+ public static String dumpMutationsFile = "mutations.dump";
+ public static String loadMutationsFile = "mutations.dump";
+ public static String reportLogFile = "report.log";
+ public static String uniqueDatabaseFile = "unique_progs.db";
+
+ // FLAG OPTIONS
+ public static boolean execute;
+ public static boolean local;
+ public static boolean noBootImage;
+ public static boolean useInterpreter;
+ public static boolean useQuick;
+ public static boolean useOptimizing;
+ public static boolean useArchArm;
+ public static boolean useArchArm64;
+ public static boolean useArchX86;
+ public static boolean useArchX86_64;
+ public static boolean useArchMips;
+ public static boolean useArchMips64;
+ public static boolean skipHostVerify;
+ public static boolean shortTimeouts;
+ public static boolean dumpOutput;
+ public static boolean dumpVerify;
+ public static boolean mutateLimit;
+ public static boolean reportUnique;
+ public static boolean skipMutation;
+ public static boolean dumpMutations;
+ public static boolean loadMutations;
+
+ /**
+ * Print out usage information about dexfuzz, and then exit.
+ */
+ public static void usage() {
+ Log.always("DexFuzz Usage:");
+ Log.always(" --input=<file> : Seed DEX file to be fuzzed");
+ Log.always(" (Can specify multiple times.)");
+ Log.always(" --inputs=<file> : Directory containing DEX files to be fuzzed.");
+ Log.always(" --output=<file> : Output DEX file to be produced");
+ Log.always("");
+ Log.always(" --execute : Execute the resulting fuzzed program");
+ Log.always(" --local : Execute on host (Not available yet.)");
+ Log.always(" --device=<device> : Execute on an ADB-connected-device, where <device> is");
+ Log.always(" the argument given to adb -s. Default execution mode.");
+ Log.always(" --execute-dir=<dir> : Push tests to this directory to execute them.");
+ Log.always(" (Default: /data/art-test)");
+ Log.always(" --no-boot-image : Use this flag when boot.art is not available.");
+ Log.always(" --skip-host-verify : When executing, skip host-verification stage");
+ Log.always(" --execute-class=<c> : When executing, execute this class (default: Main)");
+ Log.always("");
+ Log.always(" --interpreter : Include the Interpreter in comparisons");
+ Log.always(" --quick : Include the Quick Compiler in comparisons");
+ Log.always(" --optimizing : Include the Optimizing Compiler in comparisons");
+ Log.always("");
+ Log.always(" --arm : Include ARM backends in comparisons");
+ Log.always(" --arm64 : Include ARM64 backends in comparisons");
+ Log.always(" --allarm : Short for --arm --arm64");
+ Log.always(" --x86 : Include x86 backends in comparisons");
+ Log.always(" --x86-64 : Include x86-64 backends in comparisons");
+ Log.always(" --mips : Include MIPS backends in comparisons");
+ Log.always(" --mips64 : Include MIPS64 backends in comparisons");
+ Log.always("");
+ Log.always(" --dump-output : Dump outputs of executed programs");
+ Log.always(" --dump-verify : Dump outputs of verification");
+ Log.always(" --repeat=<n> : Fuzz N programs, executing each one.");
+ Log.always(" --short-timeouts : Shorten timeouts (faster; use if");
+ Log.always(" you want to focus on output divergences)");
+ Log.always(" --seed=<seed> : RNG seed to use");
+ Log.always(" --method-mutations=<n> : Maximum number of mutations to perform on each method.");
+ Log.always(" (Default: 3)");
+ Log.always(" --min-methods=<n> : Minimum number of methods to mutate. (Default: 2)");
+ Log.always(" --max-methods=<n> : Maximum number of methods to mutate. (Default: 10)");
+ Log.always(" --one-mutation : Short for --method-mutations=1 ");
+ Log.always(" --min-methods=1 --max-methods=1");
+ Log.always(" --likelihoods=<file> : A file containing a table of mutation likelihoods");
+ Log.always(" --mutate-limit : Mutate only methods whose names end with _MUTATE");
+ Log.always(" --skip-mutation : Do not actually mutate the input, just output it");
+ Log.always(" after parsing");
+ Log.always("");
+ Log.always(" --dump-mutations[=<file>] : Dump an editable set of mutations applied");
+ Log.always(" to <file> (default: mutations.dump)");
+ Log.always(" --load-mutations[=<file>] : Load and apply a set of mutations");
+ Log.always(" from <file> (default: mutations.dump)");
+ Log.always(" --log=<tag> : Set more verbose logging level: DEBUG, INFO, WARN");
+ Log.always(" --report=<file> : Use <file> to report results when using --repeat");
+ Log.always(" (Default: report.log)");
+ Log.always(" --report-unique : Print out information about unique programs generated");
+ Log.always(" --unique-db=<file> : Use <file> store results about unique programs");
+ Log.always(" (Default: unique_progs.db)");
+ Log.always("");
+ System.exit(0);
+ }
+
+ /**
+ * Given a flag option (one that does not feature an =), handle it
+ * accordingly. Report an error and print usage info if the flag is not
+ * recognised.
+ */
+ private static void handleFlagOption(String flag) {
+ if (flag.equals("execute")) {
+ execute = true;
+ } else if (flag.equals("local")) {
+ local = true;
+ } else if (flag.equals("no-boot-image")) {
+ noBootImage = true;
+ } else if (flag.equals("skip-host-verify")) {
+ skipHostVerify = true;
+ } else if (flag.equals("interpreter")) {
+ useInterpreter = true;
+ } else if (flag.equals("quick")) {
+ useQuick = true;
+ } else if (flag.equals("optimizing")) {
+ useOptimizing = true;
+ } else if (flag.equals("arm")) {
+ useArchArm = true;
+ } else if (flag.equals("arm64")) {
+ useArchArm64 = true;
+ } else if (flag.equals("allarm")) {
+ useArchArm = true;
+ useArchArm64 = true;
+ } else if (flag.equals("x86")) {
+ useArchX86 = true;
+ } else if (flag.equals("x86-64")) {
+ useArchX86_64 = true;
+ } else if (flag.equals("mips")) {
+ useArchMips = true;
+ } else if (flag.equals("mips64")) {
+ useArchMips64 = true;
+ } else if (flag.equals("mutate-limit")) {
+ mutateLimit = true;
+ } else if (flag.equals("report-unique")) {
+ reportUnique = true;
+ } else if (flag.equals("dump-output")) {
+ dumpOutput = true;
+ } else if (flag.equals("dump-verify")) {
+ dumpVerify = true;
+ } else if (flag.equals("short-timeouts")) {
+ shortTimeouts = true;
+ } else if (flag.equals("skip-mutation")) {
+ skipMutation = true;
+ } else if (flag.equals("dump-mutations")) {
+ dumpMutations = true;
+ } else if (flag.equals("load-mutations")) {
+ loadMutations = true;
+ } else if (flag.equals("one-mutation")) {
+ methodMutations = 1;
+ minMethods = 1;
+ maxMethods = 1;
+ } else if (flag.equals("help")) {
+ usage();
+ } else {
+ Log.error("Unrecognised flag: --" + flag);
+ usage();
+ }
+ }
+
+ /**
+ * Given a key-value option (one that features an =), handle it
+ * accordingly. Report an error and print usage info if the key is not
+ * recognised.
+ */
+ private static void handleKeyValueOption(String key, String value) {
+ if (key.equals("input")) {
+ inputFileList.add(value);
+ } else if (key.equals("inputs")) {
+ File folder = new File(value);
+ if (folder.listFiles() == null) {
+ Log.errorAndQuit("Specified argument to --inputs is not a directory!");
+ }
+ for (File file : folder.listFiles()) {
+ String inputName = value + "/" + file.getName();
+ Log.always("Adding " + inputName + " to input seed files.");
+ inputFileList.add(inputName);
+ }
+ } else if (key.equals("output")) {
+ outputFile = value;
+ } else if (key.equals("seed")) {
+ rngSeed = Long.parseLong(value);
+ usingProvidedSeed = true;
+ } else if (key.equals("method-mutations")) {
+ methodMutations = Integer.parseInt(value);
+ } else if (key.equals("min-methods")) {
+ minMethods = Integer.parseInt(value);
+ } else if (key.equals("max-methods")) {
+ maxMethods = Integer.parseInt(value);
+ } else if (key.equals("repeat")) {
+ repeat = Integer.parseInt(value);
+ } else if (key.equals("log")) {
+ Log.setLoggingLevel(LogTag.valueOf(value.toUpperCase()));
+ } else if (key.equals("likelihoods")) {
+ setupMutationLikelihoodTable(value);
+ } else if (key.equals("dump-mutations")) {
+ dumpMutations = true;
+ dumpMutationsFile = value;
+ } else if (key.equals("load-mutations")) {
+ loadMutations = true;
+ loadMutationsFile = value;
+ } else if (key.equals("report")) {
+ reportLogFile = value;
+ } else if (key.equals("unique-db")) {
+ uniqueDatabaseFile = value;
+ } else if (key.equals("execute-class")) {
+ executeClass = value;
+ } else if (key.equals("device")) {
+ deviceName = value;
+ usingSpecificDevice = true;
+ } else if (key.equals("execute-dir")) {
+ executeDirectory = value;
+ } else {
+ Log.error("Unrecognised key: --" + key);
+ usage();
+ }
+ }
+
+ private static void setupMutationLikelihoodTable(String tableFilename) {
+ try {
+ BufferedReader reader = new BufferedReader(new FileReader(tableFilename));
+ String line = reader.readLine();
+ while (line != null) {
+ line = line.replaceAll("\\s+", " ");
+ String[] entries = line.split(" ");
+ String name = entries[0].toLowerCase();
+ int likelihood = Integer.parseInt(entries[1]);
+ if (likelihood > 100) {
+ likelihood = 100;
+ }
+ if (likelihood < 0) {
+ likelihood = 0;
+ }
+ mutationLikelihoods.put(name, likelihood);
+ line = reader.readLine();
+ }
+ reader.close();
+ } catch (FileNotFoundException e) {
+ Log.error("Unable to open mutation probability table file: " + tableFilename);
+ } catch (IOException e) {
+ Log.error("Unable to read mutation probability table file: " + tableFilename);
+ }
+ }
+
+ /**
+ * Called by the DexFuzz class during program initialisation to parse
+ * the program's command line arguments.
+ * @return If options were successfully read and validated.
+ */
+ public static boolean readOptions(String[] args) {
+ for (String arg : args) {
+ if (!(arg.startsWith("--"))) {
+ Log.error("Unrecognised option: " + arg);
+ usage();
+ }
+
+ // cut off the --
+ arg = arg.substring(2);
+
+ // choose between a --X=Y option (keyvalue) and a --X option (flag)
+ if (arg.contains("=")) {
+ String[] split = arg.split("=");
+ handleKeyValueOption(split[0], split[1]);
+ } else {
+ handleFlagOption(arg);
+ }
+ }
+
+ return validateOptions();
+ }
+
+ /**
+ * Checks if the current options settings are valid, called after reading
+ * all options.
+ * @return If the options are valid or not.
+ */
+ private static boolean validateOptions() {
+ // Deal with option assumptions.
+ if (inputFileList.isEmpty()) {
+ File seedFile = new File("fuzzingseed.dex");
+ if (seedFile.exists()) {
+ Log.always("Assuming --input=fuzzingseed.dex");
+ inputFileList.add("fuzzingseed.dex");
+ } else {
+ Log.errorAndQuit("No input given, and couldn't find fuzzingseed.dex!");
+ return false;
+ }
+ }
+
+ if (outputFile.equals("")) {
+ Log.always("Assuming --output=fuzzingseed_fuzzed.dex");
+ outputFile = "fuzzingseed_fuzzed.dex";
+ }
+
+
+ if (mutationLikelihoods.isEmpty()) {
+ File likelihoodsFile = new File("likelihoods.txt");
+ if (likelihoodsFile.exists()) {
+ Log.always("Assuming --likelihoods=likelihoods.txt ");
+ setupMutationLikelihoodTable("likelihoods.txt");
+ } else {
+ Log.always("Using default likelihoods (see README for values)");
+ }
+ }
+
+ // Now check for hard failures.
+ if (repeat < 1) {
+ Log.error("--repeat must be at least 1!");
+ return false;
+ }
+ if (usingProvidedSeed && repeat > 1) {
+ Log.error("Cannot use --repeat with --seed");
+ return false;
+ }
+ if (loadMutations && dumpMutations) {
+ Log.error("Cannot both load and dump mutations");
+ return false;
+ }
+ if (repeat == 1 && inputFileList.size() > 1) {
+ Log.error("Must use --repeat if you have provided more than one input");
+ return false;
+ }
+ if (methodMutations < 0) {
+ Log.error("Cannot use --method-mutations with a negative value.");
+ return false;
+ }
+ if (minMethods < 0) {
+ Log.error("Cannot use --min-methods with a negative value.");
+ return false;
+ }
+ if (maxMethods < 0) {
+ Log.error("Cannot use --max-methods with a negative value.");
+ return false;
+ }
+ if (maxMethods < minMethods) {
+ Log.error("Cannot use --max-methods that's smaller than --min-methods");
+ return false;
+ }
+ if (local && usingSpecificDevice) {
+ Log.error("Cannot use --local and --device!");
+ return false;
+ }
+ if (execute) {
+ if (!(useArchArm
+ || useArchArm64
+ || useArchX86
+ || useArchX86_64
+ || useArchMips
+ || useArchMips64)) {
+ Log.error("No architecture to execute on was specified!");
+ return false;
+ }
+ if ((useArchArm || useArchArm64) && (useArchX86 || useArchX86_64)) {
+ Log.error("Did you mean to specify ARM and x86?");
+ return false;
+ }
+ if ((useArchArm || useArchArm64) && (useArchMips || useArchMips64)) {
+ Log.error("Did you mean to specify ARM and MIPS?");
+ return false;
+ }
+ if ((useArchX86 || useArchX86_64) && (useArchMips || useArchMips64)) {
+ Log.error("Did you mean to specify x86 and MIPS?");
+ return false;
+ }
+ int backends = 0;
+ if (useInterpreter) {
+ backends++;
+ }
+ if (useQuick) {
+ backends++;
+ }
+ if (useOptimizing) {
+ backends++;
+ }
+ if (useArchArm && useArchArm64) {
+ // Could just be comparing quick-ARM versus quick-ARM64?
+ backends++;
+ }
+ if (backends < 2) {
+ Log.error("Not enough backends specified! Try --quick --interpreter!");
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/StreamConsumer.java b/tools/dexfuzz/src/dexfuzz/StreamConsumer.java
new file mode 100644
index 0000000..cd93374
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/StreamConsumer.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+
+/**
+ * process.waitFor() can block if its output buffers are not drained.
+ * These threads are used to keep the buffers drained, and provide the final
+ * output once the command has finished executing. Each Executor has its own
+ * output and error StreamConsumers.
+ */
+public class StreamConsumer extends Thread {
+ private List<String> output;
+ private BufferedReader reader;
+
+ private State state;
+
+ private Semaphore workToBeDone;
+ private Semaphore outputIsReady;
+
+ enum State {
+ WAITING,
+ CONSUMING,
+ SHOULD_STOP_CONSUMING,
+ FINISHED,
+ ERROR
+ }
+
+ /**
+ * Create a StreamConsumer, will be immediately ready to start consuming.
+ */
+ public StreamConsumer() {
+ output = new ArrayList<String>();
+ workToBeDone = new Semaphore(0);
+ outputIsReady = new Semaphore(0);
+
+ state = State.WAITING;
+ }
+
+ /**
+ * Executor should call this to provide its StreamConsumers with the Streams
+ * for a Process it is about to call waitFor() on.
+ */
+ public void giveStreamAndStartConsuming(InputStream stream) {
+ output.clear();
+
+ reader = new BufferedReader(new InputStreamReader(stream));
+
+ changeState(State.CONSUMING, State.WAITING);
+
+ // Tell consumer there is work to be done.
+ workToBeDone.release();
+ }
+
+ /**
+ * Executor should call this once its call to waitFor() returns.
+ */
+ public void processFinished() {
+ changeState(State.SHOULD_STOP_CONSUMING, State.CONSUMING);
+ }
+
+ /**
+ * Executor should call this to get the captured output of this StreamConsumer.
+ */
+ public List<String> getOutput() {
+
+ try {
+ // Wait until the output is ready.
+ outputIsReady.acquire();
+ } catch (InterruptedException e) {
+ Log.error("Client of StreamConsumer was interrupted while waiting for output?");
+ return null;
+ }
+
+ // Take a copy of the Strings, so when we call output.clear(), we don't
+ // clear the ExecutionResult's list.
+ List<String> copy = new ArrayList<String>(output);
+ return copy;
+ }
+
+ /**
+ * Executor should call this when we're shutting down.
+ */
+ public void shutdown() {
+ changeState(State.FINISHED, State.WAITING);
+
+ // Tell Consumer there is work to be done (it will check first if FINISHED has been set.)
+ workToBeDone.release();
+ }
+
+ private void consume() {
+ try {
+
+ if (checkState(State.SHOULD_STOP_CONSUMING)) {
+ // Caller already called processFinished() before we even started
+ // consuming. Just get what we can and finish.
+ while (reader.ready()) {
+ output.add(reader.readLine());
+ }
+ } else {
+ // Caller's process is still executing, so just loop and consume.
+ while (checkState(State.CONSUMING)) {
+ Thread.sleep(50);
+ while (reader.ready()) {
+ output.add(reader.readLine());
+ }
+ }
+ }
+
+ if (checkState(State.SHOULD_STOP_CONSUMING)) {
+ changeState(State.WAITING, State.SHOULD_STOP_CONSUMING);
+ } else {
+ Log.error("StreamConsumer stopped consuming, but was not told to?");
+ setErrorState();
+ }
+
+ reader.close();
+
+ } catch (IOException e) {
+ Log.error("StreamConsumer caught IOException while consuming");
+ setErrorState();
+ } catch (InterruptedException e) {
+ Log.error("StreamConsumer caught InterruptedException while consuming");
+ setErrorState();
+ }
+
+ // Tell client of Consumer that the output is ready.
+ outputIsReady.release();
+ }
+
+ @Override
+ public void run() {
+ while (checkState(State.WAITING)) {
+ try {
+ // Wait until there is work to be done
+ workToBeDone.acquire();
+ } catch (InterruptedException e) {
+ Log.error("StreamConsumer caught InterruptedException while waiting for work");
+ setErrorState();
+ break;
+ }
+
+ // Check first if we're done
+ if (checkState(State.FINISHED)) {
+ break;
+ }
+
+ // Make sure we're either supposed to be consuming
+ // or supposed to be finishing up consuming
+ if (!(checkState(State.CONSUMING) || checkState(State.SHOULD_STOP_CONSUMING))) {
+ Log.error("invalid state: StreamConsumer told about work, but not CONSUMING?");
+ Log.error("state was: " + getCurrentState());
+ setErrorState();
+ break;
+ }
+
+ consume();
+ }
+ }
+
+ private synchronized boolean checkState(State expectedState) {
+ return (expectedState == state);
+ }
+
+ private synchronized void changeState(State newState, State previousState) {
+ if (state != previousState) {
+ Log.error("StreamConsumer Unexpected state: " + state + ", expected " + previousState);
+ state = State.ERROR;
+ } else {
+ state = newState;
+ }
+ }
+
+ private synchronized void setErrorState() {
+ state = State.ERROR;
+ }
+
+ private synchronized State getCurrentState() {
+ return state;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/Timer.java b/tools/dexfuzz/src/dexfuzz/Timer.java
new file mode 100644
index 0000000..8979b8a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/Timer.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz;
+
+import dexfuzz.listeners.BaseListener;
+
+/**
+ * For timing splits of program execution.
+ */
+public class Timer {
+ /**
+ * The name of the timer, the phase of the program it is intended to time.
+ */
+ private String name;
+
+ /**
+ * A point in time remembered when start() is called.
+ */
+ private long startPoint;
+
+ /**
+ * A cumulative count of how much time has elapsed. Updated each time
+ * stop() is called.
+ */
+ private long elapsedTime;
+
+ /**
+ * Initialise a new timer with the provided name.
+ */
+ public Timer(String name) {
+ this.name = name;
+ this.elapsedTime = 0L;
+ }
+
+ /**
+ * Start timing.
+ */
+ public void start() {
+ startPoint = System.currentTimeMillis();
+ }
+
+ /**
+ * Stop timing, update how much time has elapsed.
+ */
+ public void stop() {
+ long endPoint = System.currentTimeMillis();
+ elapsedTime += (endPoint - startPoint);
+ }
+
+ /**
+ * Log the elapsed time this timer has recorded.
+ */
+ public void printTime(BaseListener listener) {
+ listener.handleTiming(name, ((float)elapsedTime) / 1000.0f);
+ }
+}
diff --git a/runtime/arch/arm64/portable_entrypoints_arm64.S b/tools/dexfuzz/src/dexfuzz/executors/Architecture.java
similarity index 63%
copy from runtime/arch/arm64/portable_entrypoints_arm64.S
copy to tools/dexfuzz/src/dexfuzz/executors/Architecture.java
index 9e2c030..5cdabc3 100644
--- a/runtime/arch/arm64/portable_entrypoints_arm64.S
+++ b/tools/dexfuzz/src/dexfuzz/executors/Architecture.java
@@ -14,17 +14,18 @@
* limitations under the License.
*/
-#include "asm_support_arm64.S"
+package dexfuzz.executors;
- /*
- * Portable invocation stub.
- */
-UNIMPLEMENTED art_portable_invoke_stub
-
-UNIMPLEMENTED art_portable_proxy_invoke_handler
-
-UNIMPLEMENTED art_portable_resolution_trampoline
-
-UNIMPLEMENTED art_portable_to_interpreter_bridge
-
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
+/**
+ * Every Executor must specify an Architecture. It is important that when reduced
+ * to lower case, these match the ISA string that ART would produce. For example,
+ * the architecture directory used for /data/dalvik-cache/${ISA}
+ */
+public enum Architecture {
+ ARM,
+ ARM64,
+ X86,
+ X86_64,
+ MIPS,
+ MIPS64
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64InterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64InterpreterExecutor.java
new file mode 100644
index 0000000..a945283
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Arm64InterpreterExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Arm64InterpreterExecutor extends Executor {
+
+ public Arm64InterpreterExecutor(BaseListener listener, Device device) {
+ super("ARM64 Interpreter", 30, listener, Architecture.ARM64, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm64 -Xint ");
+ if (device.noBootImageAvailable()) {
+ commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+ }
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/arm64/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return false;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java
new file mode 100644
index 0000000..2204ba8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Arm64OptimizingBackendExecutor extends Executor {
+
+ public Arm64OptimizingBackendExecutor(BaseListener listener, Device device) {
+ super("ARM64 Optimizing Backend", 5, listener, Architecture.ARM64, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
+ if (device.noBootImageAvailable()) {
+ commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+ }
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/arm64/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return true;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java
new file mode 100644
index 0000000..55c9c7a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Arm64QuickBackendExecutor extends Executor {
+
+ public Arm64QuickBackendExecutor(BaseListener listener, Device device) {
+ super("ARM64 Quick Backend", 5, listener, Architecture.ARM64, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm64 ");
+ if (device.noBootImageAvailable()) {
+ commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+ }
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/arm64/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return true;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmInterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmInterpreterExecutor.java
new file mode 100644
index 0000000..68ce2e0
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/ArmInterpreterExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class ArmInterpreterExecutor extends Executor {
+
+ public ArmInterpreterExecutor(BaseListener listener, Device device) {
+ super("ARM Interpreter", 30, listener, Architecture.ARM, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm32 -Xint ");
+ if (device.noBootImageAvailable()) {
+ commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+ }
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/arm/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return false;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java
new file mode 100644
index 0000000..78cf652
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class ArmOptimizingBackendExecutor extends Executor {
+
+ public ArmOptimizingBackendExecutor(BaseListener listener, Device device) {
+ super("ARM Optimizing Backend", 5, listener, Architecture.ARM, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
+ if (device.noBootImageAvailable()) {
+ commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+ }
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/arm/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return true;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java
new file mode 100644
index 0000000..8f026b2
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class ArmQuickBackendExecutor extends Executor {
+
+ public ArmQuickBackendExecutor(BaseListener listener, Device device) {
+ super("ARM Quick Backend", 5, listener, Architecture.ARM, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm32 ");
+ if (device.noBootImageAvailable()) {
+ commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+ }
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/arm/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return true;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Device.java b/tools/dexfuzz/src/dexfuzz/executors/Device.java
new file mode 100644
index 0000000..8c03103
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Device.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+/**
+ * Handles execution either on a remote device, or locally.
+ * Currently only remote execution, on an ADB-connected device, is supported.
+ */
+public class Device {
+ private boolean isLocal;
+ private String deviceName;
+ private boolean usingSpecificDevice;
+ private boolean noBootImage;
+
+ /**
+ * The constructor for a local "device". Not yet supported.
+ */
+ public Device() {
+ this.isLocal = true;
+ throw new UnsupportedOperationException("Currently local execution is not supported.");
+ }
+
+ /**
+ * The constructor for an ADB connected device.
+ */
+ public Device(String deviceName, boolean noBootImage) {
+ if (!deviceName.isEmpty()) {
+ this.deviceName = deviceName;
+ this.usingSpecificDevice = true;
+ }
+ this.noBootImage = noBootImage;
+ }
+
+ /**
+ * Get the name that would be provided to adb -s to communicate specifically with this device.
+ */
+ public String getName() {
+ if (isLocal) {
+ return "LOCAL DEVICE";
+ }
+ return deviceName;
+ }
+
+ public boolean isLocal() {
+ return isLocal;
+ }
+
+ /**
+ * Certain AOSP builds of Android may not have a full boot.art built. This will be set if
+ * we use --no-boot-image, and is used by Executors when deciding the arguments for dalvikvm
+ * and dex2oat when performing host-side verification.
+ */
+ public boolean noBootImageAvailable() {
+ return noBootImage;
+ }
+
+ /**
+ * Get the command prefix for this device if we want to use adb shell.
+ */
+ public String getExecutionShellPrefix() {
+ if (isLocal) {
+ return "";
+ }
+ return getExecutionPrefixWithAdb("shell");
+ }
+
+ /**
+ * Get the command prefix for this device if we want to use adb push.
+ */
+ public String getExecutionPushPrefix() {
+ if (isLocal) {
+ return "";
+ }
+ return getExecutionPrefixWithAdb("push");
+ }
+
+ private String getExecutionPrefixWithAdb(String command) {
+ if (usingSpecificDevice) {
+ return String.format("adb -s %s %s ", deviceName, command);
+ } else {
+ return String.format("adb %s ", command);
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Executor.java b/tools/dexfuzz/src/dexfuzz/executors/Executor.java
new file mode 100644
index 0000000..7cc584d
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Executor.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.Log;
+import dexfuzz.Options;
+import dexfuzz.StreamConsumer;
+import dexfuzz.listeners.BaseListener;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Base class containing the common methods for executing a particular backend of ART.
+ */
+public abstract class Executor {
+ private String androidHostOut;
+ private String androidProductOut;
+
+ private StreamConsumer outputConsumer;
+ private StreamConsumer errorConsumer;
+
+ protected ExecutionResult executionResult;
+ protected String executeClass;
+
+ // Set by subclasses.
+ protected String name;
+ protected int timeout;
+ protected BaseListener listener;
+ protected String testLocation;
+ protected Architecture architecture;
+ protected Device device;
+
+ protected Executor(String name, int timeout, BaseListener listener, Architecture architecture,
+ Device device) {
+ executeClass = Options.executeClass;
+
+ if (Options.shortTimeouts) {
+ this.timeout = 2;
+ } else {
+ this.timeout = timeout;
+ }
+
+ this.name = name;
+ this.listener = listener;
+ this.architecture = architecture;
+ this.device = device;
+
+ this.testLocation = Options.executeDirectory;
+
+ Map<String, String> envVars = System.getenv();
+ androidProductOut = checkForEnvVar(envVars, "ANDROID_PRODUCT_OUT");
+ androidHostOut = checkForEnvVar(envVars, "ANDROID_HOST_OUT");
+
+ outputConsumer = new StreamConsumer();
+ outputConsumer.start();
+ errorConsumer = new StreamConsumer();
+ errorConsumer.start();
+
+ if (!device.isLocal()) {
+ // Check for ADB.
+ try {
+ ProcessBuilder pb = new ProcessBuilder();
+ pb.command("adb", "devices");
+ Process process = pb.start();
+ int exitValue = process.waitFor();
+ if (exitValue != 0) {
+ Log.errorAndQuit("Problem executing ADB - is it in your $PATH?");
+ }
+ } catch (IOException e) {
+ Log.errorAndQuit("IOException when executing ADB, is it working?");
+ } catch (InterruptedException e) {
+ Log.errorAndQuit("InterruptedException when executing ADB, is it working?");
+ }
+
+ // Check we can run something on ADB.
+ ExecutionResult result = executeOnDevice("true", true);
+ if (result.getFlattenedAll().contains("device not found")) {
+ Log.errorAndQuit("Couldn't connect to specified ADB device: " + device.getName());
+ }
+ }
+ }
+
+ private String checkForEnvVar(Map<String, String> envVars, String key) {
+ if (!envVars.containsKey(key)) {
+ Log.errorAndQuit("Cannot run a fuzzed program if $" + key + " is not set!");
+ }
+ return envVars.get(key);
+ }
+
+ private ExecutionResult executeCommand(String command, boolean captureOutput) {
+ ExecutionResult result = new ExecutionResult();
+
+ Log.info("Executing: " + command);
+
+ try {
+ ProcessBuilder processBuilder = new ProcessBuilder(command.split(" "));
+ processBuilder.environment().put("ANDROID_ROOT", androidHostOut);
+ Process process = processBuilder.start();
+
+ if (captureOutput) {
+ // Give the streams to the StreamConsumers.
+ outputConsumer.giveStreamAndStartConsuming(process.getInputStream());
+ errorConsumer.giveStreamAndStartConsuming(process.getErrorStream());
+ }
+
+ // Wait until the process is done - the StreamConsumers will keep the
+ // buffers drained, so this shouldn't block indefinitely.
+ // Get the return value as well.
+ result.returnValue = process.waitFor();
+
+ Log.info("Return value: " + result.returnValue);
+
+ if (captureOutput) {
+ // Tell the StreamConsumers to stop consuming, and wait for them to finish
+ // so we know we have all of the output.
+ outputConsumer.processFinished();
+ errorConsumer.processFinished();
+ result.output = outputConsumer.getOutput();
+ result.error = errorConsumer.getOutput();
+
+ // Always explicitly indicate the return code in the text output now.
+ // NB: adb shell doesn't actually return exit codes currently, but this will
+ // be useful if/when it does.
+ result.output.add("RETURN CODE: " + result.returnValue);
+ }
+
+ } catch (IOException e) {
+ Log.errorAndQuit("ExecutionResult.execute() caught an IOException");
+ } catch (InterruptedException e) {
+ Log.errorAndQuit("ExecutionResult.execute() caught an InterruptedException");
+ }
+
+ return result;
+ }
+
+ /**
+ * Called by subclass Executors in their execute() implementations.
+ */
+ protected ExecutionResult executeOnDevice(String command, boolean captureOutput) {
+ String timeoutString = "timeout " + timeout + " ";
+ return executeCommand(timeoutString + device.getExecutionShellPrefix() + command,
+ captureOutput);
+ }
+
+ private ExecutionResult pushToDevice(String command) {
+ return executeCommand(device.getExecutionPushPrefix() + command, false);
+ }
+
+ /**
+ * Call this to make sure the StreamConsumer threads are stopped.
+ */
+ public void shutdown() {
+ outputConsumer.shutdown();
+ errorConsumer.shutdown();
+ }
+
+ /**
+ * Called by the Fuzzer after each execution has finished, to clear the results.
+ */
+ public void reset() {
+ executionResult = null;
+ }
+
+ /**
+ * Called by the Fuzzer to verify the mutated program using the host-side dex2oat.
+ */
+ public boolean verifyOnHost(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dex2oat ");
+
+ // This assumes that the Architecture enum's name, when reduced to lower-case,
+ // matches what dex2oat would expect.
+ commandBuilder.append("--instruction-set=").append(architecture.toString().toLowerCase());
+ commandBuilder.append(" --instruction-set-features=default ");
+
+ // Select the correct boot image.
+ commandBuilder.append("--boot-image=").append(androidProductOut);
+ if (device.noBootImageAvailable()) {
+ commandBuilder.append("/data/art-test/core.art ");
+ } else {
+ commandBuilder.append("/system/framework/boot.art ");
+ }
+
+ commandBuilder.append("--oat-file=output.oat ");
+ commandBuilder.append("--android-root=").append(androidHostOut).append(" ");
+ commandBuilder.append("--runtime-arg -classpath ");
+ commandBuilder.append("--runtime-arg ").append(programName).append(" ");
+ commandBuilder.append("--dex-file=").append(programName).append(" ");
+ commandBuilder.append("--compiler-filter=interpret-only --runtime-arg -Xnorelocate ");
+
+ ExecutionResult verificationResult = executeCommand(commandBuilder.toString(), true);
+
+ boolean success = true;
+
+ if (verificationResult.isSigabort()) {
+ listener.handleHostVerificationSigabort(verificationResult);
+ success = false;
+ }
+
+ if (success) {
+ // Search for a keyword that indicates verification was not successful.
+ // TODO: Determine if dex2oat crashed?
+ for (String line : verificationResult.error) {
+ if (line.contains("Verification error")
+ || line.contains("Failure to verify dex file")) {
+ success = false;
+ }
+ if (Options.dumpVerify) {
+ // Strip out the start of the log lines.
+ listener.handleDumpVerify(line.replaceFirst(".*(cc|h):\\d+] ", ""));
+ }
+ }
+ }
+
+ if (!success) {
+ listener.handleFailedHostVerification(verificationResult);
+ }
+
+ executeCommand("rm output.oat", false);
+
+ return success;
+ }
+
+ /**
+ * Called by the Fuzzer to upload the program to the target device.
+ * TODO: Check if we're executing on a local device, and don't do this?
+ */
+ public void uploadToTarget(String programName) {
+ pushToDevice(programName + " " + testLocation);
+ }
+
+ /**
+ * Executor subclasses need to override this, to construct their arguments for dalvikvm
+ * invocation correctly.
+ */
+ public abstract void execute(String programName);
+
+ /**
+ * Executor subclasses need to override this, to delete their generated OAT file correctly.
+ */
+ public abstract void deleteGeneratedOatFile(String programName);
+
+ /**
+ * Executor subclasses need to override this, to report if they need a cleaned code cache.
+ */
+ public abstract boolean needsCleanCodeCache();
+
+ /**
+ * Fuzzer.checkForArchitectureSplit() will use this determine the architecture of the Executor.
+ */
+ public Architecture getArchitecture() {
+ return architecture;
+ }
+
+ /**
+ * Used in each subclass of Executor's deleteGeneratedOatFile() method, to know what to delete.
+ */
+ protected String getOatFileName(String programName) {
+ // Converts e.g. /data/art-test/file.dex to data@art-test@file.dex
+ return (testLocation.replace("/", "@").substring(1) + "@" + programName);
+ }
+
+ /**
+ * Used by the Fuzzer to get result of execution.
+ */
+ public ExecutionResult getResult() {
+ return executionResult;
+ }
+
+ /**
+ * Because dex2oat can accept a program with soft errors on the host, and then fail after
+ * performing hard verification on the target, we need to check if the Executor detected
+ * a target verification failure, before doing anything else with the resulting output.
+ * Used by the Fuzzer.
+ */
+ public boolean verifyOnTarget() {
+ // TODO: Remove this once host-verification can be forced to always fail?
+ if (executionResult.getFlattenedOutput().contains("VerifyError")) {
+ return false;
+ }
+ return true;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64InterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64InterpreterExecutor.java
new file mode 100644
index 0000000..9f27b5e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Mips64InterpreterExecutor.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Mips64InterpreterExecutor extends Executor {
+
+ public Mips64InterpreterExecutor(BaseListener listener, Device device) {
+ super("MIPS64 Interpreter", 30, listener, Architecture.MIPS64, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm64 -Xint ");
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/mips64/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java
new file mode 100644
index 0000000..b30240d
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Mips64OptimizingBackendExecutor extends Executor {
+
+ public Mips64OptimizingBackendExecutor(BaseListener listener, Device device) {
+ super("MIPS64 Optimizing Backend", 5, listener, Architecture.MIPS64, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/mips64/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return true;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java
new file mode 100644
index 0000000..42ccd1e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Mips64QuickBackendExecutor extends Executor {
+
+ public Mips64QuickBackendExecutor(BaseListener listener, Device device) {
+ super("MIPS64 Quick Backend", 5, listener, Architecture.MIPS64, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm64 ");
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/mips64/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return true;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsInterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsInterpreterExecutor.java
new file mode 100644
index 0000000..524eaa9
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/MipsInterpreterExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class MipsInterpreterExecutor extends Executor {
+
+ public MipsInterpreterExecutor(BaseListener listener, Device device) {
+ super("MIPS Interpreter", 30, listener, Architecture.MIPS, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm32 -Xint ");
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/mips/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return false;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java
new file mode 100644
index 0000000..fcc92c8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class MipsOptimizingBackendExecutor extends Executor {
+
+ public MipsOptimizingBackendExecutor(BaseListener listener, Device device) {
+ super("MIPS Optimizing Backend", 5, listener, Architecture.MIPS, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/mips/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return true;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java
new file mode 100644
index 0000000..cb442f9
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class MipsQuickBackendExecutor extends Executor {
+
+ public MipsQuickBackendExecutor(BaseListener listener, Device device) {
+ super("MIPS Quick Backend", 5, listener, Architecture.MIPS, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm32 ");
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/mips/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return true;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86InterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86InterpreterExecutor.java
new file mode 100644
index 0000000..93c14e9
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86InterpreterExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86InterpreterExecutor extends Executor {
+
+ public X86InterpreterExecutor(BaseListener listener, Device device) {
+ super("x86 Interpreter", 30, listener, Architecture.X86, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm32 -Xint ");
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/x86/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return false;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java
new file mode 100644
index 0000000..b27d5ca
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86OptimizingBackendExecutor extends Executor {
+
+ public X86OptimizingBackendExecutor(BaseListener listener, Device device) {
+ super("x86 Optimizing Backend", 5, listener, Architecture.X86, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/x86/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return true;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java
new file mode 100644
index 0000000..d8ec217
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86QuickBackendExecutor extends Executor {
+
+ public X86QuickBackendExecutor(BaseListener listener, Device device) {
+ super("x86 Quick Backend", 5, listener, Architecture.X86, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm32 ");
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/x86/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return true;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64InterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64InterpreterExecutor.java
new file mode 100644
index 0000000..7497322
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86_64InterpreterExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86_64InterpreterExecutor extends Executor {
+
+ public X86_64InterpreterExecutor(BaseListener listener, Device device) {
+ super("x86_64 Interpreter", 30, listener, Architecture.X86_64, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm64 -Xint ");
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/x86_64/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return false;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java
new file mode 100644
index 0000000..a978f73
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86_64OptimizingBackendExecutor extends Executor {
+
+ public X86_64OptimizingBackendExecutor(BaseListener listener, Device device) {
+ super("x86_64 Optimizing Backend", 5, listener, Architecture.X86_64, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/x86_64/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return true;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java
new file mode 100644
index 0000000..85532d8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86_64QuickBackendExecutor extends Executor {
+
+ public X86_64QuickBackendExecutor(BaseListener listener, Device device) {
+ super("x86_64 Quick Backend", 5, listener, Architecture.X86_64, device);
+ }
+
+ @Override
+ public void execute(String programName) {
+ StringBuilder commandBuilder = new StringBuilder();
+ commandBuilder.append("dalvikvm64 ");
+ commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+ commandBuilder.append(executeClass);
+ executionResult = executeOnDevice(commandBuilder.toString(), true);
+ }
+
+ @Override
+ public void deleteGeneratedOatFile(String programName) {
+ String command = "rm -f /data/dalvik-cache/x86_64/" + getOatFileName(programName);
+ executeOnDevice(command, false);
+ }
+
+ @Override
+ public boolean needsCleanCodeCache() {
+ return true;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java
new file mode 100644
index 0000000..4c1acdb
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.Log;
+import dexfuzz.Options;
+import dexfuzz.Timer;
+import dexfuzz.executors.Architecture;
+import dexfuzz.executors.Arm64InterpreterExecutor;
+import dexfuzz.executors.Arm64OptimizingBackendExecutor;
+import dexfuzz.executors.Arm64QuickBackendExecutor;
+import dexfuzz.executors.ArmInterpreterExecutor;
+import dexfuzz.executors.ArmOptimizingBackendExecutor;
+import dexfuzz.executors.ArmQuickBackendExecutor;
+import dexfuzz.executors.Device;
+import dexfuzz.executors.Executor;
+import dexfuzz.executors.Mips64InterpreterExecutor;
+import dexfuzz.executors.Mips64OptimizingBackendExecutor;
+import dexfuzz.executors.Mips64QuickBackendExecutor;
+import dexfuzz.executors.MipsInterpreterExecutor;
+import dexfuzz.executors.MipsOptimizingBackendExecutor;
+import dexfuzz.executors.MipsQuickBackendExecutor;
+import dexfuzz.executors.X86InterpreterExecutor;
+import dexfuzz.executors.X86OptimizingBackendExecutor;
+import dexfuzz.executors.X86QuickBackendExecutor;
+import dexfuzz.executors.X86_64InterpreterExecutor;
+import dexfuzz.executors.X86_64OptimizingBackendExecutor;
+import dexfuzz.executors.X86_64QuickBackendExecutor;
+import dexfuzz.listeners.BaseListener;
+import dexfuzz.program.Mutation;
+import dexfuzz.program.Program;
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.OffsetTracker;
+import dexfuzz.rawdex.RawDexFile;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A particular fuzzing strategy, this class provides the common methods
+ * most fuzzing will involve, and subclasses override the run() method, to
+ * employ a particular strategy.
+ */
+public abstract class Fuzzer {
+ private List<Executor> executors;
+ private OffsetTracker offsetTracker;
+
+ /**
+ * This is the executor that we use to test for self-divergent programs.
+ */
+ private Executor goldenExecutor;
+
+ /*
+ * These two flags are set during fuzz(), and then cleared at the end of execute().
+ */
+ private boolean mutatedSuccessfully;
+ private boolean savedSuccessfully;
+
+ private Timer totalTimer = new Timer("Total Time");
+ private Timer timerDexInput = new Timer("DEX Input");
+ private Timer timerProgGen = new Timer("Program Generation");
+ private Timer timerMutation = new Timer("Mutation Time");
+ private Timer timerDexOutput = new Timer("DEX Output");
+ private Timer timerChecksumCalc = new Timer("Checksum Calculation");
+
+ protected BaseListener listener;
+
+ protected Fuzzer(BaseListener listener) {
+ totalTimer.start();
+ executors = new ArrayList<Executor>();
+ this.listener = listener;
+ }
+
+ public abstract void run();
+
+ protected abstract String getNextInputFilename();
+
+ protected abstract String getNextOutputFilename();
+
+ /**
+ * Call this after fuzzer execution to print out timing results.
+ */
+ public void printTimingInfo() {
+ totalTimer.stop();
+ timerDexInput.printTime(listener);
+ timerProgGen.printTime(listener);
+ timerMutation.printTime(listener);
+ timerDexOutput.printTime(listener);
+ timerChecksumCalc.printTime(listener);
+ totalTimer.printTime(listener);
+ }
+
+ /**
+ * Make sure this is called to correctly shutdown each Executor's StreamConsumers.
+ */
+ public void shutdown() {
+ if (executors != null) {
+ for (Executor executor : executors) {
+ executor.shutdown();
+ }
+ }
+ }
+
+ private void addExecutorsForArchitecture(Device device, Class<? extends Executor> quick,
+ Class<? extends Executor> optimizing, Class<? extends Executor> interpreter) {
+ // NB: Currently QuickBackend MUST come immediately before same arch's Interpreter.
+ // This is because intepreter execution relies on there being an OAT file already
+ // created to produce correct debug information. Otherwise we will see
+ // false-positive divergences.
+ try {
+ if (Options.useQuick) {
+ Constructor<? extends Executor> constructor =
+ quick.getConstructor(BaseListener.class, Device.class);
+ executors.add(constructor.newInstance(listener, device));
+ }
+ if (Options.useOptimizing) {
+ Constructor<? extends Executor> constructor =
+ optimizing.getConstructor(BaseListener.class, Device.class);
+ executors.add(constructor.newInstance(listener, device));
+ }
+ if (Options.useInterpreter) {
+ Constructor<? extends Executor> constructor =
+ interpreter.getConstructor(BaseListener.class, Device.class);
+ executors.add(constructor.newInstance(listener, device));
+ }
+ } catch (NoSuchMethodException e) {
+ Log.errorAndQuit("Executor doesn't have correct constructor.");
+ } catch (InstantiationException e) {
+ Log.errorAndQuit("Executor couldn't be instantiated.");
+ } catch (IllegalAccessException e) {
+ Log.errorAndQuit("Executor couldn't be accessed.");
+ } catch (IllegalArgumentException e) {
+ Log.errorAndQuit("Invalid arguments to instantiation of Executor.");
+ } catch (InvocationTargetException e) {
+ Log.errorAndQuit("Instantiation of Executor threw an Exception!");
+ }
+ }
+
+ protected void addExecutors() {
+ Device device = null;
+ if (Options.local) {
+ device = new Device();
+ } else {
+ device = new Device(Options.deviceName, Options.noBootImage);
+ }
+
+ if (Options.useArchArm64) {
+ addExecutorsForArchitecture(device, Arm64QuickBackendExecutor.class,
+ Arm64OptimizingBackendExecutor.class, Arm64InterpreterExecutor.class);
+ }
+
+ if (Options.useArchArm) {
+ addExecutorsForArchitecture(device, ArmQuickBackendExecutor.class,
+ ArmOptimizingBackendExecutor.class, ArmInterpreterExecutor.class);
+ }
+
+ if (Options.useArchX86_64) {
+ addExecutorsForArchitecture(device, X86_64QuickBackendExecutor.class,
+ X86_64OptimizingBackendExecutor.class, X86_64InterpreterExecutor.class);
+ }
+
+ if (Options.useArchX86) {
+ addExecutorsForArchitecture(device, X86QuickBackendExecutor.class,
+ X86OptimizingBackendExecutor.class, X86InterpreterExecutor.class);
+ }
+
+ if (Options.useArchMips64) {
+ addExecutorsForArchitecture(device, Mips64QuickBackendExecutor.class,
+ Mips64OptimizingBackendExecutor.class, Mips64InterpreterExecutor.class);
+ }
+
+ if (Options.useArchMips) {
+ addExecutorsForArchitecture(device, MipsQuickBackendExecutor.class,
+ MipsOptimizingBackendExecutor.class, MipsInterpreterExecutor.class);
+ }
+
+ // Add the first backend as the golden executor for self-divergence tests.
+ goldenExecutor = executors.get(0);
+ }
+
+ /**
+ * Called from each Fuzzer subclass that we can instantiate. Parses the program, fuzzes it,
+ * and then saves it, if mutation was successful. We can use --skip-mutation to bypass
+ * the mutation phase, if we wanted to verify that a test program itself works.
+ */
+ protected Program fuzz() {
+ String inputFile = getNextInputFilename();
+ Program program = loadProgram(inputFile, null);
+ if (program == null) {
+ Log.errorAndQuit("Problem loading seed file.");
+ }
+ // Mutate the program.
+ if (!Options.skipMutation) {
+ timerMutation.start();
+ program.mutateTheProgram();
+
+ mutatedSuccessfully = program.updateRawDexFile();
+ timerMutation.stop();
+ if (!mutatedSuccessfully) {
+ listener.handleMutationFail();
+ }
+ } else {
+ Log.info("Skipping mutation stage as requested.");
+ mutatedSuccessfully = true;
+ }
+ if (mutatedSuccessfully) {
+ savedSuccessfully = saveProgram(program, getNextOutputFilename());
+ }
+ return program;
+ }
+
+ protected boolean safeToExecute() {
+ return mutatedSuccessfully && savedSuccessfully;
+ }
+
+ protected void execute(Program program) {
+ if (!safeToExecute()) {
+ Log.errorAndQuit("Your Fuzzer subclass called execute() "
+ + "without checking safeToExecute()!");
+ }
+
+ String programName = getNextOutputFilename();
+ boolean verified = true;
+ if (!Options.skipHostVerify) {
+ verified = goldenExecutor.verifyOnHost(programName);
+ }
+ if (verified) {
+ boolean skipAnalysis = false;
+ boolean uploadedToTarget = false;
+ if (!Options.skipHostVerify) {
+ listener.handleSuccessfulHostVerification();
+ }
+ for (Executor executor : executors) {
+ executor.reset();
+ if (!uploadedToTarget) {
+ executor.uploadToTarget(programName);
+ } else {
+ uploadedToTarget = true;
+ }
+ if (executor.needsCleanCodeCache()) {
+ executor.deleteGeneratedOatFile(programName);
+ }
+ executor.execute(programName);
+ if (!executor.verifyOnTarget()) {
+ listener.handleFailedTargetVerification();
+ skipAnalysis = true;
+ break;
+ }
+ // Results are saved in the executors until they reset, usually at the
+ // next iteration.
+ }
+
+ if (!skipAnalysis) {
+ listener.handleSuccessfullyFuzzedFile(programName);
+ analyseResults(program, programName);
+ }
+ }
+ mutatedSuccessfully = false;
+ savedSuccessfully = false;
+ }
+
+ /**
+ * Checks if the different outputs we observed align with different architectures.
+ */
+ private boolean checkForArchitectureSplit(Map<String, List<Executor>> outputMap) {
+ if (outputMap.size() != 2) {
+ // Cannot have a two-way split if we don't have 2 kinds of output.
+ return false;
+ }
+
+ Architecture[] architectures = new Architecture[2];
+ int archIdx = 0;
+
+ // For each kind of output we saw, make sure they all
+ // came from the same architecture.
+ for (List<Executor> executorList : outputMap.values()) {
+ architectures[archIdx] = executorList.get(0).getArchitecture();
+ for (int execIdx = 1; execIdx < executorList.size(); execIdx++) {
+ if (executorList.get(execIdx).getArchitecture() != architectures[archIdx]) {
+ // Not every executor with this output shared the same architecture.
+ return false;
+ }
+ }
+ archIdx++;
+ }
+
+ // Now make sure that the two outputs we saw were different architectures.
+ if (architectures[0] == architectures[1]) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean checkGoldenExecutorForSelfDivergence(String programName) {
+ // Run golden executor 5 times, make sure it always produces
+ // the same output, otherwise report that it is self-divergent.
+
+ // TODO: Instead, produce a list of acceptable outputs, and see if the divergent
+ // outputs of the backends fall within this set of outputs.
+ String seenOutput = null;
+ for (int i = 0; i < 5; i++) {
+ goldenExecutor.reset();
+ goldenExecutor.execute(programName);
+ String output = goldenExecutor.getResult().getFlattenedOutput();
+ if (seenOutput == null) {
+ seenOutput = output;
+ } else if (!seenOutput.equals(output)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void analyseResults(Program program, String programName) {
+ // Check timeouts.
+ // Construct two lists of executors, those who timed out, and those who did not.
+ // Report if we had some timeouts.
+ List<Executor> timedOut = new ArrayList<Executor>();
+ List<Executor> didNotTimeOut = new ArrayList<Executor>();
+ for (Executor executor : executors) {
+ if (executor.getResult().isTimeout()) {
+ timedOut.add(executor);
+ } else {
+ didNotTimeOut.add(executor);
+ }
+ }
+ if (!timedOut.isEmpty()) {
+ listener.handleTimeouts(timedOut, didNotTimeOut);
+ // Do not bother reporting divergence information.
+ return;
+ }
+
+ // Check divergences.
+ // Construct a map {output1: [executor that produced output1, ...], output2: [...]}
+ // If the map has more than one output, we had divergence, report it.
+ Map<String, List<Executor>> outputMap = new HashMap<String, List<Executor>>();
+ for (Executor executor : executors) {
+ String output = executor.getResult().getFlattenedOutput();
+ if (Options.dumpOutput) {
+ listener.handleDumpOutput(
+ executor.getResult().getFlattenedOutputWithNewlines(), executor);
+ }
+ if (outputMap.containsKey(output)) {
+ outputMap.get(output).add(executor);
+ } else {
+ List<Executor> newList = new ArrayList<Executor>();
+ newList.add(executor);
+ outputMap.put(output, newList);
+ }
+ }
+
+ if (outputMap.size() > 1) {
+ // Report that we had divergence.
+ listener.handleDivergences(outputMap);
+ listener.handleMutations(program.getMutations());
+ // If we found divergences, try running the "golden executor"
+ // a few times in succession, to see if the output it produces is different
+ // from run to run. If so, then we're probably executing something with either:
+ // a) randomness
+ // b) timing-dependent code
+ // c) threads
+ // So we will not consider it a "true" divergence, but still useful?
+ if (checkGoldenExecutorForSelfDivergence(programName)) {
+ listener.handleSelfDivergence();
+ return;
+ }
+ // If we found divergences, try checking if the differences
+ // in outputs align with differences in architectures.
+ // For example, if we have: {Output1: [ARM, ARM], Output2: [ARM64, ARM64]}
+ if (checkForArchitectureSplit(outputMap)) {
+ listener.handleArchitectureSplit();
+ }
+ } else {
+ // No problems with execution.
+ listener.handleSuccess(outputMap);
+ }
+ }
+
+ private Program loadProgram(String inputName, List<Mutation> mutations) {
+ Program program = null;
+ try {
+ DexRandomAccessFile input = new DexRandomAccessFile(inputName, "r");
+ offsetTracker = new OffsetTracker();
+ input.setOffsetTracker(offsetTracker);
+ // Read the raw DexFile
+ RawDexFile rawDexFile = new RawDexFile();
+ timerDexInput.start();
+ rawDexFile.read(input);
+ timerDexInput.stop();
+ input.close();
+ // Create the program view.
+ timerProgGen.start();
+ program = new Program(rawDexFile, mutations, listener);
+ timerProgGen.stop();
+ } catch (FileNotFoundException e) {
+ Log.errorAndQuit("Couldn't open a file called " + inputName);
+ } catch (IOException e) {
+ Log.errorAndQuit("IOException when trying to load a DEX test file!");
+ }
+ return program;
+ }
+
+ private boolean saveProgram(Program program, String outputName) {
+ boolean success = false;
+
+ try {
+ // Write out the results of mutation.
+ DexRandomAccessFile output = new DexRandomAccessFile(outputName, "rw");
+ output.setOffsetTracker(offsetTracker);
+ // Delete the contents of the file, in case it already existed.
+ output.setLength(0);
+ // Write out the file.
+ timerDexOutput.start();
+ program.writeRawDexFile(output);
+ timerDexOutput.stop();
+ // Recalculate the header info.
+ timerChecksumCalc.start();
+ program.updateRawDexFileHeader(output);
+ timerChecksumCalc.stop();
+ output.close();
+ success = true;
+ } catch (FileNotFoundException e) {
+ Log.errorAndQuit("Couldn't open a file called " + outputName);
+ } catch (IOException e) {
+ Log.errorAndQuit("IOException when trying to save a DEX test file!");
+ }
+ return success;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultiple.java b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultiple.java
new file mode 100644
index 0000000..8abaeb0
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultiple.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.Options;
+import dexfuzz.listeners.BaseListener;
+
+/**
+ * Superclass for fuzzing strategies that perform multiple fuzzes, and want
+ * their inputs to come from the input list in a round-robin fashion.
+ */
+public abstract class FuzzerMultiple extends Fuzzer {
+ protected int iterations;
+
+ protected FuzzerMultiple(BaseListener listener) {
+ super(listener);
+ }
+
+ @Override
+ protected String getNextInputFilename() {
+ String inputFile = Options.inputFileList.get(0);
+ if (Options.inputFileList.size() > 1) {
+ int nextIndex = iterations % Options.inputFileList.size();
+ inputFile = Options.inputFileList.get(nextIndex);
+ }
+ listener.handleFuzzingFile(inputFile);
+ return inputFile;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleExecute.java b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleExecute.java
new file mode 100644
index 0000000..0cf6df7
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleExecute.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.Options;
+import dexfuzz.listeners.BaseListener;
+import dexfuzz.program.Program;
+
+/**
+ * Fuzz programs multiple times, testing each.
+ */
+public class FuzzerMultipleExecute extends FuzzerMultiple {
+ public FuzzerMultipleExecute(BaseListener listener) {
+ super(listener);
+ addExecutors();
+ }
+
+ @Override
+ protected String getNextOutputFilename() {
+ // In MultipleExecute, always use the same output.
+ return Options.outputFile;
+ }
+
+ @Override
+ public void run() {
+ // TODO: Test that all seed files execute correctly before they are mutated!
+ for (iterations = 0; iterations < Options.repeat; iterations++) {
+ listener.handleIterationStarted(iterations);
+ Program program = fuzz();
+ if (safeToExecute()) {
+ execute(program);
+ }
+ listener.handleIterationFinished(iterations);
+ }
+ listener.handleSummary();
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleNoExecute.java b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleNoExecute.java
new file mode 100644
index 0000000..fea8788
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleNoExecute.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.Options;
+import dexfuzz.listeners.BaseListener;
+
+/**
+ * Fuzz programs multiple times, writing each one to a new DEX file.
+ */
+public class FuzzerMultipleNoExecute extends FuzzerMultiple {
+ public FuzzerMultipleNoExecute(BaseListener listener) {
+ super(listener);
+ }
+
+ @Override
+ protected String getNextOutputFilename() {
+ // In MultipleNoExecute, produce multiple files, each prefixed
+ // with the iteration value.
+ return String.format("%09d_%s", iterations, Options.outputFile);
+ }
+
+ @Override
+ public void run() {
+ for (iterations = 0; iterations < Options.repeat; iterations++) {
+ listener.handleIterationStarted(iterations);
+ fuzz();
+ listener.handleIterationFinished(iterations);
+ }
+ listener.handleSummary();
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingle.java b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingle.java
new file mode 100644
index 0000000..68b47c2
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingle.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.Options;
+import dexfuzz.listeners.BaseListener;
+
+/**
+ * Superclass for fuzzers that fuzz once.
+ */
+public abstract class FuzzerSingle extends Fuzzer {
+ protected FuzzerSingle(BaseListener listener) {
+ super(listener);
+ }
+
+ @Override
+ protected String getNextInputFilename() {
+ return Options.inputFileList.get(0);
+ }
+
+ protected String getNextOutputFilename() {
+ return Options.outputFile;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleExecute.java b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleExecute.java
new file mode 100644
index 0000000..de0c7db
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleExecute.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.listeners.BaseListener;
+import dexfuzz.program.Program;
+
+/**
+ * Fuzz a DEX file once, and test it.
+ */
+public class FuzzerSingleExecute extends FuzzerSingle {
+ public FuzzerSingleExecute(BaseListener listener) {
+ super(listener);
+ addExecutors();
+ }
+
+ @Override
+ public void run() {
+ Program program = fuzz();
+ if (safeToExecute()) {
+ execute(program);
+ }
+ }
+}
diff --git a/runtime/arch/arm64/portable_entrypoints_arm64.S b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleNoExecute.java
similarity index 65%
rename from runtime/arch/arm64/portable_entrypoints_arm64.S
rename to tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleNoExecute.java
index 9e2c030..6015284 100644
--- a/runtime/arch/arm64/portable_entrypoints_arm64.S
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleNoExecute.java
@@ -14,17 +14,20 @@
* limitations under the License.
*/
-#include "asm_support_arm64.S"
+package dexfuzz.fuzzers;
- /*
- * Portable invocation stub.
- */
-UNIMPLEMENTED art_portable_invoke_stub
+import dexfuzz.listeners.BaseListener;
-UNIMPLEMENTED art_portable_proxy_invoke_handler
+/**
+ * Fuzz a DEX file once, but don't test it.
+ */
+public class FuzzerSingleNoExecute extends FuzzerSingle {
+ public FuzzerSingleNoExecute(BaseListener listener) {
+ super(listener);
+ }
-UNIMPLEMENTED art_portable_resolution_trampoline
-
-UNIMPLEMENTED art_portable_to_interpreter_bridge
-
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
+ @Override
+ public void run() {
+ fuzz();
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/BaseListener.java b/tools/dexfuzz/src/dexfuzz/listeners/BaseListener.java
new file mode 100644
index 0000000..e33fb09
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/BaseListener.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.executors.Executor;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Base class for Listeners, who are notified about certain events in dexfuzz's execution.
+ */
+public abstract class BaseListener {
+ public void setup() { }
+
+ public void shutdown() { }
+
+ public void handleSuccessfulHostVerification() { }
+
+ public void handleFailedHostVerification(ExecutionResult verificationResult) { }
+
+ public void handleFailedTargetVerification() { }
+
+ public void handleIterationStarted(int iteration) { }
+
+ public void handleIterationFinished(int iteration) { }
+
+ public void handleTimeouts(List<Executor> timedOut, List<Executor> didNotTimeOut) { }
+
+ public void handleDivergences(Map<String, List<Executor>> outputMap) { }
+
+ public void handleFuzzingFile(String inputFile) { }
+
+ public void handleSeed(long seed) { }
+
+ public void handleHostVerificationSigabort(ExecutionResult verificationResult) { }
+
+ public void handleSuccess(Map<String, List<Executor>> outputMap) { }
+
+ public void handleDumpOutput(String outputLine, Executor executor) { }
+
+ public void handleDumpVerify(String verifyLine) { }
+
+ public void handleMutationStats(String statsString) { }
+
+ public void handleTiming(String name, float elapsedTime) { }
+
+ public void handleMutationFail() { }
+
+ public void handleSummary() { }
+
+ public void handleSuccessfullyFuzzedFile(String programName) { }
+
+ public void handleSelfDivergence() { }
+
+ public void handleMessage(String msg) { }
+
+ public void handleMutations(List<Mutation> mutations) { }
+
+ public void handleArchitectureSplit() { }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/ConsoleLoggerListener.java b/tools/dexfuzz/src/dexfuzz/listeners/ConsoleLoggerListener.java
new file mode 100644
index 0000000..1ea74d9
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/ConsoleLoggerListener.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.executors.Executor;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Logs output to the console, when not using --repeat.
+ */
+public class ConsoleLoggerListener extends BaseListener {
+ @Override
+ public void setup() {
+
+ }
+
+ @Override
+ public void shutdown() {
+
+ }
+
+ private void logToConsole(String msg) {
+ System.out.println("CONSOLE: " + msg);
+ }
+
+ @Override
+ public void handleSuccessfulHostVerification() {
+ logToConsole("Successful host verification");
+ }
+
+ @Override
+ public void handleFailedHostVerification(ExecutionResult verificationResult) {
+ logToConsole("Failed host verification");
+ }
+
+ @Override
+ public void handleMutations(List<Mutation> mutations) {
+ for (Mutation mutation : mutations) {
+ logToConsole("Applied mutation: " + mutation.toString());
+ }
+ }
+
+ @Override
+ public void handleArchitectureSplit() {
+ logToConsole("Detected architectural split.");
+ }
+
+ @Override
+ public void handleFailedTargetVerification() {
+ logToConsole("Failed target verification");
+ }
+
+ @Override
+ public void handleIterationStarted(int iteration) {
+ logToConsole("Starting iteration " + iteration);
+ }
+
+ @Override
+ public void handleIterationFinished(int iteration) {
+ logToConsole("Finished iteration " + iteration);
+ }
+
+ @Override
+ public void handleTimeouts(List<Executor> timedOut, List<Executor> didNotTimeOut) {
+ logToConsole("Timed out: " + timedOut.size() + " Did not time out: " + didNotTimeOut.size());
+ }
+
+ @Override
+ public void handleDivergences(Map<String, List<Executor>> outputMap) {
+ logToConsole("Got divergences!");
+ int outputCount = 1;
+ for (List<Executor> executors : outputMap.values()) {
+ logToConsole("Output " + outputCount + ":");
+ for (Executor executor : executors) {
+ logToConsole(" " + executor.getName());
+ }
+ outputCount++;
+ }
+ }
+
+ @Override
+ public void handleFuzzingFile(String inputFile) {
+ logToConsole("Fuzzing: " + inputFile);
+ }
+
+ @Override
+ public void handleSeed(long seed) {
+ logToConsole("Seed: " + seed);
+ }
+
+ @Override
+ public void handleHostVerificationSigabort(ExecutionResult verificationResult) {
+ logToConsole("Sigaborted host verification");
+ }
+
+ @Override
+ public void handleSuccessfullyFuzzedFile(String programName) {
+ logToConsole("Program " + programName + " successfully fuzzed.");
+ }
+
+ @Override
+ public void handleSuccess(Map<String, List<Executor>> outputMap) {
+ logToConsole("Execution was successful");
+ }
+
+ @Override
+ public void handleDumpOutput(String outputLine, Executor executor) {
+ logToConsole(executor.getName() + " OUTPUT: " + outputLine);
+ }
+
+ @Override
+ public void handleDumpVerify(String verifyLine) {
+ logToConsole("VERIFY: " + verifyLine);
+ }
+
+ @Override
+ public void handleMutationFail() {
+ logToConsole("DEX file was not mutated");
+ }
+
+ @Override
+ public void handleMutationStats(String statsString) {
+ logToConsole("Mutations performed: " + statsString);
+ }
+
+ @Override
+ public void handleTiming(String name, float elapsedTime) {
+ logToConsole(String.format("'%s': %.3fs", name, elapsedTime));
+ }
+
+ @Override
+ public void handleSummary() {
+ logToConsole("--- SUMMARY ---");
+ }
+
+ @Override
+ public void handleSelfDivergence() {
+ logToConsole("Seen self divergence");
+ }
+
+ @Override
+ public void handleMessage(String msg) {
+ logToConsole(msg);
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/LogFileListener.java b/tools/dexfuzz/src/dexfuzz/listeners/LogFileListener.java
new file mode 100644
index 0000000..09ee756
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/LogFileListener.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.Log;
+import dexfuzz.executors.Executor;
+import dexfuzz.program.Mutation;
+import dexfuzz.program.MutationSerializer;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Logs events to a file.
+ */
+public class LogFileListener extends BaseListener {
+ private BufferedWriter writer;
+ boolean ready = false;
+
+ long successfulVerification;
+ long failedVerification;
+ long failedMutation;
+ long success;
+ long timedOut;
+ long divergence;
+ long selfDivergent;
+ long architectureSplit;
+ long iterations;
+
+ private String logFile;
+
+ public LogFileListener(String logFile) {
+ this.logFile = logFile;
+ }
+
+ @Override
+ public void setup() {
+ try {
+ writer = new BufferedWriter(new FileWriter(logFile));
+ ready = true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void shutdown() {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ Log.always("Full log in " + logFile);
+ }
+
+ private void write(String msg) {
+ if (!ready) {
+ return;
+ }
+ try {
+ writer.write(msg + "\n");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void handleSuccessfulHostVerification() {
+ write("Host verification: SUCCESS");
+ successfulVerification++;
+ }
+
+ @Override
+ public void handleFailedHostVerification(ExecutionResult verificationResult) {
+ write("Host verification: FAILED");
+ failedVerification++;
+ }
+
+ @Override
+ public void handleFailedTargetVerification() {
+ write("Target verification: FAILED");
+ failedVerification++;
+ }
+
+ @Override
+ public void handleIterationStarted(int iteration) {
+ write("--> FUZZ " + (iteration + 1));
+ Date now = new Date(System.currentTimeMillis());
+ write("Time started: " + now.toString());
+ iterations++;
+ }
+
+ @Override
+ public void handleTimeouts(List<Executor> timedOut, List<Executor> didNotTimeOut) {
+ write("Some executors timed out.");
+ write("Timed out:");
+ for (Executor executor : timedOut) {
+ write(" " + executor.getName());
+ }
+ if (!didNotTimeOut.isEmpty()) {
+ write("Did not time out:");
+ for (Executor executor : didNotTimeOut) {
+ write(" " + executor.getName());
+ }
+ }
+ this.timedOut++;
+ }
+
+ @Override
+ public void handleDivergences(Map<String, List<Executor>> outputMap) {
+ write("DIVERGENCE between some executors!");
+ int outputCount = 1;
+ for (List<Executor> executors : outputMap.values()) {
+ write("Output " + outputCount + ":");
+ for (Executor executor : executors) {
+ write(" " + executor.getName());
+ }
+ outputCount++;
+ }
+ divergence++;
+
+ // You are probably interested in reading about these divergences while fuzzing
+ // is taking place, so flush the writer now.
+ try {
+ writer.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void handleFuzzingFile(String inputFile) {
+ write("Fuzzing file '" + inputFile + "'");
+ }
+
+ @Override
+ public void handleSeed(long seed) {
+ write("Using " + seed + " for seed.");
+ // Flush the seed as well, so if anything goes wrong we can see what seed lead
+ // to the issue.
+ try {
+ writer.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void handleHostVerificationSigabort(ExecutionResult verificationResult) {
+ write("Host verification: SIGABORTED");
+ }
+
+ @Override
+ public void handleSuccess(Map<String, List<Executor>> outputMap) {
+ write("All executors agreed on result.");
+ success++;
+ }
+
+ @Override
+ public void handleDumpOutput(String outputLine, Executor executor) {
+ write(executor.getName() + " OUTPUT:");
+ write(outputLine);
+ }
+
+ @Override
+ public void handleDumpVerify(String verifyLine) {
+ write("VERIFY: " + verifyLine);
+ }
+
+ @Override
+ public void handleMutationStats(String statsString) {
+ write("Mutation Stats: " + statsString);
+ }
+
+ @Override
+ public void handleTiming(String name, float elapsedTime) {
+ write(String.format("'%s': %.3fs", name, elapsedTime));
+ }
+
+ @Override
+ public void handleMutationFail() {
+ write("Mutation process: FAILED");
+ failedMutation++;
+ }
+
+ @Override
+ public void handleSummary() {
+ write("");
+ write("---+++--- SUMMARY ---+++---");
+ write("Fuzzing attempts: " + iterations);
+ write(" Failed verification: " + failedVerification);
+ write(" Failed mutation: " + failedMutation);
+ write(" Timed out: " + timedOut);
+ write("Successful: " + success);
+ write(" Self divergent: " + selfDivergent);
+ write(" Architecture split: " + architectureSplit);
+ write("Produced divergence: " + divergence);
+
+ long truelyDivergent = divergence - (selfDivergent + architectureSplit);
+ long verified = success + timedOut + truelyDivergent;
+
+ write("");
+
+ float verifiedTotalRatio =
+ (((float) (verified)) / iterations) * 100.0f;
+ write(String.format("Percentage Verified/Total: %.3f%%", verifiedTotalRatio));
+
+ float timedOutVerifiedRatio =
+ (((float) timedOut) / (verified)) * 100.0f;
+ write(String.format("Percentage Timed Out/Verified: %.3f%%", timedOutVerifiedRatio));
+
+ float successfulVerifiedRatio =
+ (((float) success) / (verified)) * 100.0f;
+ write(String.format("Percentage Successful/Verified: %.3f%%", successfulVerifiedRatio));
+
+ float divergentVerifiedRatio =
+ (((float) truelyDivergent) / (verified)) * 100.0f;
+ write(String.format("Percentage Divergent/Verified: %.3f%%", divergentVerifiedRatio));
+
+ write("---+++--- SUMMARY ---+++---");
+ write("");
+ }
+
+ @Override
+ public void handleIterationFinished(int iteration) {
+ write("");
+ }
+
+ @Override
+ public void handleSuccessfullyFuzzedFile(String programName) {
+ write("Successfully fuzzed file '" + programName + "'");
+ }
+
+ @Override
+ public void handleSelfDivergence() {
+ write("Golden Executor was self-divergent!");
+ selfDivergent++;
+ }
+
+ @Override
+ public void handleArchitectureSplit() {
+ write("Divergent outputs align with difference in architectures.");
+ architectureSplit++;
+ }
+
+ @Override
+ public void handleMessage(String msg) {
+ write(msg);
+ }
+
+ @Override
+ public void handleMutations(List<Mutation> mutations) {
+ write("Mutations Report");
+ for (Mutation mutation : mutations) {
+ write(MutationSerializer.getMutationString(mutation));
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/MultiplexerListener.java b/tools/dexfuzz/src/dexfuzz/listeners/MultiplexerListener.java
new file mode 100644
index 0000000..28ebce7
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/MultiplexerListener.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.executors.Executor;
+import dexfuzz.program.Mutation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Handles situation where multiple Listeners are wanted, passes notifications
+ * onto each Listener it is responsible for.
+ */
+public class MultiplexerListener extends BaseListener {
+
+ private List<BaseListener> listeners;
+
+ @Override
+ public void setup() {
+ listeners = new ArrayList<BaseListener>();
+ }
+
+ public void addListener(BaseListener listener) {
+ listeners.add(listener);
+ listener.setup();
+ }
+
+ @Override
+ public void shutdown() {
+ for (BaseListener listener : listeners) {
+ listener.shutdown();
+ }
+ }
+
+ @Override
+ public void handleSuccessfulHostVerification() {
+ for (BaseListener listener : listeners) {
+ listener.handleSuccessfulHostVerification();
+ }
+ }
+
+ @Override
+ public void handleFailedHostVerification(ExecutionResult verificationResult) {
+ for (BaseListener listener : listeners) {
+ listener.handleFailedHostVerification(verificationResult);
+ }
+ }
+
+ @Override
+ public void handleFailedTargetVerification() {
+ for (BaseListener listener : listeners) {
+ listener.handleFailedTargetVerification();
+ }
+ }
+
+ @Override
+ public void handleIterationStarted(int iteration) {
+ for (BaseListener listener : listeners) {
+ listener.handleIterationStarted(iteration);
+ }
+ }
+
+ @Override
+ public void handleIterationFinished(int iteration) {
+ for (BaseListener listener : listeners) {
+ listener.handleIterationFinished(iteration);
+ }
+ }
+
+ @Override
+ public void handleTimeouts(List<Executor> timedOut, List<Executor> didNotTimeOut) {
+ for (BaseListener listener : listeners) {
+ listener.handleTimeouts(timedOut, didNotTimeOut);
+ }
+ }
+
+ @Override
+ public void handleDivergences(Map<String, List<Executor>> outputMap) {
+ for (BaseListener listener : listeners) {
+ listener.handleDivergences(outputMap);
+ }
+ }
+
+ @Override
+ public void handleFuzzingFile(String inputFile) {
+ for (BaseListener listener : listeners) {
+ listener.handleFuzzingFile(inputFile);
+ }
+ }
+
+ @Override
+ public void handleSeed(long seed) {
+ for (BaseListener listener : listeners) {
+ listener.handleSeed(seed);
+ }
+ }
+
+ @Override
+ public void handleHostVerificationSigabort(ExecutionResult verificationResult) {
+ for (BaseListener listener : listeners) {
+ listener.handleHostVerificationSigabort(verificationResult);
+ }
+ }
+
+ @Override
+ public void handleSuccess(Map<String, List<Executor>> outputMap) {
+ for (BaseListener listener : listeners) {
+ listener.handleSuccess(outputMap);
+ }
+ }
+
+ @Override
+ public void handleDumpOutput(String outputLine, Executor executor) {
+ for (BaseListener listener : listeners) {
+ listener.handleDumpOutput(outputLine, executor);
+ }
+ }
+
+ @Override
+ public void handleDumpVerify(String verifyLine) {
+ for (BaseListener listener : listeners) {
+ listener.handleDumpVerify(verifyLine);
+ }
+ }
+
+ @Override
+ public void handleMutationStats(String statsString) {
+ for (BaseListener listener : listeners) {
+ listener.handleMutationStats(statsString);
+ }
+ }
+
+ @Override
+ public void handleTiming(String name, float elapsedTime) {
+ for (BaseListener listener : listeners) {
+ listener.handleTiming(name, elapsedTime);
+ }
+ }
+
+ @Override
+ public void handleMutationFail() {
+ for (BaseListener listener : listeners) {
+ listener.handleMutationFail();
+ }
+ }
+
+ @Override
+ public void handleSummary() {
+ for (BaseListener listener : listeners) {
+ listener.handleSummary();
+ }
+ }
+
+ @Override
+ public void handleSuccessfullyFuzzedFile(String programName) {
+ for (BaseListener listener : listeners) {
+ listener.handleSuccessfullyFuzzedFile(programName);
+ }
+ }
+
+ @Override
+ public void handleSelfDivergence() {
+ for (BaseListener listener : listeners) {
+ listener.handleSelfDivergence();
+ }
+ }
+
+ @Override
+ public void handleMessage(String msg) {
+ for (BaseListener listener : listeners) {
+ listener.handleMessage(msg);
+ }
+ }
+
+ @Override
+ public void handleMutations(List<Mutation> mutations) {
+ for (BaseListener listener : listeners) {
+ listener.handleMutations(mutations);
+ }
+ }
+
+ @Override
+ public void handleArchitectureSplit() {
+ for (BaseListener listener : listeners) {
+ listener.handleArchitectureSplit();
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/UniqueProgramTrackerListener.java b/tools/dexfuzz/src/dexfuzz/listeners/UniqueProgramTrackerListener.java
new file mode 100644
index 0000000..affaffc
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/UniqueProgramTrackerListener.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.Log;
+import dexfuzz.Options;
+import dexfuzz.executors.Executor;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Tracks unique programs and outputs. Also saves divergent programs!
+ */
+public class UniqueProgramTrackerListener extends BaseListener {
+ /**
+ * Map of unique program MD5 sums, mapped to times seen.
+ */
+ private Map<String, Integer> uniquePrograms;
+
+ /**
+ * Map of unique program outputs (MD5'd), mapped to times seen.
+ */
+ private Map<String, Integer> uniqueOutputs;
+
+ /**
+ * Used to remember the seed used to fuzz the fuzzed file, so we can save it with this
+ * seed as a name, if we find a divergence.
+ */
+ private long currentSeed;
+
+ /**
+ * Used to remember the name of the file we've fuzzed, so we can save it if we
+ * find a divergence.
+ */
+ private String fuzzedFile;
+
+ private MessageDigest digest;
+ private String databaseFile;
+
+ /**
+ * Save the database every X number of iterations.
+ */
+ private static final int saveDatabasePeriod = 20;
+
+ public UniqueProgramTrackerListener(String databaseFile) {
+ this.databaseFile = databaseFile;
+ }
+
+ @Override
+ public void handleSeed(long seed) {
+ currentSeed = seed;
+ }
+
+ /**
+ * Given a program filename, calculate the MD5sum of
+ * this program.
+ */
+ private String getMD5SumOfProgram(String programName) {
+ byte[] buf = new byte[256];
+ try {
+ FileInputStream stream = new FileInputStream(programName);
+ boolean done = false;
+ while (!done) {
+ int bytesRead = stream.read(buf);
+ if (bytesRead == -1) {
+ done = true;
+ } else {
+ digest.update(buf);
+ }
+ }
+ stream.close();
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return new String(digest.digest());
+ }
+
+ private String getMD5SumOfOutput(String output) {
+ digest.update(output.getBytes());
+ return new String(digest.digest());
+ }
+
+ @SuppressWarnings("unchecked")
+ private void loadUniqueProgsData() {
+ File file = new File(databaseFile);
+ if (!file.exists()) {
+ uniquePrograms = new HashMap<String, Integer>();
+ uniqueOutputs = new HashMap<String, Integer>();
+ return;
+ }
+
+ try {
+ ObjectInputStream objectStream =
+ new ObjectInputStream(new FileInputStream(databaseFile));
+ uniquePrograms = (Map<String, Integer>) objectStream.readObject();
+ uniqueOutputs = (Map<String, Integer>) objectStream.readObject();
+ objectStream.close();
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ private void saveUniqueProgsData() {
+ // Since we could potentially stop the program while writing out this DB,
+ // copy the old file beforehand, and then delete it if we successfully wrote out the DB.
+ boolean oldWasSaved = false;
+ File file = new File(databaseFile);
+ if (file.exists()) {
+ try {
+ Process process =
+ Runtime.getRuntime().exec(String.format("cp %1$s %1$s.old", databaseFile));
+ // Shouldn't block, cp shouldn't produce output.
+ process.waitFor();
+ oldWasSaved = true;
+ } catch (IOException exception) {
+ exception.printStackTrace();
+ } catch (InterruptedException exception) {
+ exception.printStackTrace();
+ }
+ }
+
+ // Now write out the DB.
+ boolean success = false;
+ try {
+ ObjectOutputStream objectStream =
+ new ObjectOutputStream(new FileOutputStream(databaseFile));
+ objectStream.writeObject(uniquePrograms);
+ objectStream.writeObject(uniqueOutputs);
+ objectStream.close();
+ success = true;
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ // If we get here, and we successfully wrote out the DB, delete the saved one.
+ if (oldWasSaved && success) {
+ try {
+ Process process =
+ Runtime.getRuntime().exec(String.format("rm %s.old", databaseFile));
+ // Shouldn't block, rm shouldn't produce output.
+ process.waitFor();
+ } catch (IOException exception) {
+ exception.printStackTrace();
+ } catch (InterruptedException exception) {
+ exception.printStackTrace();
+ }
+ } else if (oldWasSaved && !success) {
+ Log.error("Failed to successfully write out the unique programs DB!");
+ Log.error("Old DB should be saved in " + databaseFile + ".old");
+ }
+ }
+
+ private void addToMap(String md5sum, Map<String, Integer> map) {
+ if (map.containsKey(md5sum)) {
+ map.put(md5sum, map.get(md5sum) + 1);
+ } else {
+ map.put(md5sum, 1);
+ }
+ }
+
+ private void saveDivergentProgram() {
+ File before = new File(fuzzedFile);
+ File after = new File(String.format("divergent_programs/%d.dex", currentSeed));
+ boolean success = before.renameTo(after);
+ if (!success) {
+ Log.error("Failed to save divergent program! Does divergent_programs/ exist?");
+ }
+ }
+
+ @Override
+ public void setup() {
+ try {
+ digest = MessageDigest.getInstance("MD5");
+ loadUniqueProgsData();
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void handleIterationFinished(int iteration) {
+ if ((iteration % saveDatabasePeriod) == (saveDatabasePeriod - 1)) {
+ saveUniqueProgsData();
+ }
+ }
+
+ @Override
+ public void handleSuccessfullyFuzzedFile(String programName) {
+ String md5sum = getMD5SumOfProgram(programName);
+ addToMap(md5sum, uniquePrograms);
+
+ fuzzedFile = programName;
+ }
+
+ @Override
+ public void handleDivergences(Map<String, List<Executor>> outputMap) {
+ // Just use the first one.
+ String output = (String) outputMap.keySet().toArray()[0];
+ String md5sum = getMD5SumOfOutput(output);
+ addToMap(md5sum, uniqueOutputs);
+
+ saveDivergentProgram();
+ }
+
+ @Override
+ public void handleSuccess(Map<String, List<Executor>> outputMap) {
+ // There's only one, use it.
+ String output = (String) outputMap.keySet().toArray()[0];
+ String md5sum = getMD5SumOfOutput(output);
+ addToMap(md5sum, uniqueOutputs);
+ }
+
+ @Override
+ public void handleSummary() {
+ if (Options.reportUnique) {
+ Log.always("-- UNIQUE PROGRAM REPORT --");
+ Log.always("Unique Programs Seen: " + uniquePrograms.size());
+ Log.always("Unique Outputs Seen: " + uniqueOutputs.size());
+ Log.always("---------------------------");
+ }
+
+ saveUniqueProgsData();
+ }
+
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/UpdatingConsoleListener.java b/tools/dexfuzz/src/dexfuzz/listeners/UpdatingConsoleListener.java
new file mode 100644
index 0000000..39d1d2f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/UpdatingConsoleListener.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.executors.Executor;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implements the live updating table of results when --repeat is being used.
+ */
+public class UpdatingConsoleListener extends BaseListener {
+ long successfulVerification;
+ long failedVerification;
+ long failedMutation;
+ long success;
+ long timedOut;
+ long divergence;
+ long selfDivergent;
+ long architectureSplit;
+ long iterations;
+
+ @Override
+ public void setup() {
+ System.out.println("|-----------------------------------------------------------------|");
+ System.out.println("|Iterations|VerifyFail|MutateFail|Timed Out |Successful|Divergence|");
+ System.out.println("|-----------------------------------------------------------------|");
+ }
+
+ @Override
+ public void handleSuccessfulHostVerification() {
+ successfulVerification++;
+ }
+
+ @Override
+ public void handleFailedHostVerification(ExecutionResult verificationResult) {
+ failedVerification++;
+ }
+
+ @Override
+ public void handleFailedTargetVerification() {
+ failedVerification++;
+ }
+
+ @Override
+ public void handleIterationStarted(int iteration) {
+ iterations++;
+ }
+
+ @Override
+ public void handleIterationFinished(int iteration) {
+ String output = String.format("| %-9d| %-9d| %-9d| %-9d| %-9d| %-9d|",
+ iterations, failedVerification, failedMutation, timedOut, success,
+ divergence - (selfDivergent + architectureSplit));
+ System.out.print("\r" + output);
+ }
+
+ @Override
+ public void handleTimeouts(List<Executor> timedOut, List<Executor> didNotTimeOut) {
+ this.timedOut++;
+ }
+
+ @Override
+ public void handleDivergences(Map<String, List<Executor>> outputMap) {
+ divergence++;
+ }
+
+ @Override
+ public void handleSelfDivergence() {
+ selfDivergent++;
+ }
+
+ @Override
+ public void handleArchitectureSplit() {
+ architectureSplit++;
+ }
+
+ @Override
+ public void handleSuccess(Map<String, List<Executor>> outputMap) {
+ success++;
+ }
+
+ @Override
+ public void handleMutationFail() {
+ failedMutation++;
+ }
+
+ @Override
+ public void handleSummary() {
+ System.out.println("\n|-----------------------------------------------------------------|");
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/CodeTranslator.java b/tools/dexfuzz/src/dexfuzz/program/CodeTranslator.java
new file mode 100644
index 0000000..650501b
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/CodeTranslator.java
@@ -0,0 +1,592 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.Log;
+import dexfuzz.rawdex.CodeItem;
+import dexfuzz.rawdex.EncodedCatchHandler;
+import dexfuzz.rawdex.EncodedTypeAddrPair;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.TryItem;
+import dexfuzz.rawdex.formats.ContainsTarget;
+import dexfuzz.rawdex.formats.RawInsnHelper;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Translates from a CodeItem (the raw list of Instructions) to MutatableCode
+ * (graph of Instructions, using MInsns and subclasses) and vice-versa.
+ */
+public class CodeTranslator {
+
+ /**
+ * Given a raw DEX file's CodeItem, produce a MutatableCode object, that CodeMutators
+ * are designed to operate on.
+ * @param codeItemIdx Used to make sure the correct CodeItem is updated later after mutation.
+ * @return A new MutatableCode object, which contains all relevant information
+ * obtained from the CodeItem.
+ */
+ public MutatableCode codeItemToMutatableCode(Program program, CodeItem codeItem,
+ int codeItemIdx, int mutatableCodeIdx) {
+ Log.debug("Translating CodeItem " + codeItemIdx
+ + " (" + codeItem.meta.methodName + ") to MutatableCode");
+
+ MutatableCode mutatableCode = new MutatableCode(program);
+
+ codeItem.registerMutatableCode(mutatableCode);
+
+ mutatableCode.name = codeItem.meta.methodName;
+ mutatableCode.shorty = codeItem.meta.shorty;
+ mutatableCode.isStatic = codeItem.meta.isStatic;
+
+ mutatableCode.codeItemIdx = codeItemIdx;
+
+ mutatableCode.mutatableCodeIdx = mutatableCodeIdx;
+
+ mutatableCode.registersSize = codeItem.registersSize;
+ mutatableCode.insSize = codeItem.insSize;
+ mutatableCode.outsSize = codeItem.outsSize;
+ mutatableCode.triesSize = codeItem.triesSize;
+
+ // Temporary map from bytecode offset -> instruction.
+ Map<Integer,MInsn> insnLocationMap = new HashMap<Integer,MInsn>();
+
+ List<Instruction> inputInsns = codeItem.insns;
+
+ // Create the MInsns.
+ int loc = 0;
+ for (Instruction insn : inputInsns) {
+ MInsn mInsn = null;
+
+ if (isInstructionSwitch(insn)) {
+ mInsn = new MSwitchInsn();
+ } else if (isInstructionBranch(insn)) {
+ mInsn = new MBranchInsn();
+ } else if (isInstructionFillArrayData(insn)) {
+ mInsn = new MInsnWithData();
+ } else {
+ mInsn = new MInsn();
+ }
+
+ mInsn.insn = insn;
+
+ // Populate the temporary map.
+ insnLocationMap.put(loc, mInsn);
+
+ // Populate the proper list of mutatable instructions.
+ mutatableCode.addInstructionToEnd(mInsn);
+
+ // Calculate the offsets for each instruction.
+ mInsn.location = loc;
+ mInsn.locationUpdated = false;
+
+ loc += mInsn.insn.getSize();
+ }
+
+ // Now make branch/switch instructions point at the right target instructions.
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (mInsn instanceof MSwitchInsn) {
+ readSwitchInstruction((MSwitchInsn) mInsn, insnLocationMap);
+ } else if (mInsn instanceof MInsnWithData) {
+ ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format;
+ int targetLoc = mInsn.location + (int) containsTarget.getTarget(mInsn.insn);
+ ((MInsnWithData)mInsn).dataTarget = insnLocationMap.get(targetLoc);
+ if (((MInsnWithData)mInsn).dataTarget == null) {
+ Log.errorAndQuit("Bad offset calculation in data-target insn");
+ }
+ } else if (mInsn instanceof MBranchInsn) {
+ ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format;
+ int targetLoc = mInsn.location + (int) containsTarget.getTarget(mInsn.insn);
+ ((MBranchInsn)mInsn).target = insnLocationMap.get(targetLoc);
+ if (((MBranchInsn)mInsn).target == null) {
+ Log.errorAndQuit("Bad offset calculation in branch insn");
+ }
+ }
+ }
+
+ // Now create try blocks.
+ if (mutatableCode.triesSize > 0) {
+ readTryBlocks(codeItem, mutatableCode, insnLocationMap);
+ }
+
+ return mutatableCode;
+ }
+
+ /**
+ * Given a MutatableCode item that may have been mutated, update the original CodeItem
+ * correctly, to allow valid DEX to be written back to the output file.
+ */
+ public void mutatableCodeToCodeItem(CodeItem codeItem, MutatableCode mutatableCode) {
+ Log.debug("Translating MutatableCode " + mutatableCode.name
+ + " to CodeItem " + mutatableCode.codeItemIdx);
+
+ // We must first align any data instructions at the end of the code
+ // before we recalculate any offsets.
+ // This also updates their sizes...
+ alignDataInstructions(mutatableCode);
+
+ // Validate that the tracked locations for instructions are valid.
+ // Also mark locations as no longer being updated.
+ int loc = 0;
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (mInsn.insn.justRaw) {
+ // All just_raw instructions need alignment!
+ if ((loc % 2) != 0) {
+ loc++;
+ }
+ }
+ if (mInsn.location != loc) {
+ Log.errorAndQuit(String.format("%s does not have expected location 0x%x",
+ mInsn, loc));
+ }
+ mInsn.locationUpdated = false;
+ loc += mInsn.insn.getSize();
+ }
+
+ // This new list will be attached to the CodeItem at the end...
+ List<Instruction> outputInsns = new LinkedList<Instruction>();
+
+ // Go through our new list of MInsns, adding them to the new
+ // list of instructions that will be attached to the CodeItem.
+ // Also recalculate offsets for branches.
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (mInsn instanceof MSwitchInsn) {
+ updateSwitchInstruction((MSwitchInsn)mInsn, mutatableCode);
+ } else if (mInsn instanceof MInsnWithData) {
+ MInsn target = ((MInsnWithData) mInsn).dataTarget;
+ int dataOffset = target.location - mInsn.location;
+ ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format;
+ containsTarget.setTarget(mInsn.insn, dataOffset);
+ } else if (mInsn instanceof MBranchInsn) {
+ MInsn target = ((MBranchInsn) mInsn).target;
+ int branchOffset = target.location - mInsn.location;
+ ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format;
+ containsTarget.setTarget(mInsn.insn, branchOffset);
+ }
+ outputInsns.add(mInsn.insn);
+ }
+
+ // Calculate the new insns_size.
+ int newInsnsSize = 0;
+ for (Instruction insn : outputInsns) {
+ newInsnsSize += insn.getSize();
+ }
+
+ if (mutatableCode.triesSize > 0) {
+ updateTryBlocks(codeItem, mutatableCode);
+ }
+
+ codeItem.insnsSize = newInsnsSize;
+ codeItem.insns = outputInsns;
+ codeItem.registersSize = mutatableCode.registersSize;
+ codeItem.insSize = mutatableCode.insSize;
+ codeItem.outsSize = mutatableCode.outsSize;
+ codeItem.triesSize = mutatableCode.triesSize;
+ }
+
+ /**
+ * The TryItem specifies an offset into the EncodedCatchHandlerList for a given CodeItem,
+ * but we only have an array of the EncodedCatchHandlers that the List contains.
+ * This function produces a map that offers a way to find out the index into our array,
+ * from the try handler's offset.
+ */
+ private Map<Short,Integer> createTryHandlerOffsetToIndexMap(CodeItem codeItem) {
+ // Create a sorted set of offsets.
+ List<Short> uniqueOffsets = new ArrayList<Short>();
+ for (TryItem tryItem : codeItem.tries) {
+ int index = 0;
+ while (true) {
+ if ((index == uniqueOffsets.size())
+ || (uniqueOffsets.get(index) > tryItem.handlerOff)) {
+ // First condition means we're at the end of the set (or we're inserting
+ // into an empty set)
+ // Second condition means that the offset belongs here
+ // ...so insert it here, pushing the element currently in this position to the
+ // right, if it exists
+ uniqueOffsets.add(index, tryItem.handlerOff);
+ break;
+ } else if (uniqueOffsets.get(index) == tryItem.handlerOff) {
+ // We've already seen it, and we're making a set, not a list.
+ break;
+ } else {
+ // Keep searching.
+ index++;
+ }
+ }
+ }
+ // Now we have an (implicit) index -> offset mapping!
+
+ // Now create the reverse mapping.
+ Map<Short,Integer> offsetIndexMap = new HashMap<Short,Integer>();
+ for (int i = 0; i < uniqueOffsets.size(); i++) {
+ offsetIndexMap.put(uniqueOffsets.get(i), i);
+ }
+
+ return offsetIndexMap;
+ }
+
+ private void readTryBlocks(CodeItem codeItem, MutatableCode mutatableCode,
+ Map<Integer,MInsn> insnLocationMap) {
+ mutatableCode.mutatableTries = new LinkedList<MTryBlock>();
+
+ Map<Short,Integer> offsetIndexMap = createTryHandlerOffsetToIndexMap(codeItem);
+
+ // Read each TryItem into a MutatableTryBlock.
+ for (TryItem tryItem : codeItem.tries) {
+ MTryBlock mTryBlock = new MTryBlock();
+
+ // Get the MInsns that form the start and end of the try block.
+ int startLocation = tryItem.startAddr;
+ mTryBlock.startInsn = insnLocationMap.get(startLocation);
+ int endLocation = tryItem.startAddr + tryItem.insnCount;
+ mTryBlock.endInsn = insnLocationMap.get(endLocation);
+
+ // Sanity checks.
+ if (mTryBlock.startInsn == null) {
+ Log.errorAndQuit(String.format(
+ "Couldn't find a mutatable insn at start offset 0x%x",
+ startLocation));
+ }
+ if (mTryBlock.endInsn == null) {
+ Log.errorAndQuit(String.format(
+ "Couldn't find a mutatable insn at end offset 0x%x",
+ endLocation));
+ }
+
+ // Get the EncodedCatchHandler.
+ int handlerIdx = offsetIndexMap.get(tryItem.handlerOff);
+ EncodedCatchHandler encodedCatchHandler = codeItem.handlers.list[handlerIdx];
+
+ // Do we have a catch all? If so, associate the MInsn that's there.
+ if (encodedCatchHandler.size <= 0) {
+ mTryBlock.catchAllHandler =
+ insnLocationMap.get(encodedCatchHandler.catchAllAddr);
+ // Sanity check.
+ if (mTryBlock.catchAllHandler == null) {
+ Log.errorAndQuit(
+ String.format("Couldn't find a mutatable insn at catch-all offset 0x%x",
+ encodedCatchHandler.catchAllAddr));
+ }
+ }
+ // Do we have explicitly-typed handlers? This will remain empty if not.
+ mTryBlock.handlers = new LinkedList<MInsn>();
+
+ // Associate all the explicitly-typed handlers.
+ for (int i = 0; i < Math.abs(encodedCatchHandler.size); i++) {
+ EncodedTypeAddrPair handler = encodedCatchHandler.handlers[i];
+ MInsn handlerInsn = insnLocationMap.get(handler.addr);
+ // Sanity check.
+ if (handlerInsn == null) {
+ Log.errorAndQuit(String.format(
+ "Couldn't find a mutatable instruction at handler offset 0x%x",
+ handler.addr));
+ }
+ mTryBlock.handlers.add(handlerInsn);
+ }
+
+ // Now finally add the new MutatableTryBlock into this MutatableCode's list!
+ mutatableCode.mutatableTries.add(mTryBlock);
+ }
+ }
+
+ private void updateTryBlocks(CodeItem codeItem, MutatableCode mutatableCode) {
+
+ // TODO: Support ability to add extra try blocks/handlers?
+
+ for (MTryBlock mTryBlock : mutatableCode.mutatableTries) {
+ if (mTryBlock.startInsn.location > mTryBlock.endInsn.location) {
+ // Mutation has put this try block's end insn before its start insn. Fix this.
+ MInsn tempInsn = mTryBlock.startInsn;
+ mTryBlock.startInsn = mTryBlock.endInsn;
+ mTryBlock.endInsn = tempInsn;
+ }
+ }
+
+ // First, manipulate the try blocks if they overlap.
+ for (int i = 0; i < mutatableCode.mutatableTries.size() - 1; i++) {
+ MTryBlock first = mutatableCode.mutatableTries.get(i);
+ MTryBlock second = mutatableCode.mutatableTries.get(i + 1);
+
+ // Do they overlap?
+ if (first.endInsn.location > second.startInsn.location) {
+
+ Log.debug("Found overlap in TryBlocks, moving 2nd TryBlock...");
+ Log.debug("1st TryBlock goes from " + first.startInsn + " to " + first.endInsn);
+ Log.debug("2nd TryBlock goes from " + second.startInsn + " to " + second.endInsn);
+
+ // Find the first instruction that comes after that does not overlap
+ // with the first try block.
+ MInsn newInsn = second.startInsn;
+ int ptr = mutatableCode.getInstructionIndex(newInsn);
+ while (first.endInsn.location > newInsn.location) {
+ ptr++;
+ newInsn = mutatableCode.getInstructionAt(ptr);
+ }
+ second.startInsn = newInsn;
+
+ Log.debug("Now 2nd TryBlock goes from " + second.startInsn + " to " + second.endInsn);
+ }
+ }
+
+ Map<Short,Integer> offsetIndexMap = createTryHandlerOffsetToIndexMap(codeItem);
+
+ int tryItemIdx = 0;
+ for (MTryBlock mTryBlock : mutatableCode.mutatableTries) {
+ TryItem tryItem = codeItem.tries[tryItemIdx];
+
+ tryItem.startAddr = mTryBlock.startInsn.location;
+ tryItem.insnCount =
+ (short) (mTryBlock.endInsn.location - mTryBlock.startInsn.location);
+
+ // Get the EncodedCatchHandler.
+ EncodedCatchHandler encodedCatchHandler =
+ codeItem.handlers.list[offsetIndexMap.get(tryItem.handlerOff)];
+
+ if (encodedCatchHandler.size <= 0) {
+ encodedCatchHandler.catchAllAddr = mTryBlock.catchAllHandler.location;
+ }
+ for (int i = 0; i < Math.abs(encodedCatchHandler.size); i++) {
+ MInsn handlerInsn = mTryBlock.handlers.get(i);
+ EncodedTypeAddrPair handler = encodedCatchHandler.handlers[i];
+ handler.addr = handlerInsn.location;
+ }
+ tryItemIdx++;
+ }
+ }
+
+ /**
+ * Given a switch instruction, find the associated data's raw[] form, and update
+ * the targets of the switch instruction to point to the correct instructions.
+ */
+ private void readSwitchInstruction(MSwitchInsn switchInsn,
+ Map<Integer,MInsn> insnLocationMap) {
+ // Find the data.
+ ContainsTarget containsTarget = (ContainsTarget) switchInsn.insn.info.format;
+ int dataLocation = switchInsn.location + (int) containsTarget.getTarget(switchInsn.insn);
+ switchInsn.dataTarget = insnLocationMap.get(dataLocation);
+ if (switchInsn.dataTarget == null) {
+ Log.errorAndQuit("Bad offset calculation for data target in switch insn");
+ }
+
+ // Now read the data.
+ Instruction dataInsn = switchInsn.dataTarget.insn;
+
+ int rawPtr = 2;
+
+ int targetsSize = (int) RawInsnHelper.getUnsignedShortFromTwoBytes(dataInsn.rawBytes, rawPtr);
+ rawPtr += 2;
+
+ int[] keys = new int[targetsSize];
+ int[] targets = new int[targetsSize];
+
+ if (dataInsn.rawType == 1) {
+ switchInsn.packed = true;
+ // Dealing with a packed-switch.
+ // Read the first key.
+ keys[0] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes, rawPtr);
+ rawPtr += 4;
+ // Calculate the rest of the keys.
+ for (int i = 1; i < targetsSize; i++) {
+ keys[i] = keys[i - 1] + 1;
+ }
+ } else if (dataInsn.rawType == 2) {
+ // Dealing with a sparse-switch.
+ // Read all of the keys.
+ for (int i = 0; i < targetsSize; i++) {
+ keys[i] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes,
+ rawPtr);
+ rawPtr += 4;
+ }
+ }
+
+ // Now read the targets.
+ for (int i = 0; i < targetsSize; i++) {
+ targets[i] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes,
+ rawPtr);
+ rawPtr += 4;
+ }
+
+ // Store the keys.
+ switchInsn.keys = keys;
+
+ // Convert our targets[] offsets into pointers to MInsns.
+ for (int target : targets) {
+ int targetLocation = switchInsn.location + target;
+ MInsn targetInsn = insnLocationMap.get(targetLocation);
+ switchInsn.targets.add(targetInsn);
+ if (targetInsn == null) {
+ Log.errorAndQuit("Bad offset calculation for target in switch insn");
+ }
+ }
+ }
+
+ /**
+ * Given a mutatable switch instruction, which may have had some of its branch
+ * targets moved, update all the target offsets in the raw[] form of the instruction.
+ */
+ private void updateSwitchInstruction(MSwitchInsn switchInsn, MutatableCode mutatableCode) {
+ // Update the offset to the data instruction
+ MInsn dataTarget = switchInsn.dataTarget;
+ int dataOffset = dataTarget.location - switchInsn.location;
+ ContainsTarget containsTarget = (ContainsTarget) switchInsn.insn.info.format;
+ containsTarget.setTarget(switchInsn.insn, dataOffset);
+
+ int targetsSize = switchInsn.targets.size();
+
+ int[] keys = switchInsn.keys;
+ int[] targets = new int[targetsSize];
+
+ // Calculate the new offsets.
+ int targetIdx = 0;
+ for (MInsn target : switchInsn.targets) {
+ targets[targetIdx] = target.location - switchInsn.location;
+ targetIdx++;
+ }
+
+ // Now write the data back to the raw bytes.
+ Instruction dataInsn = switchInsn.dataTarget.insn;
+
+ int rawPtr = 2;
+
+ // Write out the size.
+ RawInsnHelper.writeUnsignedShortToTwoBytes(dataInsn.rawBytes, rawPtr, targetsSize);
+ rawPtr += 2;
+
+ // Write out the keys.
+ if (switchInsn.packed) {
+ // Only write out one key - the first.
+ RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, keys[0]);
+ rawPtr += 4;
+ } else {
+ // Write out all the keys.
+ for (int i = 0; i < targetsSize; i++) {
+ RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, keys[i]);
+ rawPtr += 4;
+ }
+ }
+
+ // Write out all the targets.
+ for (int i = 0; i < targetsSize; i++) {
+ RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, targets[i]);
+ rawPtr += 4;
+ }
+ }
+
+ /**
+ * After mutation, data instructions may no longer be 4-byte aligned.
+ * If this is the case, insert nops to align them all.
+ * This makes a number of assumptions about data currently:
+ * - data is always at the end of method insns
+ * - all data instructions are stored contiguously
+ */
+ private void alignDataInstructions(MutatableCode mutatableCode) {
+ // Find all the switch data instructions.
+ List<MInsn> dataInsns = new ArrayList<MInsn>();
+
+ // Update raw sizes of the data instructions as well.
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (mInsn instanceof MSwitchInsn) {
+ // Update the raw size of the instruction.
+ MSwitchInsn switchInsn = (MSwitchInsn) mInsn;
+ int targetsSize = switchInsn.targets.size();
+ Instruction dataInsn = switchInsn.dataTarget.insn;
+ if (switchInsn.packed) {
+ dataInsn.rawSize = (targetsSize * 2) + 4;
+ } else {
+ dataInsn.rawSize = (targetsSize * 4) + 2;
+ }
+ dataInsns.add(switchInsn.dataTarget);
+ } else if (mInsn instanceof MInsnWithData) {
+ MInsnWithData insnWithData =
+ (MInsnWithData) mInsn;
+ dataInsns.add(insnWithData.dataTarget);
+ }
+ }
+
+ // Only need to align switch data instructions if there are any!
+ if (!dataInsns.isEmpty()) {
+
+ Log.debug("Found data instructions, checking alignment...");
+
+ // Sort data_insns by location.
+ Collections.sort(dataInsns, new Comparator<MInsn>() {
+ @Override
+ public int compare(MInsn first, MInsn second) {
+ if (first.location < second.location) {
+ return -1;
+ } else if (first.location > second.location) {
+ return 1;
+ }
+ return 0;
+ }
+ });
+
+ boolean performedAlignment = false;
+
+ // Go through all the data insns, and insert an alignment nop if they're unaligned.
+ for (MInsn dataInsn : dataInsns) {
+ if (dataInsn.location % 2 != 0) {
+ Log.debug("Aligning data instruction with a nop.");
+ int alignmentNopIdx = mutatableCode.getInstructionIndex(dataInsn);
+ MInsn nop = new MInsn();
+ nop.insn = new Instruction();
+ nop.insn.info = Instruction.getOpcodeInfo(Opcode.NOP);
+ mutatableCode.insertInstructionAt(nop, alignmentNopIdx);
+ performedAlignment = true;
+ }
+ }
+
+ if (!performedAlignment) {
+ Log.debug("Alignment okay.");
+ }
+ }
+ }
+
+ /**
+ * Determine if a particular instruction is a branch instruction, based on opcode.
+ */
+ private boolean isInstructionBranch(Instruction insn) {
+ Opcode opcode = insn.info.opcode;
+ if (Opcode.isBetween(opcode, Opcode.IF_EQ, Opcode.IF_LEZ)
+ || Opcode.isBetween(opcode, Opcode.GOTO, Opcode.GOTO_32)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Determine if a particular instruction is a switch instruction, based on opcode.
+ */
+ private boolean isInstructionSwitch(Instruction insn) {
+ Opcode opcode = insn.info.opcode;
+ if (Opcode.isBetween(opcode, Opcode.PACKED_SWITCH, Opcode.SPARSE_SWITCH)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isInstructionFillArrayData(Instruction insn) {
+ return (insn.info.opcode == Opcode.FILL_ARRAY_DATA);
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/IdCreator.java b/tools/dexfuzz/src/dexfuzz/program/IdCreator.java
new file mode 100644
index 0000000..c506fa6
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/IdCreator.java
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.Log;
+import dexfuzz.rawdex.FieldIdItem;
+import dexfuzz.rawdex.MethodIdItem;
+import dexfuzz.rawdex.Offset;
+import dexfuzz.rawdex.Offsettable;
+import dexfuzz.rawdex.ProtoIdItem;
+import dexfuzz.rawdex.RawDexFile;
+import dexfuzz.rawdex.RawDexObject.IndexUpdateKind;
+import dexfuzz.rawdex.StringDataItem;
+import dexfuzz.rawdex.StringIdItem;
+import dexfuzz.rawdex.TypeIdItem;
+import dexfuzz.rawdex.TypeItem;
+import dexfuzz.rawdex.TypeList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Responsible for the finding and creation of TypeIds, MethodIds, FieldIds, and StringIds,
+ * during mutation.
+ */
+public class IdCreator {
+ private RawDexFile rawDexFile;
+
+ public IdCreator(RawDexFile rawDexFile) {
+ this.rawDexFile = rawDexFile;
+ }
+
+ private int findProtoIdInsertionPoint(String signature) {
+ int returnTypeIdx = findTypeId(convertSignatureToReturnType(signature));
+ String[] parameterListStrings = convertSignatureToParameterList(signature);
+ TypeList parameterList = null;
+ if (parameterListStrings.length > 0) {
+ parameterList = findTypeList(parameterListStrings);
+ }
+
+ if (returnTypeIdx < 0) {
+ Log.errorAndQuit("Did not create necessary return type before finding insertion "
+ + "point for new proto!");
+ }
+
+ if (parameterListStrings.length > 0 && parameterList == null) {
+ Log.errorAndQuit("Did not create necessary parameter list before finding insertion "
+ + "point for new proto!");
+ }
+
+ int protoIdIdx = 0;
+ for (ProtoIdItem protoId : rawDexFile.protoIds) {
+ if (returnTypeIdx < protoId.returnTypeIdx) {
+ break;
+ }
+ if (returnTypeIdx == protoId.returnTypeIdx
+ && parameterListStrings.length == 0) {
+ break;
+ }
+ if (returnTypeIdx == protoId.returnTypeIdx
+ && parameterListStrings.length > 0
+ && protoId.parametersOff.pointsToSomething()
+ && parameterList.comesBefore(
+ (TypeList) protoId.parametersOff.getPointedToItem())) {
+ break;
+ }
+ protoIdIdx++;
+ }
+ return protoIdIdx;
+ }
+
+ private int findMethodIdInsertionPoint(String className, String methodName, String signature) {
+ int classIdx = findTypeId(className);
+ int nameIdx = findString(methodName);
+ int protoIdx = findProtoId(signature);
+
+ if (classIdx < 0 || nameIdx < 0 || protoIdx < 0) {
+ Log.errorAndQuit("Did not create necessary class, name or proto strings before finding "
+ + " insertion point for new method!");
+ }
+
+ int methodIdIdx = 0;
+ for (MethodIdItem methodId : rawDexFile.methodIds) {
+ if (classIdx < methodId.classIdx) {
+ break;
+ }
+ if (classIdx == methodId.classIdx && nameIdx < methodId.nameIdx) {
+ break;
+ }
+ if (classIdx == methodId.classIdx && nameIdx == methodId.nameIdx
+ && protoIdx < methodId.protoIdx) {
+ break;
+ }
+ methodIdIdx++;
+ }
+ return methodIdIdx;
+ }
+
+ private int findTypeIdInsertionPoint(String className) {
+ int descriptorIdx = findString(className);
+
+ if (descriptorIdx < 0) {
+ Log.errorAndQuit("Did not create necessary descriptor string before finding "
+ + " insertion point for new type!");
+ }
+
+ int typeIdIdx = 0;
+ for (TypeIdItem typeId : rawDexFile.typeIds) {
+ if (descriptorIdx < typeId.descriptorIdx) {
+ break;
+ }
+ typeIdIdx++;
+ }
+ return typeIdIdx;
+ }
+
+ private int findStringDataInsertionPoint(String string) {
+ int stringDataIdx = 0;
+ for (StringDataItem stringData : rawDexFile.stringDatas) {
+ if (stringData.getSize() > 0 && stringData.getString().compareTo(string) >= 0) {
+ break;
+ }
+ stringDataIdx++;
+ }
+ return stringDataIdx;
+ }
+
+ private int findFieldIdInsertionPoint(String className, String typeName, String fieldName) {
+ int classIdx = findTypeId(className);
+ int typeIdx = findTypeId(typeName);
+ int nameIdx = findString(fieldName);
+
+ if (classIdx < 0 || typeIdx < 0 || nameIdx < 0) {
+ Log.errorAndQuit("Did not create necessary class, type or name strings before finding "
+ + " insertion point for new field!");
+ }
+
+ int fieldIdIdx = 0;
+ for (FieldIdItem fieldId : rawDexFile.fieldIds) {
+ if (classIdx < fieldId.classIdx) {
+ break;
+ }
+ if (classIdx == fieldId.classIdx && nameIdx < fieldId.nameIdx) {
+ break;
+ }
+ if (classIdx == fieldId.classIdx && nameIdx == fieldId.nameIdx
+ && typeIdx < fieldId.typeIdx) {
+ break;
+ }
+ fieldIdIdx++;
+ }
+ return fieldIdIdx;
+ }
+
+ private int createMethodId(String className, String methodName, String signature) {
+ if (rawDexFile.methodIds.size() >= 65536) {
+ Log.errorAndQuit("Referenced too many methods for the DEX file.");
+ }
+
+ // Search for (or create) the prototype.
+ int protoIdx = findOrCreateProtoId(signature);
+
+ // Search for (or create) the owning class.
+ // NB: findOrCreateProtoId could create new types, so this must come
+ // after it!
+ int typeIdIdx = findOrCreateTypeId(className);
+
+ // Search for (or create) the string representing the method name.
+ // NB: findOrCreateProtoId/TypeId could create new strings, so this must come
+ // after them!
+ int methodNameStringIdx = findOrCreateString(methodName);
+
+ // Create MethodIdItem.
+ MethodIdItem newMethodId = new MethodIdItem();
+ newMethodId.classIdx = (short) typeIdIdx;
+ newMethodId.protoIdx = (short) protoIdx;
+ newMethodId.nameIdx = methodNameStringIdx;
+
+ // MethodIds must be ordered.
+ int newMethodIdIdx = findMethodIdInsertionPoint(className, methodName, signature);
+
+ rawDexFile.methodIds.add(newMethodIdIdx, newMethodId);
+
+ // Insert into OffsetTracker.
+ if (newMethodIdIdx == 0) {
+ rawDexFile.getOffsetTracker()
+ .insertNewOffsettableAsFirstOfType(newMethodId, rawDexFile);
+ } else {
+ MethodIdItem prevMethodId = rawDexFile.methodIds.get(newMethodIdIdx - 1);
+ rawDexFile.getOffsetTracker().insertNewOffsettableAfter(newMethodId, prevMethodId);
+ }
+
+ Log.info(String.format("Created new MethodIdItem for %s %s %s, index: 0x%04x",
+ className, methodName, signature, newMethodIdIdx));
+
+ // Now that we've potentially moved a lot of method IDs along, all references
+ // to them need to be updated.
+ rawDexFile.incrementIndex(IndexUpdateKind.METHOD_ID, newMethodIdIdx);
+
+ // All done, return the index for the new method.
+ return newMethodIdIdx;
+ }
+
+ private int findMethodId(String className, String methodName, String signature) {
+ int classIdx = findTypeId(className);
+ if (classIdx == -1) {
+ return -1;
+ }
+ int nameIdx = findString(methodName);
+ if (nameIdx == -1) {
+ return -1;
+ }
+ int protoIdx = findProtoId(signature);
+ if (nameIdx == -1) {
+ return -1;
+ }
+
+ int methodIdIdx = 0;
+ for (MethodIdItem methodId : rawDexFile.methodIds) {
+ if (classIdx == methodId.classIdx
+ && nameIdx == methodId.nameIdx
+ && protoIdx == methodId.protoIdx) {
+ return methodIdIdx;
+ }
+ methodIdIdx++;
+ }
+ return -1;
+ }
+
+ /**
+ * Given a fully qualified class name (Ljava/lang/System;), method name (gc) and
+ * and signature (()V), either find the MethodId in our DEX file's table, or create it.
+ */
+ public int findOrCreateMethodId(String className, String methodName, String shorty) {
+ int methodIdIdx = findMethodId(className, methodName, shorty);
+ if (methodIdIdx != -1) {
+ return methodIdIdx;
+ }
+ return createMethodId(className, methodName, shorty);
+ }
+
+ private int createTypeId(String className) {
+ if (rawDexFile.typeIds.size() >= 65536) {
+ Log.errorAndQuit("Referenced too many classes for the DEX file.");
+ }
+
+ // Search for (or create) the string representing the class descriptor.
+ int descriptorStringIdx = findOrCreateString(className);
+
+ // Create TypeIdItem.
+ TypeIdItem newTypeId = new TypeIdItem();
+ newTypeId.descriptorIdx = descriptorStringIdx;
+
+ // TypeIds must be ordered.
+ int newTypeIdIdx = findTypeIdInsertionPoint(className);
+
+ rawDexFile.typeIds.add(newTypeIdIdx, newTypeId);
+
+ // Insert into OffsetTracker.
+ if (newTypeIdIdx == 0) {
+ rawDexFile.getOffsetTracker().insertNewOffsettableAsFirstOfType(newTypeId, rawDexFile);
+ } else {
+ TypeIdItem prevTypeId = rawDexFile.typeIds.get(newTypeIdIdx - 1);
+ rawDexFile.getOffsetTracker().insertNewOffsettableAfter(newTypeId, prevTypeId);
+ }
+
+ Log.info(String.format("Created new ClassIdItem for %s, index: 0x%04x",
+ className, newTypeIdIdx));
+
+ // Now that we've potentially moved a lot of type IDs along, all references
+ // to them need to be updated.
+ rawDexFile.incrementIndex(IndexUpdateKind.TYPE_ID, newTypeIdIdx);
+
+ // All done, return the index for the new class.
+ return newTypeIdIdx;
+ }
+
+ private int findTypeId(String className) {
+ int descriptorIdx = findString(className);
+ if (descriptorIdx == -1) {
+ return -1;
+ }
+
+ // Search for class.
+ int typeIdIdx = 0;
+ for (TypeIdItem typeId : rawDexFile.typeIds) {
+ if (descriptorIdx == typeId.descriptorIdx) {
+ return typeIdIdx;
+ }
+ typeIdIdx++;
+ }
+ return -1;
+ }
+
+ /**
+ * Given a fully qualified class name (Ljava/lang/System;)
+ * either find the TypeId in our DEX file's table, or create it.
+ */
+ public int findOrCreateTypeId(String className) {
+ int typeIdIdx = findTypeId(className);
+ if (typeIdIdx != -1) {
+ return typeIdIdx;
+ }
+ return createTypeId(className);
+ }
+
+ private int createString(String string) {
+ // Didn't find it, create one...
+ int stringsCount = rawDexFile.stringIds.size();
+ if (stringsCount != rawDexFile.stringDatas.size()) {
+ Log.errorAndQuit("Corrupted DEX file, len(StringIDs) != len(StringDatas)");
+ }
+
+ // StringData must be ordered.
+ int newStringIdx = findStringDataInsertionPoint(string);
+
+ // Create StringDataItem.
+ StringDataItem newStringData = new StringDataItem();
+ newStringData.setSize(string.length());
+ newStringData.setString(string);
+
+ rawDexFile.stringDatas.add(newStringIdx, newStringData);
+
+ // Insert into OffsetTracker.
+ // (Need to save the Offsettable, because the StringIdItem will point to it.)
+ Offsettable offsettableStringData = null;
+ if (newStringIdx == 0) {
+ offsettableStringData =
+ rawDexFile.getOffsetTracker()
+ .insertNewOffsettableAsFirstOfType(newStringData, rawDexFile);
+ } else {
+ StringDataItem prevStringData = rawDexFile.stringDatas.get(newStringIdx - 1);
+ offsettableStringData = rawDexFile.getOffsetTracker()
+ .insertNewOffsettableAfter(newStringData, prevStringData);
+ }
+
+ // Create StringIdItem.
+ StringIdItem newStringId = new StringIdItem();
+ newStringId.stringDataOff = new Offset(false);
+ newStringId.stringDataOff.pointToNew(offsettableStringData);
+
+ rawDexFile.stringIds.add(newStringIdx, newStringId);
+
+ // Insert into OffsetTracker.
+ if (newStringIdx == 0) {
+ rawDexFile.getOffsetTracker()
+ .insertNewOffsettableAsFirstOfType(newStringId, rawDexFile);
+ } else {
+ StringIdItem prevStringId = rawDexFile.stringIds.get(newStringIdx - 1);
+ rawDexFile.getOffsetTracker().insertNewOffsettableAfter(newStringId, prevStringId);
+ }
+
+
+ Log.info(String.format("Created new StringIdItem and StringDataItem for %s, index: 0x%04x",
+ string, newStringIdx));
+
+ // Now that we've potentially moved a lot of string IDs along, all references
+ // to them need to be updated.
+ rawDexFile.incrementIndex(IndexUpdateKind.STRING_ID, newStringIdx);
+
+ // All done, return the index for the new string.
+ return newStringIdx;
+ }
+
+ private int findString(String string) {
+ // Search for string.
+ int stringIdx = 0;
+ for (StringDataItem stringDataItem : rawDexFile.stringDatas) {
+ if (stringDataItem.getSize() == 0 && string.isEmpty()) {
+ return stringIdx;
+ } else if (stringDataItem.getSize() > 0 && stringDataItem.getString().equals(string)) {
+ return stringIdx;
+ }
+ stringIdx++;
+ }
+ return -1;
+ }
+
+ /**
+ * Given a string, either find the StringId in our DEX file's table, or create it.
+ */
+ public int findOrCreateString(String string) {
+ int stringIdx = findString(string);
+ if (stringIdx != -1) {
+ return stringIdx;
+ }
+ return createString(string);
+ }
+
+ private int createFieldId(String className, String typeName, String fieldName) {
+ if (rawDexFile.fieldIds.size() >= 65536) {
+ Log.errorAndQuit("Referenced too many fields for the DEX file.");
+ }
+
+ // Search for (or create) the owning class.
+ int classIdx = findOrCreateTypeId(className);
+
+ // Search for (or create) the field's type.
+ int typeIdx = findOrCreateTypeId(typeName);
+
+ // The creation of the typeIdx may have changed the classIdx, search again!
+ classIdx = findOrCreateTypeId(className);
+
+ // Search for (or create) the string representing the field name.
+ int fieldNameStringIdx = findOrCreateString(fieldName);
+
+ // Create FieldIdItem.
+ FieldIdItem newFieldId = new FieldIdItem();
+ newFieldId.classIdx = (short) classIdx;
+ newFieldId.typeIdx = (short) typeIdx;
+ newFieldId.nameIdx = fieldNameStringIdx;
+
+ // FieldIds must be ordered.
+ int newFieldIdIdx = findFieldIdInsertionPoint(className, typeName, fieldName);
+
+ rawDexFile.fieldIds.add(newFieldIdIdx, newFieldId);
+
+ // Insert into OffsetTracker.
+ if (newFieldIdIdx == 0 && rawDexFile.fieldIds.size() == 1) {
+ // Special case: we didn't have any fields before!
+ rawDexFile.getOffsetTracker()
+ .insertNewOffsettableAsFirstEverField(newFieldId, rawDexFile);
+ } else if (newFieldIdIdx == 0) {
+ rawDexFile.getOffsetTracker().insertNewOffsettableAsFirstOfType(newFieldId, rawDexFile);
+ } else {
+ FieldIdItem prevFieldId = rawDexFile.fieldIds.get(newFieldIdIdx - 1);
+ rawDexFile.getOffsetTracker().insertNewOffsettableAfter(newFieldId, prevFieldId);
+ }
+
+ Log.info(String.format("Created new FieldIdItem for %s %s %s, index: 0x%04x",
+ className, typeName, fieldName, newFieldIdIdx));
+
+ // Now that we've potentially moved a lot of field IDs along, all references
+ // to them need to be updated.
+ rawDexFile.incrementIndex(IndexUpdateKind.FIELD_ID, newFieldIdIdx);
+
+ // All done, return the index for the new field.
+ return newFieldIdIdx;
+ }
+
+ private int findFieldId(String className, String typeName, String fieldName) {
+ int classIdx = findTypeId(className);
+ if (classIdx == -1) {
+ return -1;
+ }
+ int typeIdx = findTypeId(typeName);
+ if (typeIdx == -1) {
+ return -1;
+ }
+ int nameIdx = findString(fieldName);
+ if (nameIdx == -1) {
+ return -1;
+ }
+
+ int fieldIdIdx = 0;
+ for (FieldIdItem fieldId : rawDexFile.fieldIds) {
+ if (classIdx == fieldId.classIdx
+ && typeIdx == fieldId.typeIdx
+ && nameIdx == fieldId.nameIdx) {
+ return fieldIdIdx;
+ }
+ fieldIdIdx++;
+ }
+ return -1;
+ }
+
+ /**
+ * Given a field's fully qualified class name, type name, and name,
+ * either find the FieldId in our DEX file's table, or create it.
+ */
+ public int findOrCreateFieldId(String className, String typeName, String fieldName) {
+ int fieldIdx = findFieldId(className, typeName, fieldName);
+ if (fieldIdx != -1) {
+ return fieldIdx;
+ }
+ return createFieldId(className, typeName, fieldName);
+ }
+
+ /**
+ * Returns a 1 or 2 element String[]. If 1 element, the only element is the return type
+ * part of the signature. If 2 elements, the first is the parameters, the second is
+ * the return type.
+ */
+ private String[] convertSignatureToParametersAndReturnType(String signature) {
+ if (signature.charAt(0) != '(' || !signature.contains(")")) {
+ Log.errorAndQuit("Invalid signature: " + signature);
+ }
+ String[] elems = signature.substring(1).split("\\)");
+ return elems;
+ }
+
+ private String[] convertSignatureToParameterList(String signature) {
+ String[] elems = convertSignatureToParametersAndReturnType(signature);
+ String parameters = "";
+ if (elems.length == 2) {
+ parameters = elems[0];
+ }
+
+ List<String> parameterList = new ArrayList<String>();
+
+ int typePointer = 0;
+ while (typePointer != parameters.length()) {
+ if (elems[0].charAt(typePointer) == 'L') {
+ int start = typePointer;
+ // Read up to the next ;
+ while (elems[0].charAt(typePointer) != ';') {
+ typePointer++;
+ }
+ parameterList.add(parameters.substring(start, typePointer + 1));
+ } else {
+ parameterList.add(Character.toString(parameters.charAt(typePointer)));
+ }
+ typePointer++;
+ }
+
+ return parameterList.toArray(new String[]{});
+ }
+
+ private String convertSignatureToReturnType(String signature) {
+ String[] elems = convertSignatureToParametersAndReturnType(signature);
+ String returnType = "";
+ if (elems.length == 1) {
+ returnType = elems[0];
+ } else {
+ returnType = elems[1];
+ }
+
+ return returnType;
+ }
+
+ private String convertSignatureToShorty(String signature) {
+ String[] elems = convertSignatureToParametersAndReturnType(signature);
+
+ StringBuilder shortyBuilder = new StringBuilder();
+
+ String parameters = "";
+ String returnType = "";
+
+ if (elems.length == 1) {
+ shortyBuilder.append("V");
+ } else {
+ parameters = elems[0];
+ returnType = elems[1];
+ char returnChar = returnType.charAt(0);
+ // Arrays are references in shorties.
+ if (returnChar == '[') {
+ returnChar = 'L';
+ }
+ shortyBuilder.append(returnChar);
+ }
+
+ int typePointer = 0;
+ while (typePointer != parameters.length()) {
+ if (parameters.charAt(typePointer) == 'L') {
+ shortyBuilder.append('L');
+ // Read up to the next ;
+ while (parameters.charAt(typePointer) != ';') {
+ typePointer++;
+ if (typePointer == parameters.length()) {
+ Log.errorAndQuit("Illegal type specified in signature - L with no ;!");
+ }
+ }
+ } else if (parameters.charAt(typePointer) == '[') {
+ // Arrays are references in shorties.
+ shortyBuilder.append('L');
+ // Read past all the [s
+ while (parameters.charAt(typePointer) == '[') {
+ typePointer++;
+ }
+ if (parameters.charAt(typePointer) == 'L') {
+ // Read up to the next ;
+ while (parameters.charAt(typePointer) != ';') {
+ typePointer++;
+ if (typePointer == parameters.length()) {
+ Log.errorAndQuit("Illegal type specified in signature - L with no ;!");
+ }
+ }
+ }
+ } else {
+ shortyBuilder.append(parameters.charAt(typePointer));
+ }
+
+ typePointer++;
+ }
+
+ return shortyBuilder.toString();
+ }
+
+ private Integer[] convertParameterListToTypeIdList(String[] parameterList) {
+ List<Integer> typeIdList = new ArrayList<Integer>();
+ for (String parameter : parameterList) {
+ int typeIdx = findTypeId(parameter);
+ if (typeIdx == -1) {
+ return null;
+ }
+ typeIdList.add(typeIdx);
+ }
+ return typeIdList.toArray(new Integer[]{});
+ }
+
+ private TypeList createTypeList(String[] parameterList) {
+ TypeList typeList = new TypeList();
+ List<TypeItem> typeItemList = new ArrayList<TypeItem>();
+
+ // This must be done as two passes, one to create all the types,
+ // and then one to put them in the type list.
+ for (String parameter : parameterList) {
+ findOrCreateTypeId(parameter);
+ }
+
+ // Now actually put them in the list.
+ for (String parameter : parameterList) {
+ TypeItem typeItem = new TypeItem();
+ typeItem.typeIdx = (short) findOrCreateTypeId(parameter);
+ typeItemList.add(typeItem);
+ }
+ typeList.list = typeItemList.toArray(new TypeItem[]{});
+ typeList.size = typeItemList.size();
+
+ // Insert into OffsetTracker.
+ if (rawDexFile.typeLists == null) {
+ // Special case: we didn't have any fields before!
+ Log.info("Need to create first type list.");
+ rawDexFile.typeLists = new ArrayList<TypeList>();
+ rawDexFile.getOffsetTracker()
+ .insertNewOffsettableAsFirstEverTypeList(typeList, rawDexFile);
+ } else {
+ TypeList prevTypeList =
+ rawDexFile.typeLists.get(rawDexFile.typeLists.size() - 1);
+ rawDexFile.getOffsetTracker().insertNewOffsettableAfter(typeList, prevTypeList);
+ }
+
+ // Finally, add this new TypeList to the list of them.
+ rawDexFile.typeLists.add(typeList);
+
+ return typeList;
+ }
+
+ private TypeList findTypeList(String[] parameterList) {
+ Integer[] typeIdList = convertParameterListToTypeIdList(parameterList);
+ if (typeIdList == null) {
+ return null;
+ }
+
+ if (rawDexFile.typeLists == null) {
+ // There's no type lists yet!
+ return null;
+ }
+
+ for (TypeList typeList : rawDexFile.typeLists) {
+ if (typeList.size != typeIdList.length) {
+ continue;
+ }
+
+ boolean found = true;
+ int idx = 0;
+ for (TypeItem typeItem : typeList.list) {
+ if (typeItem.typeIdx != typeIdList[idx]) {
+ found = false;
+ break;
+ }
+ idx++;
+ }
+ if (found && idx == parameterList.length) {
+ return typeList;
+ }
+ }
+
+ return null;
+ }
+
+ private TypeList findOrCreateTypeList(String[] parameterList) {
+ TypeList typeList = findTypeList(parameterList);
+ if (typeList != null) {
+ return typeList;
+ }
+ return createTypeList(parameterList);
+ }
+
+ private int createProtoId(String signature) {
+ String shorty = convertSignatureToShorty(signature);
+ String returnType = convertSignatureToReturnType(signature);
+ String[] parameterList = convertSignatureToParameterList(signature);
+
+ if (rawDexFile.protoIds.size() >= 65536) {
+ Log.errorAndQuit("Referenced too many protos for the DEX file.");
+ }
+
+ TypeList typeList = null;
+ Offsettable typeListOffsettable = null;
+
+ if (parameterList.length > 0) {
+ // Search for (or create) the parameter list.
+ typeList = findOrCreateTypeList(parameterList);
+
+ typeListOffsettable =
+ rawDexFile.getOffsetTracker().getOffsettableForItem(typeList);
+ }
+
+ // Search for (or create) the return type.
+ int returnTypeIdx = findOrCreateTypeId(returnType);
+
+ // Search for (or create) the shorty string.
+ int shortyIdx = findOrCreateString(shorty);
+
+ // Create ProtoIdItem.
+ ProtoIdItem newProtoId = new ProtoIdItem();
+ newProtoId.shortyIdx = shortyIdx;
+ newProtoId.returnTypeIdx = returnTypeIdx;
+ newProtoId.parametersOff = new Offset(false);
+ if (parameterList.length > 0) {
+ newProtoId.parametersOff.pointToNew(typeListOffsettable);
+ }
+
+ // ProtoIds must be ordered.
+ int newProtoIdIdx = findProtoIdInsertionPoint(signature);
+
+ rawDexFile.protoIds.add(newProtoIdIdx, newProtoId);
+
+ // Insert into OffsetTracker.
+ if (newProtoIdIdx == 0) {
+ rawDexFile.getOffsetTracker().insertNewOffsettableAsFirstOfType(newProtoId, rawDexFile);
+ } else {
+ ProtoIdItem prevProtoId = rawDexFile.protoIds.get(newProtoIdIdx - 1);
+ rawDexFile.getOffsetTracker().insertNewOffsettableAfter(newProtoId, prevProtoId);
+ }
+
+ Log.info(String.format("Created new ProtoIdItem for %s, index: 0x%04x",
+ signature, newProtoIdIdx));
+
+ // Now that we've potentially moved a lot of proto IDs along, all references
+ // to them need to be updated.
+ rawDexFile.incrementIndex(IndexUpdateKind.PROTO_ID, newProtoIdIdx);
+
+ // All done, return the index for the new proto.
+ return newProtoIdIdx;
+ }
+
+ private int findProtoId(String signature) {
+ String shorty = convertSignatureToShorty(signature);
+ String returnType = convertSignatureToReturnType(signature);
+ String[] parameterList = convertSignatureToParameterList(signature);
+
+ int shortyIdx = findString(shorty);
+ if (shortyIdx == -1) {
+ return -1;
+ }
+ int returnTypeIdx = findTypeId(returnType);
+ if (returnTypeIdx == -1) {
+ return -1;
+ }
+
+ // Only look for a TypeList if there's a parameter list.
+ TypeList typeList = null;
+ if (parameterList.length > 0) {
+ typeList = findTypeList(parameterList);
+ if (typeList == null) {
+ return -1;
+ }
+ }
+
+ int protoIdIdx = 0;
+ for (ProtoIdItem protoId : rawDexFile.protoIds) {
+ if (parameterList.length > 0) {
+ // With parameters.
+ if (shortyIdx == protoId.shortyIdx
+ && returnTypeIdx == protoId.returnTypeIdx
+ && typeList.equals(protoId.parametersOff.getPointedToItem())) {
+ return protoIdIdx;
+ }
+ } else {
+ // Without parameters.
+ if (shortyIdx == protoId.shortyIdx
+ && returnTypeIdx == protoId.returnTypeIdx) {
+ return protoIdIdx;
+ }
+ }
+ protoIdIdx++;
+ }
+ return -1;
+ }
+
+ private int findOrCreateProtoId(String signature) {
+ int protoIdx = findProtoId(signature);
+ if (protoIdx != -1) {
+ return protoIdx;
+ }
+ return createProtoId(signature);
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/MBranchInsn.java b/tools/dexfuzz/src/dexfuzz/program/MBranchInsn.java
new file mode 100644
index 0000000..ea66844
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MBranchInsn.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+/**
+ * A subclass of the MInsn, that tracks its target instruction.
+ */
+public class MBranchInsn extends MInsn {
+ /**
+ * The MInsn this branch instruction branches to.
+ */
+ public MInsn target;
+
+ /**
+ * Clone this MBranchInsn, and clone the wrapped Instruction.
+ */
+ public MBranchInsn clone() {
+ MBranchInsn newInsn = new MBranchInsn();
+ newInsn.insn = insn.clone();
+ newInsn.target = target;
+ return newInsn;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/MInsn.java b/tools/dexfuzz/src/dexfuzz/program/MInsn.java
new file mode 100644
index 0000000..10f7755
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MInsn.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.rawdex.Instruction;
+
+/**
+ * Base class that is a thin wrapper for Instructions currently, also tracking location
+ * as the instruction is moved around.
+ */
+public class MInsn {
+ /**
+ * The raw DEX instruction that this instruction represents.
+ */
+ public Instruction insn;
+
+
+ /**
+ * The location of this instruction, as an offset in code words from the beginning.
+ * May become invalid if instructions around it are mutated.
+ */
+ public int location;
+
+ /**
+ * Denotes if the currently associated location can be trusted.
+ */
+ public boolean locationUpdated;
+
+ /**
+ * Clone this MInsn, and clone the wrapped Instruction.
+ */
+ public MInsn clone() {
+ MInsn newInsn = new MInsn();
+ newInsn.insn = insn.clone();
+ // It is the responsibility of the cloner to update these values.
+ newInsn.location = location;
+ newInsn.locationUpdated = locationUpdated;
+ return newInsn;
+ }
+
+ /**
+ * Get the String representation of an instruction.
+ */
+ public String toString() {
+ return String.format("{0x%04x%s: %s}",
+ location,
+ (locationUpdated) ? "!" : "",
+ insn.toString());
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/MInsnWithData.java b/tools/dexfuzz/src/dexfuzz/program/MInsnWithData.java
new file mode 100644
index 0000000..ffed883
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MInsnWithData.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+/**
+ * A subclass of the MInsn, that tracks the data instruction.
+ */
+public class MInsnWithData extends MInsn {
+ /**
+ * The MInsn that represents the data this instruction uses.
+ */
+ public MInsn dataTarget;
+
+ /**
+ * Clone this MInsnWithData, and clone the wrapped Instruction.
+ */
+ public MInsnWithData clone() {
+ MInsnWithData newInsn = new MInsnWithData();
+ newInsn.insn = insn.clone();
+ newInsn.dataTarget = dataTarget;
+ return newInsn;
+ }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/program/MSwitchInsn.java b/tools/dexfuzz/src/dexfuzz/program/MSwitchInsn.java
new file mode 100644
index 0000000..d8693fe
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MSwitchInsn.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A subclass of the MInsnWithData, that also has multiple jump targets.
+ */
+public class MSwitchInsn extends MInsnWithData {
+ /**
+ * The MInsns this switch instruction branches to.
+ */
+ public List<MInsn> targets = new LinkedList<MInsn>();
+
+ public boolean packed;
+
+ public int[] keys;
+
+ /**
+ * Clone this MSwitchInsn, and clone the wrapped Instruction.
+ */
+ public MSwitchInsn clone() {
+ MSwitchInsn newInsn = new MSwitchInsn();
+ newInsn.insn = insn.clone();
+ newInsn.dataTarget = dataTarget;
+ newInsn.packed = packed;
+ for (MInsn target : targets) {
+ newInsn.targets.add(target);
+ }
+ newInsn.keys = new int[keys.length];
+ System.arraycopy(keys, 0, newInsn.keys, 0, keys.length);
+ return newInsn;
+ }
+}
\ No newline at end of file
diff --git a/runtime/closure.h b/tools/dexfuzz/src/dexfuzz/program/MTryBlock.java
similarity index 65%
copy from runtime/closure.h
copy to tools/dexfuzz/src/dexfuzz/program/MTryBlock.java
index 9bea28f..a1dc029 100644
--- a/runtime/closure.h
+++ b/tools/dexfuzz/src/dexfuzz/program/MTryBlock.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,19 +14,16 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_CLOSURE_H_
-#define ART_RUNTIME_CLOSURE_H_
+package dexfuzz.program;
-namespace art {
+import java.util.List;
-class Thread;
-
-class Closure {
- public:
- virtual ~Closure() { }
- virtual void Run(Thread* self) = 0;
-};
-
-} // namespace art
-
-#endif // ART_RUNTIME_CLOSURE_H_
+/**
+ * Tracks where try blocks start and end.
+ */
+public class MTryBlock {
+ public MInsn startInsn;
+ public MInsn endInsn;
+ public List<MInsn> handlers;
+ public MInsn catchAllHandler;
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/MutatableCode.java b/tools/dexfuzz/src/dexfuzz/program/MutatableCode.java
new file mode 100644
index 0000000..c56b1bc
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MutatableCode.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.Log;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A class that represents a CodeItem in a way that is more amenable to mutation.
+ */
+public class MutatableCode {
+ /**
+ * To ensure we update the correct CodeItem in the raw DEX file.
+ */
+ public int codeItemIdx;
+
+ /**
+ * This is an index into the Program's list of MutatableCodes.
+ */
+ public int mutatableCodeIdx;
+
+ /**
+ * Number of registers this code uses.
+ */
+ public short registersSize;
+
+ /**
+ * Number of ins this code has.
+ */
+ public short insSize;
+
+ /**
+ * Number of outs this code has.
+ */
+ public short outsSize;
+
+ /**
+ * Number of tries this code has.
+ */
+ public short triesSize;
+
+ /**
+ * CodeTranslator is responsible for creating this, and
+ * converting it back to a list of Instructions.
+ */
+ private List<MInsn> mutatableInsns;
+
+ /**
+ * CodeTranslator is responsible for creating this, and
+ * converting it back to the correct form for CodeItems.
+ */
+ public List<MTryBlock> mutatableTries;
+
+ /**
+ * The name of the method this code represents.
+ */
+ public String name;
+ public String shorty;
+ public boolean isStatic;
+
+ /**
+ * The Program that owns this MutatableCode.
+ * Currently used to get the size of constant pools for
+ * PoolIndexChanger/RandomInstructionGenerator
+ */
+ public Program program;
+
+ private short originalInVReg;
+ private short tempVRegsAllocated;
+ private short initialTempVReg;
+ private boolean vregsNeedCopying;
+ private int numMoveInsnsGenerated;
+
+ public MutatableCode(Program program) {
+ this.program = program;
+ this.mutatableInsns = new LinkedList<MInsn>();
+ }
+
+ /**
+ * Call this to update all instructions after the provided mInsn, to have their
+ * locations adjusted by the provided offset. It will also mark that they have been updated.
+ */
+ public void updateInstructionLocationsAfter(MInsn mInsn, int offset) {
+ boolean updating = false;
+ for (MInsn mInsnChecking : mutatableInsns) {
+ if (updating) {
+ mInsnChecking.locationUpdated = true;
+ mInsnChecking.location += offset;
+ } else {
+ if (mInsnChecking == mInsn) {
+ updating = true;
+ }
+ }
+
+ }
+ }
+
+ private void recalculateLocations() {
+ int loc = 0;
+ for (MInsn mInsn : mutatableInsns) {
+ mInsn.location = loc;
+ loc += mInsn.insn.getSize();
+ }
+ }
+
+ public List<MInsn> getInstructions() {
+ return Collections.unmodifiableList(mutatableInsns);
+ }
+
+ public int getInstructionCount() {
+ return mutatableInsns.size();
+ }
+
+ public int getInstructionIndex(MInsn mInsn) {
+ return mutatableInsns.indexOf(mInsn);
+ }
+
+ public MInsn getInstructionAt(int idx) {
+ return mutatableInsns.get(idx);
+ }
+
+ public void addInstructionToEnd(MInsn mInsn) {
+ mutatableInsns.add(mInsn);
+ }
+
+ public void insertInstructionAfter(MInsn toBeInserted, int insertionIdx) {
+ if ((insertionIdx + 1) < mutatableInsns.size()) {
+ insertInstructionAt(toBeInserted, insertionIdx + 1);
+ } else {
+ // Appending to end.
+ MInsn finalInsn = mutatableInsns.get(mutatableInsns.size() - 1);
+ toBeInserted.location = finalInsn.location + finalInsn.insn.getSize();
+ mutatableInsns.add(toBeInserted);
+ }
+ }
+
+ /**
+ * Has same semantics as List.add()
+ */
+ public void insertInstructionAt(MInsn toBeInserted, int insertionIdx) {
+ MInsn currentInsn = mutatableInsns.get(insertionIdx);
+ toBeInserted.location = currentInsn.location;
+ mutatableInsns.add(insertionIdx , toBeInserted);
+ updateInstructionLocationsAfter(toBeInserted, toBeInserted.insn.getSize());
+ }
+
+ /**
+ * Checks if any MTryBlock's instruction refs pointed at the 'before' MInsn,
+ * and points them to the 'after' MInsn, if so. 'twoWay' will check if 'after'
+ * was pointed to, and point refs to the 'before' insn.
+ * (one-way is used when deleting instructions,
+ * two-way is used when swapping instructions.)
+ */
+ private void updateTryBlocksWithReplacementInsn(MInsn before, MInsn after,
+ boolean twoWay) {
+ if (triesSize > 0) {
+ for (MTryBlock mTryBlock : mutatableTries) {
+ if (mTryBlock.startInsn == before) {
+ Log.debug("Try block's first instruction was updated");
+ mTryBlock.startInsn = after;
+ } else if (twoWay && mTryBlock.startInsn == after) {
+ Log.debug("Try block's first instruction was updated");
+ mTryBlock.startInsn = before;
+ }
+ if (mTryBlock.endInsn == before) {
+ Log.debug("Try block's last instruction was updated");
+ mTryBlock.endInsn = after;
+ } else if (twoWay && mTryBlock.endInsn == after) {
+ Log.debug("Try block's last instruction was updated");
+ mTryBlock.endInsn = before;
+ }
+ if (mTryBlock.catchAllHandler == before) {
+ Log.debug("Try block's catch-all instruction was updated");
+ mTryBlock.catchAllHandler = after;
+ } else if (twoWay && mTryBlock.catchAllHandler == after) {
+ Log.debug("Try block's catch-all instruction was updated");
+ mTryBlock.catchAllHandler = before;
+ }
+
+ List<Integer> matchesIndicesToChange = new ArrayList<Integer>();
+ List<Integer> replacementIndicesToChange = null;
+ if (twoWay) {
+ replacementIndicesToChange = new ArrayList<Integer>();
+ }
+
+ int idx = 0;
+ for (MInsn handler : mTryBlock.handlers) {
+ if (handler == before) {
+ matchesIndicesToChange.add(idx);
+ Log.debug("Try block's handler instruction was updated");
+ } else if (twoWay && handler == after) {
+ replacementIndicesToChange.add(idx);
+ Log.debug("Try block's handler instruction was updated");
+ }
+ idx++;
+ }
+
+ for (int idxToChange : matchesIndicesToChange) {
+ mTryBlock.handlers.set(idxToChange, after);
+ }
+
+ if (twoWay) {
+ for (int idxToChange : replacementIndicesToChange) {
+ mTryBlock.handlers.set(idxToChange, before);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * The actual implementation of deleteInstruction called by
+ * the single-argument deleteInstructions.
+ */
+ private void deleteInstructionFull(MInsn toBeDeleted, int toBeDeletedIdx) {
+ // Make sure we update all locations afterwards first.
+ updateInstructionLocationsAfter(toBeDeleted, -(toBeDeleted.insn.getSize()));
+
+ // Remove it.
+ mutatableInsns.remove(toBeDeletedIdx);
+
+ // Update any branch instructions that branched to the instruction we just deleted!
+
+ // First, pick the replacement target.
+ int replacementTargetIdx = toBeDeletedIdx;
+ if (replacementTargetIdx == mutatableInsns.size()) {
+ replacementTargetIdx--;
+ }
+ MInsn replacementTarget = mutatableInsns.get(replacementTargetIdx);
+
+ for (MInsn mInsn : mutatableInsns) {
+ if (mInsn instanceof MBranchInsn) {
+ // Check if this branch insn points at the insn we just deleted.
+ MBranchInsn branchInsn = (MBranchInsn) mInsn;
+ MInsn target = branchInsn.target;
+ if (target == toBeDeleted) {
+ Log.debug(branchInsn + " was pointing at the deleted instruction, updated.");
+ branchInsn.target = replacementTarget;
+ }
+ } else if (mInsn instanceof MSwitchInsn) {
+ // Check if any of this switch insn's targets points at the insn we just deleted.
+ MSwitchInsn switchInsn = (MSwitchInsn) mInsn;
+ List<Integer> indicesToChange = new ArrayList<Integer>();
+ int idx = 0;
+ for (MInsn target : switchInsn.targets) {
+ if (target == toBeDeleted) {
+ indicesToChange.add(idx);
+ Log.debug(switchInsn + "[" + idx
+ + "] was pointing at the deleted instruction, updated.");
+ }
+ idx++;
+ }
+ for (int idxToChange : indicesToChange) {
+ switchInsn.targets.remove(idxToChange);
+ switchInsn.targets.add(idxToChange, replacementTarget);
+ }
+ }
+ }
+
+ // Now update the try blocks.
+ updateTryBlocksWithReplacementInsn(toBeDeleted, replacementTarget, false);
+ }
+
+ /**
+ * Delete the provided MInsn.
+ */
+ public void deleteInstruction(MInsn toBeDeleted) {
+ deleteInstructionFull(toBeDeleted, mutatableInsns.indexOf(toBeDeleted));
+ }
+
+ /**
+ * Delete the MInsn at the provided index.
+ */
+ public void deleteInstruction(int toBeDeletedIdx) {
+ deleteInstructionFull(mutatableInsns.get(toBeDeletedIdx), toBeDeletedIdx);
+ }
+
+ public void swapInstructionsByIndex(int aIdx, int bIdx) {
+ MInsn aInsn = mutatableInsns.get(aIdx);
+ MInsn bInsn = mutatableInsns.get(bIdx);
+
+ mutatableInsns.set(aIdx, bInsn);
+ mutatableInsns.set(bIdx, aInsn);
+
+ updateTryBlocksWithReplacementInsn(aInsn, bInsn, true);
+
+ recalculateLocations();
+ }
+
+ /**
+ * Some mutators may require the use of temporary registers. For instance,
+ * to easily add in printing of values without having to look for registers
+ * that aren't currently live.
+ * The idea is to allocate these registers at the top of the set of registers.
+ * Because this will then shift where the arguments to the method are, we then
+ * change the start of the method to copy the arguments to the method
+ * into the place where the rest of the method's code expects them to be.
+ * Call allocateTemporaryVRegs(n), then use getTemporaryVReg(n),
+ * and then make sure finishedUsingTemporaryVRegs() is called!
+ */
+ public void allocateTemporaryVRegs(int count) {
+ if (count > tempVRegsAllocated) {
+ if (tempVRegsAllocated == 0) {
+ Log.info("Allocating temporary vregs for method...");
+ initialTempVReg = registersSize;
+ originalInVReg = (short) (registersSize - insSize);
+ } else {
+ Log.info("Extending allocation of temporary vregs for method...");
+ }
+ registersSize = (short) (initialTempVReg + count);
+ if (outsSize < count) {
+ outsSize = (short) count;
+ }
+ vregsNeedCopying = true;
+ tempVRegsAllocated = (short) count;
+ }
+ }
+
+ public int getTemporaryVReg(int number) {
+ if (number >= tempVRegsAllocated) {
+ Log.errorAndQuit("Not allocated enough temporary vregs!");
+ }
+ return initialTempVReg + number;
+ }
+
+ public void finishedUsingTemporaryVRegs() {
+ if (tempVRegsAllocated > 0 && vregsNeedCopying) {
+ // Just delete all the move instructions and generate again, if we already have some.
+ while (numMoveInsnsGenerated > 0) {
+ deleteInstruction(0);
+ numMoveInsnsGenerated--;
+ }
+
+ Log.info("Moving 'in' vregs to correct locations after allocating temporary vregs");
+
+ int shortyIdx = 0;
+ if (isStatic) {
+ shortyIdx = 1;
+ }
+
+ int insertionCounter = 0;
+
+ // Insert copy insns that move all the in VRs down.
+ for (int i = 0; i < insSize; i++) {
+ MInsn moveInsn = new MInsn();
+ moveInsn.insn = new Instruction();
+ moveInsn.insn.vregA = originalInVReg + i;
+ moveInsn.insn.vregB = originalInVReg + i + tempVRegsAllocated;
+
+ char type = 'L';
+ if (shortyIdx > 0) {
+ type = shorty.charAt(shortyIdx);
+ }
+ shortyIdx++;
+
+ if (type == 'L') {
+ moveInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_OBJECT_16);
+ } else if (type == 'D' || type == 'J') {
+ moveInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_WIDE_16);
+ i++;
+ } else {
+ moveInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_16);
+ }
+
+ insertInstructionAt(moveInsn, insertionCounter);
+ insertionCounter++;
+ Log.info("Temp vregs creation, Added instruction " + moveInsn);
+ numMoveInsnsGenerated++;
+ }
+
+ vregsNeedCopying = false;
+ }
+ }
+
+ /**
+ * When we insert new Field/Type/MethodIds into the DEX file, this may shunt some Ids
+ * into a new position in the table. If this happens, every reference to the Ids must
+ * be updated! Because CodeItems have their Instructions wrapped into a graph of MInsns
+ * during mutation, they don't have a view of all their instructions during mutation,
+ * and so if they are asked to update their instructions' indices into the tables, they
+ * must call this method to get the actual list of instructions they currently own.
+ */
+ public List<Instruction> requestLatestInstructions() {
+ List<Instruction> latestInsns = new ArrayList<Instruction>();
+ for (MInsn mInsn : mutatableInsns) {
+ latestInsns.add(mInsn.insn);
+ }
+ return latestInsns;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/Mutation.java b/tools/dexfuzz/src/dexfuzz/program/Mutation.java
new file mode 100644
index 0000000..2eba718
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/Mutation.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.program.mutators.CodeMutator;
+
+/**
+ * Mutation should be subclassed by an AssociatedMutation in each CodeMutator,
+ * which will describe the parameters of the mutation, and override the getString()
+ * and parseString() methods here to allow serialization of the mutations.
+ */
+public abstract class Mutation {
+
+ public MutatableCode mutatableCode;
+
+ // The first field of any serialized mutation - the mutator that uses it.
+ public Class<? extends CodeMutator> mutatorClass;
+ // The second field of any serialized mutation...
+ // This is an index into the Program's list of MutatableCodes
+ // i.e., it is NOT an index into the DEX file's CodeItems!
+ public int mutatableCodeIdx;
+
+ public void setup(Class<? extends CodeMutator> mutatorClass, MutatableCode mutatableCode) {
+ this.mutatorClass = mutatorClass;
+ this.mutatableCode = mutatableCode;
+ this.mutatableCodeIdx = mutatableCode.mutatableCodeIdx;
+ }
+
+ public abstract String getString();
+
+ public abstract void parseString(String[] elements);
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/program/MutationSerializer.java b/tools/dexfuzz/src/dexfuzz/program/MutationSerializer.java
new file mode 100644
index 0000000..7f79517
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MutationSerializer.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.Log;
+import dexfuzz.program.mutators.CodeMutator;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+
+/**
+ * Responsible for serializing mutations, allowing replay of mutations, and searching
+ * for a minimal set of mutations.
+ */
+public class MutationSerializer {
+ public static String getMutationString(Mutation mutation) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(mutation.mutatorClass.getCanonicalName()).append(" ");
+ builder.append(mutation.mutatableCodeIdx).append(" ");
+ builder.append(mutation.getString());
+ return builder.toString();
+ }
+
+ public static void writeMutation(BufferedWriter writer, Mutation mutation) throws IOException {
+ // Write out the common fields.
+ writer.write(mutation.mutatorClass.getCanonicalName() + " "
+ + mutation.mutatableCodeIdx + " ");
+
+ // Use the mutation's own function to write out the rest of the fields.
+ writer.write(mutation.getString() + "\n");
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Mutation readMutation(BufferedReader reader) throws IOException {
+ String line = reader.readLine();
+ String[] fields = null;
+ if (line != null) {
+ fields = line.split(" ");
+ } else {
+ Log.errorAndQuit("Could not read line during mutation loading.");
+ }
+
+ // Read the mutator's class name
+ String mutatorClassName = fields[0];
+
+ // Get the class for that mutator
+ Class<? extends CodeMutator> mutatorClass = null;
+ try {
+ mutatorClass = (Class<? extends CodeMutator>) Class.forName(mutatorClassName);
+ } catch (ClassNotFoundException e) {
+ Log.errorAndQuit("Cannot find a mutator class called: " + mutatorClassName);
+ }
+
+ Mutation mutation = null;
+ try {
+ mutation = mutatorClass.newInstance().getNewMutation();
+ } catch (InstantiationException e) {
+ Log.errorAndQuit("Unable to instantiate " + mutatorClassName
+ + " using default constructor.");
+ } catch (IllegalAccessException e) {
+ Log.errorAndQuit("Unable to access methods in " + mutatorClassName + ".");
+ }
+
+ if (mutation == null) {
+ Log.errorAndQuit("Unable to get Mutation for Mutator: " + mutatorClassName);
+ }
+
+ // Populate the common fields of the mutation.
+ mutation.mutatorClass = mutatorClass;
+ // The Program must set this later, using the mutatable_code_idx
+ // into its list of MutatableCodes.
+ mutation.mutatableCode = null;
+ mutation.mutatableCodeIdx = Integer.parseInt(fields[1]);
+
+ // Use the mutation's own method to read the rest of the fields.
+ mutation.parseString(fields);
+
+ return mutation;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/Program.java b/tools/dexfuzz/src/dexfuzz/program/Program.java
new file mode 100644
index 0000000..286fe52
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/Program.java
@@ -0,0 +1,602 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.Options;
+import dexfuzz.listeners.BaseListener;
+import dexfuzz.program.mutators.ArithOpChanger;
+import dexfuzz.program.mutators.BranchShifter;
+import dexfuzz.program.mutators.CmpBiasChanger;
+import dexfuzz.program.mutators.CodeMutator;
+import dexfuzz.program.mutators.ConstantValueChanger;
+import dexfuzz.program.mutators.ConversionRepeater;
+import dexfuzz.program.mutators.FieldFlagChanger;
+import dexfuzz.program.mutators.InstructionDeleter;
+import dexfuzz.program.mutators.InstructionDuplicator;
+import dexfuzz.program.mutators.InstructionSwapper;
+import dexfuzz.program.mutators.NewMethodCaller;
+import dexfuzz.program.mutators.NonsenseStringPrinter;
+import dexfuzz.program.mutators.PoolIndexChanger;
+import dexfuzz.program.mutators.RandomInstructionGenerator;
+import dexfuzz.program.mutators.SwitchBranchShifter;
+import dexfuzz.program.mutators.TryBlockShifter;
+import dexfuzz.program.mutators.ValuePrinter;
+import dexfuzz.program.mutators.VRegChanger;
+import dexfuzz.rawdex.ClassDataItem;
+import dexfuzz.rawdex.ClassDefItem;
+import dexfuzz.rawdex.CodeItem;
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.EncodedField;
+import dexfuzz.rawdex.EncodedMethod;
+import dexfuzz.rawdex.FieldIdItem;
+import dexfuzz.rawdex.MethodIdItem;
+import dexfuzz.rawdex.ProtoIdItem;
+import dexfuzz.rawdex.RawDexFile;
+import dexfuzz.rawdex.TypeIdItem;
+import dexfuzz.rawdex.formats.ContainsPoolIndex.PoolIndexKind;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * After the raw DEX file has been parsed, it is passed into this class
+ * that represents the program in a mutatable form.
+ * The class uses a CodeTranslator to translate between the raw DEX form
+ * for a method, and the mutatable form. It also controls all CodeMutators,
+ * deciding which ones should be applied to each CodeItem.
+ */
+public class Program {
+ /**
+ * The RNG used during mutation.
+ */
+ private Random rng;
+
+ /**
+ * The seed that was given to the RNG.
+ */
+ public long rngSeed;
+
+ /**
+ * The parsed raw DEX file.
+ */
+ private RawDexFile rawDexFile;
+
+ /**
+ * The system responsible for translating from CodeItems to MutatableCode and vice-versa.
+ */
+ private CodeTranslator translator;
+
+ /**
+ * Responsible for adding new class ID items, method ID items, etc.
+ */
+ private IdCreator idCreator;
+
+ /**
+ * A list of all the MutatableCode that the CodeTranslator produced from
+ * CodeItems that are acceptable to mutate.
+ */
+ private List<MutatableCode> mutatableCodes;
+
+ /**
+ * A list of all MutatableCode items that were mutated when mutateTheProgram()
+ * was called. updateRawDexFile() will update the relevant CodeItems when called,
+ * and then clear this list.
+ */
+ private List<MutatableCode> mutatedCodes;
+
+ /**
+ * A list of all registered CodeMutators that this Program can use to mutate methods.
+ */
+ private List<CodeMutator> mutators;
+
+ /**
+ * Used if we're loading mutations from a file, so we can find the correct mutator.
+ */
+ private Map<Class<? extends CodeMutator>, CodeMutator> mutatorsLookupByClass;
+
+ /**
+ * Tracks mutation stats.
+ */
+ private MutationStats mutationStats;
+
+ /**
+ * A list of all mutations used for loading/dumping mutations from/to a file.
+ */
+ private List<Mutation> mutations;
+
+ /**
+ * The listener who is interested in events.
+ * May be a listener that is responsible for multiple listeners.
+ */
+ private BaseListener listener;
+
+ /**
+ * Given a maximum number of mutations that can be performed on a method, n,
+ * give up after attempting (n * this value) mutations for any method.
+ */
+ private static final int MAXIMUM_MUTATION_ATTEMPT_FACTOR = 10;
+
+ /**
+ * Construct the mutatable Program based on the raw DEX file that was parsed initially.
+ */
+ public Program(RawDexFile rawDexFile, List<Mutation> previousMutations,
+ BaseListener listener) {
+ this.listener = listener;
+
+ idCreator = new IdCreator(rawDexFile);
+
+ // Set up the RNG.
+ rng = new Random();
+ if (Options.usingProvidedSeed) {
+ rng.setSeed(Options.rngSeed);
+ rngSeed = Options.rngSeed;
+ } else {
+ long seed = System.currentTimeMillis();
+ listener.handleSeed(seed);
+ rng.setSeed(seed);
+ rngSeed = seed;
+ }
+
+ if (previousMutations != null) {
+ mutations = previousMutations;
+ } else {
+ // Allocate the mutations list.
+ mutations = new ArrayList<Mutation>();
+
+ // Read in the mutations if we need to.
+ if (Options.loadMutations) {
+ // Allocate the mutators lookup table.
+ mutatorsLookupByClass = new HashMap<Class<? extends CodeMutator>, CodeMutator>();
+ loadMutationsFromDisk(Options.loadMutationsFile);
+ }
+ }
+
+ // Allocate the mutators list.
+ mutators = new ArrayList<CodeMutator>();
+
+ this.rawDexFile = rawDexFile;
+
+ mutatableCodes = new ArrayList<MutatableCode>();
+ mutatedCodes = new ArrayList<MutatableCode>();
+
+ translator = new CodeTranslator();
+
+ mutationStats = new MutationStats();
+
+ // Register all the code mutators here.
+ registerMutator(new ArithOpChanger(rng, mutationStats, mutations));
+ registerMutator(new BranchShifter(rng, mutationStats, mutations));
+ registerMutator(new CmpBiasChanger(rng, mutationStats, mutations));
+ registerMutator(new ConstantValueChanger(rng, mutationStats, mutations));
+ registerMutator(new ConversionRepeater(rng, mutationStats, mutations));
+ registerMutator(new FieldFlagChanger(rng, mutationStats, mutations));
+ registerMutator(new InstructionDeleter(rng, mutationStats, mutations));
+ registerMutator(new InstructionDuplicator(rng, mutationStats, mutations));
+ registerMutator(new InstructionSwapper(rng, mutationStats, mutations));
+ registerMutator(new NewMethodCaller(rng, mutationStats, mutations));
+ registerMutator(new NonsenseStringPrinter(rng, mutationStats, mutations));
+ registerMutator(new PoolIndexChanger(rng, mutationStats, mutations));
+ registerMutator(new RandomInstructionGenerator(rng, mutationStats, mutations));
+ registerMutator(new SwitchBranchShifter(rng, mutationStats, mutations));
+ registerMutator(new TryBlockShifter(rng, mutationStats, mutations));
+ registerMutator(new ValuePrinter(rng, mutationStats, mutations));
+ registerMutator(new VRegChanger(rng, mutationStats, mutations));
+
+ associateClassDefsAndClassData();
+ associateCodeItemsWithMethodNames();
+
+ int codeItemIdx = 0;
+ for (CodeItem codeItem : rawDexFile.codeItems) {
+ if (legalToMutate(codeItem)) {
+ Log.debug("Legal to mutate code item " + codeItemIdx);
+ int mutatableCodeIdx = mutatableCodes.size();
+ mutatableCodes.add(translator.codeItemToMutatableCode(this, codeItem,
+ codeItemIdx, mutatableCodeIdx));
+ } else {
+ Log.debug("Not legal to mutate code item " + codeItemIdx);
+ }
+ codeItemIdx++;
+ }
+ }
+
+ private void registerMutator(CodeMutator mutator) {
+ if (mutator.canBeTriggered()) {
+ Log.debug("Registering mutator " + mutator.getClass().getSimpleName());
+ mutators.add(mutator);
+ }
+ if (Options.loadMutations) {
+ mutatorsLookupByClass.put(mutator.getClass(), mutator);
+ }
+ }
+
+ /**
+ * Associate ClassDefItem to a ClassDataItem and vice-versa.
+ * This is so when we're associating method names with code items,
+ * we can find the name of the class the method belongs to.
+ */
+ private void associateClassDefsAndClassData() {
+ for (ClassDefItem classDefItem : rawDexFile.classDefs) {
+ if (classDefItem.classDataOff.pointsToSomething()) {
+ ClassDataItem classDataItem = (ClassDataItem)
+ classDefItem.classDataOff.getPointedToItem();
+ classDataItem.meta.classDefItem = classDefItem;
+ classDefItem.meta.classDataItem = classDataItem;
+ }
+ }
+ }
+
+ /**
+ * For each CodeItem, find the name of the method the item represents.
+ * This is done to allow the filtering of mutating methods based on if
+ * they have the name *_MUTATE, but also for debugging info.
+ */
+ private void associateCodeItemsWithMethodNames() {
+ // Associate method names with codeItems.
+ for (ClassDataItem classDataItem : rawDexFile.classDatas) {
+
+ String className = "";
+ if (classDataItem.meta.classDefItem != null) {
+ int typeIdx = classDataItem.meta.classDefItem.classIdx;
+ TypeIdItem typeIdItem = rawDexFile.typeIds.get(typeIdx);
+ className = rawDexFile.stringDatas.get(typeIdItem.descriptorIdx).getString() + ".";
+ }
+
+ // Do direct methods...
+ // Track the current method index with this value, since the encoding in
+ // each EncodedMethod is the absolute index for the first EncodedMethod,
+ // and then relative index for the rest...
+ int methodIdx = 0;
+ for (EncodedMethod method : classDataItem.directMethods) {
+ methodIdx = associateMethod(method, methodIdx, className);
+ }
+ // Reset methodIdx for virtual methods...
+ methodIdx = 0;
+ for (EncodedMethod method : classDataItem.virtualMethods) {
+ methodIdx = associateMethod(method, methodIdx, className);
+ }
+ }
+ }
+
+ /**
+ * Associate the name of the provided method with its CodeItem, if it
+ * has one.
+ *
+ * @param methodIdx The method index of the last EncodedMethod that was handled in this class.
+ * @return The method index of the EncodedMethod that has just been handled in this class.
+ */
+ private int associateMethod(EncodedMethod method, int methodIdx, String className) {
+ if (!method.codeOff.pointsToSomething()) {
+ // This method doesn't have a code item, so we won't encounter it later.
+ return methodIdx;
+ }
+
+ // First method index is an absolute index.
+ // The rest are relative to the previous.
+ // (so if methodIdx is initialised to 0, this single line works)
+ methodIdx = methodIdx + method.methodIdxDiff;
+
+ // Get the name.
+ MethodIdItem methodIdItem = rawDexFile.methodIds.get(methodIdx);
+ ProtoIdItem protoIdItem = rawDexFile.protoIds.get(methodIdItem.protoIdx);
+ String shorty = rawDexFile.stringDatas.get(protoIdItem.shortyIdx).getString();
+ String methodName = className
+ + rawDexFile.stringDatas.get(methodIdItem.nameIdx).getString();
+
+ // Get the codeItem.
+ if (method.codeOff.getPointedToItem() instanceof CodeItem) {
+ CodeItem codeItem = (CodeItem) method.codeOff.getPointedToItem();
+ codeItem.meta.methodName = methodName;
+ codeItem.meta.shorty = shorty;
+ codeItem.meta.isStatic = method.isStatic();
+ } else {
+ Log.errorAndQuit("You've got an EncodedMethod that points to an Offsettable"
+ + " that does not contain a CodeItem");
+ }
+
+ return methodIdx;
+ }
+
+ /**
+ * Determine, based on the current options supplied to dexfuzz, as well as
+ * its capabilities, if the provided CodeItem can be mutated.
+ * @param codeItem The CodeItem we may wish to mutate.
+ * @return If the CodeItem can be mutated.
+ */
+ private boolean legalToMutate(CodeItem codeItem) {
+ if (!Options.mutateLimit) {
+ Log.debug("Mutating everything.");
+ return true;
+ }
+ if (codeItem.meta.methodName.endsWith("_MUTATE")) {
+ Log.debug("Code item marked with _MUTATE.");
+ return true;
+ }
+ Log.debug("Code item not marked with _MUTATE, but not mutating all code items.");
+ return false;
+ }
+
+ private int getNumberOfMutationsToPerform() {
+ // We want n mutations to be twice as likely as n+1 mutations.
+ //
+ // So if we have max 3,
+ // then 0 has 8 chances ("tickets"),
+ // 1 has 4 chances
+ // 2 has 2 chances
+ // and 3 has 1 chance
+
+ // Allocate the tickets
+ // n mutations need (2^(n+1) - 1) tickets
+ // e.g.
+ // 3 mutations => 15 tickets
+ // 4 mutations => 31 tickets
+ int tickets = (2 << Options.methodMutations) - 1;
+
+ // Pick the lucky ticket
+ int luckyTicket = rng.nextInt(tickets);
+
+ // The tickets are put into buckets with accordance with log-base-2.
+ // have to make sure it's luckyTicket + 1, because log(0) is undefined
+ // so:
+ // log_2(1) => 0
+ // log_2(2) => 1
+ // log_2(3) => 1
+ // log_2(4) => 2
+ // log_2(5) => 2
+ // log_2(6) => 2
+ // log_2(7) => 2
+ // log_2(8) => 3
+ // ...
+ // so to make the highest mutation value the rarest,
+ // subtract log_2(luckyTicket+1) from the maximum number
+ // log2(x) <=> 31 - Integer.numberOfLeadingZeros(x)
+ int luckyMutation = Options.methodMutations
+ - (31 - Integer.numberOfLeadingZeros(luckyTicket + 1));
+
+ return luckyMutation;
+ }
+
+ /**
+ * Returns true if we completely failed to mutate this method's mutatable code after
+ * attempting to.
+ */
+ private boolean mutateAMutatableCode(MutatableCode mutatableCode) {
+ int mutations = getNumberOfMutationsToPerform();
+
+ Log.info("Attempting " + mutations + " mutations for method " + mutatableCode.name);
+
+ int mutationsApplied = 0;
+
+ int maximumMutationAttempts = Options.methodMutations * MAXIMUM_MUTATION_ATTEMPT_FACTOR;
+ int mutationAttempts = 0;
+ boolean hadToBail = false;
+
+ while (mutationsApplied < mutations) {
+ int mutatorIdx = rng.nextInt(mutators.size());
+ CodeMutator mutator = mutators.get(mutatorIdx);
+ Log.info("Running mutator " + mutator.getClass().getSimpleName());
+ if (mutator.attemptToMutate(mutatableCode)) {
+ mutationsApplied++;
+ }
+ mutationAttempts++;
+ if (mutationAttempts > maximumMutationAttempts) {
+ Log.info("Bailing out on mutation for this method, tried too many times...");
+ hadToBail = true;
+ break;
+ }
+ }
+
+ // If any of them actually mutated it, excellent!
+ if (mutationsApplied > 0) {
+ Log.info("Method was mutated.");
+ mutatedCodes.add(mutatableCode);
+ } else {
+ Log.info("Method was not mutated.");
+ }
+
+ return ((mutationsApplied == 0) && hadToBail);
+ }
+
+ /**
+ * Go through each mutatable method in turn, and attempt to mutate it.
+ * Afterwards, call updateRawDexFile() to apply the results of mutation to the
+ * original code.
+ */
+ public void mutateTheProgram() {
+ if (Options.loadMutations) {
+ applyMutationsFromList();
+ return;
+ }
+
+ // Typically, this is 2 to 10...
+ int methodsToMutate = Options.minMethods
+ + rng.nextInt((Options.maxMethods - Options.minMethods) + 1);
+
+ // Check we aren't trying to mutate more methods than we have.
+ if (methodsToMutate > mutatableCodes.size()) {
+ methodsToMutate = mutatableCodes.size();
+ }
+
+ // Check if we're going to end up mutating all the methods.
+ if (methodsToMutate == mutatableCodes.size()) {
+ // Just do them all in order.
+ Log.info("Mutating all possible methods.");
+ for (MutatableCode mutatableCode : mutatableCodes) {
+ if (mutatableCode == null) {
+ Log.errorAndQuit("Why do you have a null MutatableCode?");
+ }
+ mutateAMutatableCode(mutatableCode);
+ }
+ Log.info("Finished mutating all possible methods.");
+ } else {
+ // Pick them at random.
+ Log.info("Randomly selecting " + methodsToMutate + " methods to mutate.");
+ while (mutatedCodes.size() < methodsToMutate) {
+ int randomMethodIdx = rng.nextInt(mutatableCodes.size());
+ MutatableCode mutatableCode = mutatableCodes.get(randomMethodIdx);
+ if (mutatableCode == null) {
+ Log.errorAndQuit("Why do you have a null MutatableCode?");
+ }
+ if (!mutatedCodes.contains(mutatableCode)) {
+ boolean completelyFailedToMutate = mutateAMutatableCode(mutatableCode);
+ if (completelyFailedToMutate) {
+ methodsToMutate--;
+ }
+ }
+ }
+ Log.info("Finished mutating the methods.");
+ }
+
+ listener.handleMutationStats(mutationStats.getStatsString());
+
+ if (Options.dumpMutations) {
+ writeMutationsToDisk(Options.dumpMutationsFile);
+ }
+ }
+
+ private void writeMutationsToDisk(String fileName) {
+ Log.debug("Writing mutations to disk.");
+ try {
+ BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
+ for (Mutation mutation : mutations) {
+ MutationSerializer.writeMutation(writer, mutation);
+ }
+ writer.close();
+ } catch (IOException e) {
+ Log.errorAndQuit("IOException while writing mutations to disk...");
+ }
+ }
+
+ private void loadMutationsFromDisk(String fileName) {
+ Log.debug("Loading mutations from disk.");
+ try {
+ BufferedReader reader = new BufferedReader(new FileReader(fileName));
+ while (reader.ready()) {
+ Mutation mutation = MutationSerializer.readMutation(reader);
+ mutations.add(mutation);
+ }
+ reader.close();
+ } catch (IOException e) {
+ Log.errorAndQuit("IOException while loading mutations from disk...");
+ }
+ }
+
+ private void applyMutationsFromList() {
+ Log.info("Applying preloaded list of mutations...");
+ for (Mutation mutation : mutations) {
+ // Repopulate the MutatableCode field from the recorded index into the Program's list.
+ mutation.mutatableCode = mutatableCodes.get(mutation.mutatableCodeIdx);
+
+ // Get the right mutator.
+ CodeMutator mutator = mutatorsLookupByClass.get(mutation.mutatorClass);
+
+ // Apply the mutation.
+ mutator.forceMutate(mutation);
+
+ // Add this mutatable code to the list of mutated codes, if we haven't already.
+ if (!mutatedCodes.contains(mutation.mutatableCode)) {
+ mutatedCodes.add(mutation.mutatableCode);
+ }
+ }
+ Log.info("...finished applying preloaded list of mutations.");
+ }
+
+ public List<Mutation> getMutations() {
+ return mutations;
+ }
+
+ /**
+ * Updates any CodeItems that need to be updated after mutation.
+ */
+ public boolean updateRawDexFile() {
+ boolean anythingMutated = !(mutatedCodes.isEmpty());
+ for (MutatableCode mutatedCode : mutatedCodes) {
+ translator.mutatableCodeToCodeItem(rawDexFile.codeItems
+ .get(mutatedCode.codeItemIdx), mutatedCode);
+ }
+ mutatedCodes.clear();
+ return anythingMutated;
+ }
+
+ public void writeRawDexFile(DexRandomAccessFile file) throws IOException {
+ rawDexFile.write(file);
+ }
+
+ public void updateRawDexFileHeader(DexRandomAccessFile file) throws IOException {
+ rawDexFile.updateHeader(file);
+ }
+
+ /**
+ * Used by the CodeMutators to determine legal index values.
+ */
+ public int getTotalPoolIndicesByKind(PoolIndexKind poolIndexKind) {
+ switch (poolIndexKind) {
+ case Type:
+ return rawDexFile.typeIds.size();
+ case Field:
+ return rawDexFile.fieldIds.size();
+ case String:
+ return rawDexFile.stringIds.size();
+ case Method:
+ return rawDexFile.methodIds.size();
+ case Invalid:
+ return 0;
+ default:
+ }
+ return 0;
+ }
+
+ /**
+ * Used by the CodeMutators to lookup and/or create Ids.
+ */
+ public IdCreator getNewItemCreator() {
+ return idCreator;
+ }
+
+ /**
+ * Used by FieldFlagChanger, to find an EncodedField for a specified field in an insn,
+ * if that field is actually defined in this DEX file. If not, null is returned.
+ */
+ public EncodedField getEncodedField(int fieldIdx) {
+ if (fieldIdx >= rawDexFile.fieldIds.size()) {
+ Log.debug(String.format("Field idx 0x%x specified is not defined in this DEX file.",
+ fieldIdx));
+ return null;
+ }
+ FieldIdItem fieldId = rawDexFile.fieldIds.get(fieldIdx);
+
+ for (ClassDefItem classDef : rawDexFile.classDefs) {
+ if (classDef.classIdx == fieldId.classIdx) {
+ ClassDataItem classData = classDef.meta.classDataItem;
+ return classData.getEncodedFieldWithIndex(fieldIdx);
+ }
+ }
+
+ Log.debug(String.format("Field idx 0x%x specified is not defined in this DEX file.",
+ fieldIdx));
+ return null;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/ArithOpChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/ArithOpChanger.java
new file mode 100644
index 0000000..4c69694
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/ArithOpChanger.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class ArithOpChanger extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int arithmeticInsnIdx;
+ public int newOpcode;
+
+ @Override
+ public String getString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(arithmeticInsnIdx).append(" ");
+ builder.append(newOpcode);
+ return builder.toString();
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ arithmeticInsnIdx = Integer.parseInt(elements[2]);
+ newOpcode = Integer.parseInt(elements[3]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public ArithOpChanger() { }
+
+ public ArithOpChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 75;
+ }
+
+ // A cache that should only exist between generateMutation() and applyMutation(),
+ // or be created at the start of applyMutation(), if we're reading in mutations from
+ // a file.
+ private List<MInsn> arithmeticInsns = null;
+
+ private void generateCachedArithmeticInsns(MutatableCode mutatableCode) {
+ if (arithmeticInsns != null) {
+ return;
+ }
+
+ arithmeticInsns = new ArrayList<MInsn>();
+
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (isArithmeticOperation(mInsn)) {
+ arithmeticInsns.add(mInsn);
+ }
+ }
+ }
+
+ @Override
+ protected boolean canMutate(MutatableCode mutatableCode) {
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (isArithmeticOperation(mInsn)) {
+ return true;
+ }
+ }
+
+ Log.debug("No arithmetic operations in method, skipping...");
+ return false;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ generateCachedArithmeticInsns(mutatableCode);
+
+ int arithmeticInsnIdx = rng.nextInt(arithmeticInsns.size());
+
+ MInsn randomInsn = arithmeticInsns.get(arithmeticInsnIdx);
+
+ OpcodeInfo oldOpcodeInfo = randomInsn.insn.info;
+
+ OpcodeInfo newOpcodeInfo = oldOpcodeInfo;
+
+ while (newOpcodeInfo.value == oldOpcodeInfo.value) {
+ newOpcodeInfo = Instruction.getOpcodeInfo(getLegalDifferentOpcode(randomInsn));
+ }
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.arithmeticInsnIdx = arithmeticInsnIdx;
+ mutation.newOpcode = newOpcodeInfo.value;
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ generateCachedArithmeticInsns(mutatableCode);
+
+ MInsn randomInsn = arithmeticInsns.get(mutation.arithmeticInsnIdx);
+
+ String oldInsnString = randomInsn.toString();
+
+ OpcodeInfo newOpcodeInfo = Instruction.getOpcodeInfo(mutation.newOpcode);
+
+ randomInsn.insn.info = newOpcodeInfo;
+
+ Log.info("Changed " + oldInsnString + " to " + randomInsn);
+
+ stats.incrementStat("Changed arithmetic opcode");
+
+ // Clear the cache.
+ arithmeticInsns = null;
+ }
+
+ private boolean isArithmeticOperation(MInsn mInsn) {
+ Opcode opcode = mInsn.insn.info.opcode;
+ if (Opcode.isBetween(opcode, Opcode.ADD_INT, Opcode.USHR_INT_LIT8)) {
+ return true;
+ }
+ return false;
+ }
+
+ private Opcode getLegalDifferentOpcode(MInsn mInsn) {
+ Opcode opcode = mInsn.insn.info.opcode;
+
+ for (List<Opcode> opcodeList : opcodeLists) {
+ Opcode first = opcodeList.get(0);
+ Opcode last = opcodeList.get(opcodeList.size() - 1);
+ if (Opcode.isBetween(opcode, first, last)) {
+ int newOpcodeIdx = rng.nextInt(opcodeList.size());
+ return opcodeList.get(newOpcodeIdx);
+ }
+ }
+
+ return opcode;
+ }
+
+ private static List<Opcode> intOpcodes = new ArrayList<Opcode>();
+ private static List<Opcode> int2addrOpcodes = new ArrayList<Opcode>();
+ private static List<Opcode> longOpcodes = new ArrayList<Opcode>();
+ private static List<Opcode> long2addrOpcodes = new ArrayList<Opcode>();
+ private static List<Opcode> floatOpcodes = new ArrayList<Opcode>();
+ private static List<Opcode> float2addrOpcodes = new ArrayList<Opcode>();
+ private static List<Opcode> doubleOpcodes = new ArrayList<Opcode>();
+ private static List<Opcode> double2addrOpcodes = new ArrayList<Opcode>();
+ private static List<Opcode> intLit8Opcodes = new ArrayList<Opcode>();
+ private static List<Opcode> intLit16Opcodes = new ArrayList<Opcode>();
+ private static List<List<Opcode>> opcodeLists = new ArrayList<List<Opcode>>();
+
+ static {
+ intOpcodes.add(Opcode.ADD_INT);
+ intOpcodes.add(Opcode.SUB_INT);
+ intOpcodes.add(Opcode.MUL_INT);
+ intOpcodes.add(Opcode.DIV_INT);
+ intOpcodes.add(Opcode.REM_INT);
+ intOpcodes.add(Opcode.AND_INT);
+ intOpcodes.add(Opcode.OR_INT);
+ intOpcodes.add(Opcode.XOR_INT);
+ intOpcodes.add(Opcode.SHL_INT);
+ intOpcodes.add(Opcode.SHR_INT);
+ intOpcodes.add(Opcode.USHR_INT);
+
+ int2addrOpcodes.add(Opcode.ADD_INT_2ADDR);
+ int2addrOpcodes.add(Opcode.SUB_INT_2ADDR);
+ int2addrOpcodes.add(Opcode.MUL_INT_2ADDR);
+ int2addrOpcodes.add(Opcode.DIV_INT_2ADDR);
+ int2addrOpcodes.add(Opcode.REM_INT_2ADDR);
+ int2addrOpcodes.add(Opcode.AND_INT_2ADDR);
+ int2addrOpcodes.add(Opcode.OR_INT_2ADDR);
+ int2addrOpcodes.add(Opcode.XOR_INT_2ADDR);
+ int2addrOpcodes.add(Opcode.SHL_INT_2ADDR);
+ int2addrOpcodes.add(Opcode.SHR_INT_2ADDR);
+ int2addrOpcodes.add(Opcode.USHR_INT_2ADDR);
+
+ longOpcodes.add(Opcode.ADD_LONG);
+ longOpcodes.add(Opcode.SUB_LONG);
+ longOpcodes.add(Opcode.MUL_LONG);
+ longOpcodes.add(Opcode.DIV_LONG);
+ longOpcodes.add(Opcode.REM_LONG);
+ longOpcodes.add(Opcode.AND_LONG);
+ longOpcodes.add(Opcode.OR_LONG);
+ longOpcodes.add(Opcode.XOR_LONG);
+ longOpcodes.add(Opcode.SHL_LONG);
+ longOpcodes.add(Opcode.SHR_LONG);
+ longOpcodes.add(Opcode.USHR_LONG);
+
+ long2addrOpcodes.add(Opcode.ADD_LONG_2ADDR);
+ long2addrOpcodes.add(Opcode.SUB_LONG_2ADDR);
+ long2addrOpcodes.add(Opcode.MUL_LONG_2ADDR);
+ long2addrOpcodes.add(Opcode.DIV_LONG_2ADDR);
+ long2addrOpcodes.add(Opcode.REM_LONG_2ADDR);
+ long2addrOpcodes.add(Opcode.AND_LONG_2ADDR);
+ long2addrOpcodes.add(Opcode.OR_LONG_2ADDR);
+ long2addrOpcodes.add(Opcode.XOR_LONG_2ADDR);
+ long2addrOpcodes.add(Opcode.SHL_LONG_2ADDR);
+ long2addrOpcodes.add(Opcode.SHR_LONG_2ADDR);
+ long2addrOpcodes.add(Opcode.USHR_LONG_2ADDR);
+
+ floatOpcodes.add(Opcode.ADD_FLOAT);
+ floatOpcodes.add(Opcode.SUB_FLOAT);
+ floatOpcodes.add(Opcode.MUL_FLOAT);
+ floatOpcodes.add(Opcode.DIV_FLOAT);
+ floatOpcodes.add(Opcode.REM_FLOAT);
+
+ float2addrOpcodes.add(Opcode.ADD_FLOAT_2ADDR);
+ float2addrOpcodes.add(Opcode.SUB_FLOAT_2ADDR);
+ float2addrOpcodes.add(Opcode.MUL_FLOAT_2ADDR);
+ float2addrOpcodes.add(Opcode.DIV_FLOAT_2ADDR);
+ float2addrOpcodes.add(Opcode.REM_FLOAT_2ADDR);
+
+ doubleOpcodes.add(Opcode.ADD_DOUBLE);
+ doubleOpcodes.add(Opcode.SUB_DOUBLE);
+ doubleOpcodes.add(Opcode.MUL_DOUBLE);
+ doubleOpcodes.add(Opcode.DIV_DOUBLE);
+ doubleOpcodes.add(Opcode.REM_DOUBLE);
+
+ double2addrOpcodes.add(Opcode.ADD_DOUBLE_2ADDR);
+ double2addrOpcodes.add(Opcode.SUB_DOUBLE_2ADDR);
+ double2addrOpcodes.add(Opcode.MUL_DOUBLE_2ADDR);
+ double2addrOpcodes.add(Opcode.DIV_DOUBLE_2ADDR);
+ double2addrOpcodes.add(Opcode.REM_DOUBLE_2ADDR);
+
+ intLit8Opcodes.add(Opcode.ADD_INT_LIT8);
+ intLit8Opcodes.add(Opcode.RSUB_INT_LIT8);
+ intLit8Opcodes.add(Opcode.MUL_INT_LIT8);
+ intLit8Opcodes.add(Opcode.DIV_INT_LIT8);
+ intLit8Opcodes.add(Opcode.REM_INT_LIT8);
+ intLit8Opcodes.add(Opcode.AND_INT_LIT8);
+ intLit8Opcodes.add(Opcode.OR_INT_LIT8);
+ intLit8Opcodes.add(Opcode.XOR_INT_LIT8);
+ intLit8Opcodes.add(Opcode.SHL_INT_LIT8);
+ intLit8Opcodes.add(Opcode.SHR_INT_LIT8);
+ intLit8Opcodes.add(Opcode.USHR_INT_LIT8);
+
+ intLit16Opcodes.add(Opcode.ADD_INT_LIT16);
+ intLit16Opcodes.add(Opcode.RSUB_INT);
+ intLit16Opcodes.add(Opcode.MUL_INT_LIT16);
+ intLit16Opcodes.add(Opcode.DIV_INT_LIT16);
+ intLit16Opcodes.add(Opcode.REM_INT_LIT16);
+ intLit16Opcodes.add(Opcode.AND_INT_LIT16);
+ intLit16Opcodes.add(Opcode.OR_INT_LIT16);
+ intLit16Opcodes.add(Opcode.XOR_INT_LIT16);
+
+ opcodeLists.add(intOpcodes);
+ opcodeLists.add(longOpcodes);
+ opcodeLists.add(floatOpcodes);
+ opcodeLists.add(doubleOpcodes);
+ opcodeLists.add(int2addrOpcodes);
+ opcodeLists.add(long2addrOpcodes);
+ opcodeLists.add(float2addrOpcodes);
+ opcodeLists.add(double2addrOpcodes);
+ opcodeLists.add(intLit8Opcodes);
+ opcodeLists.add(intLit16Opcodes);
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/BranchShifter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/BranchShifter.java
new file mode 100644
index 0000000..a28f5ba
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/BranchShifter.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MBranchInsn;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class BranchShifter extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int branchInsnIdx;
+ public int newTargetIdx;
+
+ @Override
+ public String getString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(branchInsnIdx).append(" ");
+ builder.append(newTargetIdx);
+ return builder.toString();
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ branchInsnIdx = Integer.parseInt(elements[2]);
+ newTargetIdx = Integer.parseInt(elements[3]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public BranchShifter() { }
+
+ public BranchShifter(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 30;
+ }
+
+ // A cache that should only exist between generateMutation() and applyMutation(),
+ // or be created at the start of applyMutation(), if we're reading in mutations from
+ // a file.
+ private List<MBranchInsn> branchInsns;
+
+ private void generateCachedBranchInsns(MutatableCode mutatableCode) {
+ if (branchInsns != null) {
+ return;
+ }
+
+ branchInsns = new ArrayList<MBranchInsn>();
+
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (mInsn instanceof MBranchInsn) {
+ branchInsns.add((MBranchInsn) mInsn);
+ }
+ }
+ }
+
+ @Override
+ protected boolean canMutate(MutatableCode mutatableCode) {
+ // Can't shift a branch if there's only one instruction in the method.
+ if (mutatableCode.getInstructionCount() == 1) {
+ Log.debug("Method contains only one instruction, skipping.");
+ return false;
+ }
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (mInsn instanceof MBranchInsn) {
+ return true;
+ }
+ }
+
+ Log.debug("Method contains no branch instructions.");
+ return false;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ generateCachedBranchInsns(mutatableCode);
+
+ // Pick a random branching instruction.
+ int branchInsnIdx = rng.nextInt(branchInsns.size());
+ MBranchInsn branchInsn = branchInsns.get(branchInsnIdx);
+
+ // Get its original target, find its index.
+ MInsn oldTargetInsn = branchInsn.target;
+ int oldTargetInsnIdx = mutatableCode.getInstructionIndex(oldTargetInsn);
+
+ int newTargetIdx = oldTargetInsnIdx;
+
+ int delta = 0;
+
+ // Keep searching for a new index.
+ while (newTargetIdx == oldTargetInsnIdx) {
+ // Vary by +/- 2 instructions.
+ delta = 0;
+ while (delta == 0) {
+ delta = (rng.nextInt(5) - 2);
+ }
+
+ newTargetIdx = oldTargetInsnIdx + delta;
+
+ // Check the new index is legal.
+ if (newTargetIdx < 0) {
+ newTargetIdx = 0;
+ } else if (newTargetIdx >= mutatableCode.getInstructionCount()) {
+ newTargetIdx = mutatableCode.getInstructionCount() - 1;
+ }
+ }
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.branchInsnIdx = branchInsnIdx;
+ mutation.newTargetIdx = newTargetIdx;
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ generateCachedBranchInsns(mutatableCode);
+
+ MBranchInsn branchInsn = branchInsns.get(mutation.branchInsnIdx);
+
+ // Get the new target.
+ MInsn newTargetInsn = mutatableCode.getInstructionAt(mutation.newTargetIdx);
+
+ // Set the new target.
+ branchInsn.target = newTargetInsn;
+
+ Log.info("Shifted the target of " + branchInsn + " to point to " + newTargetInsn);
+
+ stats.incrementStat("Shifted branch target");
+
+ // Clear cache.
+ branchInsns = null;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/CmpBiasChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/CmpBiasChanger.java
new file mode 100644
index 0000000..dc60e79
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/CmpBiasChanger.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class CmpBiasChanger extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int cmpBiasInsnIdx;
+
+ @Override
+ public String getString() {
+ return Integer.toString(cmpBiasInsnIdx);
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ cmpBiasInsnIdx = Integer.parseInt(elements[2]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public CmpBiasChanger() { }
+
+ public CmpBiasChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 30;
+ }
+
+ // A cache that should only exist between generateMutation() and applyMutation(),
+ // or be created at the start of applyMutation(), if we're reading in mutations from
+ // a file.
+ private List<MInsn> cmpBiasInsns = null;
+
+ private void generateCachedCmpBiasInsns(MutatableCode mutatableCode) {
+ if (cmpBiasInsns != null) {
+ return;
+ }
+
+ cmpBiasInsns = new ArrayList<MInsn>();
+
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (isCmpBiasOperation(mInsn)) {
+ cmpBiasInsns.add(mInsn);
+ }
+ }
+ }
+
+ @Override
+ protected boolean canMutate(MutatableCode mutatableCode) {
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (isCmpBiasOperation(mInsn)) {
+ return true;
+ }
+ }
+
+ Log.debug("No cmp-with-bias operations in method, skipping...");
+ return false;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ generateCachedCmpBiasInsns(mutatableCode);
+
+ int cmpBiasInsnIdx = rng.nextInt(cmpBiasInsns.size());
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.cmpBiasInsnIdx = cmpBiasInsnIdx;
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ generateCachedCmpBiasInsns(mutatableCode);
+
+ MInsn cmpBiasInsn = cmpBiasInsns.get(mutation.cmpBiasInsnIdx);
+
+ String oldInsnString = cmpBiasInsn.toString();
+
+ Opcode newOpcode = getLegalDifferentOpcode(cmpBiasInsn);
+
+ cmpBiasInsn.insn.info = Instruction.getOpcodeInfo(newOpcode);
+
+ Log.info("Changed " + oldInsnString + " to " + cmpBiasInsn);
+
+ stats.incrementStat("Changed comparison bias");
+
+ // Clear cache.
+ cmpBiasInsns = null;
+ }
+
+ private Opcode getLegalDifferentOpcode(MInsn mInsn) {
+ Opcode opcode = mInsn.insn.info.opcode;
+ if (opcode == Opcode.CMPG_DOUBLE) {
+ return Opcode.CMPL_DOUBLE;
+ }
+ if (opcode == Opcode.CMPL_DOUBLE) {
+ return Opcode.CMPG_DOUBLE;
+ }
+ if (opcode == Opcode.CMPG_FLOAT) {
+ return Opcode.CMPL_FLOAT;
+ }
+ return Opcode.CMPG_FLOAT;
+ }
+
+ private boolean isCmpBiasOperation(MInsn mInsn) {
+ Opcode opcode = mInsn.insn.info.opcode;
+ if (Opcode.isBetween(opcode, Opcode.CMPL_FLOAT, Opcode.CMPG_DOUBLE)) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/CodeMutator.java b/tools/dexfuzz/src/dexfuzz/program/mutators/CodeMutator.java
new file mode 100644
index 0000000..be566ad
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/CodeMutator.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.Options;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Random;
+
+/**
+ * The base class for all classes that can mutate methods.
+ */
+public abstract class CodeMutator {
+ /**
+ * The RNG, passed in by the Program that initialised us.
+ */
+ protected Random rng;
+
+ /**
+ * Used to track which mutations happen.
+ */
+ protected MutationStats stats;
+
+ /**
+ * Used to track mutations that have been applied so far.
+ */
+ protected List<Mutation> mutations;
+
+ /**
+ * The chance, out of 100, that this mutator actually mutates the the program
+ * when asked to by the Program. The default is 50% chance, but each mutator that
+ * extends CodeMutator should its own default.
+ */
+ protected int likelihood = 50;
+
+ /**
+ * This constructor is only intended for use in MutationRecorder...
+ */
+ public CodeMutator() {
+
+ }
+
+ /**
+ * Constructor that all subclasses must call...
+ *
+ * @param rng The RNG that the Program created.
+ */
+ public CodeMutator(Random rng, MutationStats stats, List<Mutation> mutations) {
+ this.rng = rng;
+ this.stats = stats;
+ this.mutations = mutations;
+
+ String name = this.getClass().getSimpleName().toLowerCase();
+
+ if (Options.mutationLikelihoods.containsKey(name)) {
+ likelihood = Options.mutationLikelihoods.get(name);
+ Log.info("Set mutation likelihood to " + likelihood
+ + "% for " + this.getClass().getSimpleName());
+ }
+ }
+
+ /**
+ * When the Program picks a particular mutator to mutate the code, it calls
+ * this function, that determines if the mutator will actually mutate the code.
+ * If so, it then calls the mutationFunction() method, that every subclass CodeMutator
+ * is expected to implement to perform its mutation.
+ *
+ * @return If mutation took place.
+ */
+ public boolean attemptToMutate(MutatableCode mutatableCode) {
+ if (shouldMutate(mutatableCode)) {
+ generateAndApplyMutation(mutatableCode);
+ return true;
+ }
+ Log.info("Skipping mutation.");
+ return false;
+ }
+
+ public void forceMutate(Mutation mutation) {
+ Log.info("Forcing mutation.");
+ applyMutation(mutation);
+ }
+
+ public boolean canBeTriggered() {
+ return (likelihood > 0);
+ }
+
+ /**
+ * Randomly determine if the mutator will actually mutate a method, based on its
+ * provided likelihood of mutation.
+ *
+ * @return If the method should be mutated.
+ */
+ private boolean shouldMutate(MutatableCode mutatableCode) {
+ return ((rng.nextInt(100) < likelihood) && canMutate(mutatableCode));
+ }
+
+ private void generateAndApplyMutation(MutatableCode mutatableCode) {
+ Mutation mutation = generateMutation(mutatableCode);
+ // Always save the mutation.
+ mutations.add(mutation);
+ applyMutation(mutation);
+ }
+
+ /**
+ * A CodeMutator must override this method if there is any reason why could not mutate
+ * a particular method, and return false in that case.
+ */
+ protected boolean canMutate(MutatableCode mutatableCode) {
+ return true;
+ }
+
+ protected abstract Mutation generateMutation(MutatableCode mutatableCode);
+
+ protected abstract void applyMutation(Mutation uncastMutation);
+
+ public abstract Mutation getNewMutation();
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/ConstantValueChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/ConstantValueChanger.java
new file mode 100644
index 0000000..22f04e8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/ConstantValueChanger.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.formats.ContainsConst;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class ConstantValueChanger extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int constInsnIdx;
+ public long newConstant;
+
+ @Override
+ public String getString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(constInsnIdx).append(" ");
+ builder.append(newConstant);
+ return builder.toString();
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ constInsnIdx = Integer.parseInt(elements[2]);
+ newConstant = Long.parseLong(elements[3]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public ConstantValueChanger() { }
+
+ public ConstantValueChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 70;
+ }
+
+ // A cache that should only exist between generateMutation() and applyMutation(),
+ // or be created at the start of applyMutation(), if we're reading in mutations from
+ // a file.
+ private List<MInsn> constInsns = null;
+
+ private void generateCachedConstInsns(MutatableCode mutatableCode) {
+ if (constInsns != null) {
+ return;
+ }
+
+ constInsns = new ArrayList<MInsn>();
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (mInsn.insn.info.format instanceof ContainsConst) {
+ constInsns.add(mInsn);
+ }
+ }
+ }
+
+ @Override
+ protected boolean canMutate(MutatableCode mutatableCode) {
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (mInsn.insn.info.format instanceof ContainsConst) {
+ return true;
+ }
+ }
+
+ Log.debug("Method contains no const instructions.");
+ return false;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ generateCachedConstInsns(mutatableCode);
+
+ // Pick a random const instruction.
+ int constInsnIdx = rng.nextInt(constInsns.size());
+ MInsn constInsn = constInsns.get(constInsnIdx);
+
+ // Get the constant.
+ long oldConstant = ((ContainsConst)constInsn.insn.info.format).getConst(constInsn.insn);
+
+ long newConstant = oldConstant;
+
+ // Make a new constant.
+ while (newConstant == oldConstant) {
+ newConstant = rng.nextLong()
+ % ((ContainsConst)constInsn.insn.info.format).getConstRange();
+ }
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.constInsnIdx = constInsnIdx;
+ mutation.newConstant = newConstant;
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ generateCachedConstInsns(mutatableCode);
+
+ MInsn constInsn = constInsns.get(mutation.constInsnIdx);
+
+ long oldConstant = ((ContainsConst)constInsn.insn.info.format).getConst(constInsn.insn);
+
+ Log.info("Changed constant value #" + oldConstant + " to #" + mutation.newConstant
+ + " in " + constInsn);
+
+ stats.incrementStat("Changed constant value");
+
+ // Set the new constant.
+ ((ContainsConst)constInsn.insn.info.format).setConst(constInsn.insn, mutation.newConstant);
+
+ // Clear cache.
+ constInsns = null;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/ConversionRepeater.java b/tools/dexfuzz/src/dexfuzz/program/mutators/ConversionRepeater.java
new file mode 100644
index 0000000..7fdf304
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/ConversionRepeater.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class ConversionRepeater extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int conversionInsnIdx;
+
+ @Override
+ public String getString() {
+ return Integer.toString(conversionInsnIdx);
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ conversionInsnIdx = Integer.parseInt(elements[2]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public ConversionRepeater() { }
+
+ public ConversionRepeater(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 50;
+ }
+
+ // A cache that should only exist between generateMutation() and applyMutation(),
+ // or be created at the start of applyMutation(), if we're reading in mutations from
+ // a file.
+ private List<MInsn> conversionInsns = null;
+
+ private void generateCachedConversionInsns(MutatableCode mutatableCode) {
+ if (conversionInsns != null) {
+ return;
+ }
+
+ conversionInsns = new ArrayList<MInsn>();
+
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (isConversionInstruction(mInsn)) {
+ conversionInsns.add(mInsn);
+ }
+ }
+ }
+
+ @Override
+ protected boolean canMutate(MutatableCode mutatableCode) {
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (isConversionInstruction(mInsn)) {
+ return true;
+ }
+ }
+
+ Log.debug("No conversion operations in method, skipping...");
+ return false;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ generateCachedConversionInsns(mutatableCode);
+ int conversionInsnIdx = rng.nextInt(conversionInsns.size());
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.conversionInsnIdx = conversionInsnIdx;
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ generateCachedConversionInsns(mutatableCode);
+
+ MInsn originalInsn = conversionInsns.get(mutation.conversionInsnIdx);
+
+ // We want to create two new instructions:
+ // [original conversion] eg float-to-int
+ // NEW: [there] eg int-to-float (with vregs of first inst swapped)
+ // NEW: [back] eg float-to-int
+
+ // Create the "there" instruction.
+ MInsn newInsnThere = originalInsn.clone();
+
+ // Flip the opcode.
+ Opcode oppositeOpcode = null;
+ switch (newInsnThere.insn.info.opcode) {
+ case INT_TO_LONG:
+ oppositeOpcode = Opcode.LONG_TO_INT;
+ break;
+ case INT_TO_FLOAT:
+ oppositeOpcode = Opcode.FLOAT_TO_INT;
+ break;
+ case INT_TO_DOUBLE:
+ oppositeOpcode = Opcode.DOUBLE_TO_INT;
+ break;
+ case LONG_TO_INT:
+ oppositeOpcode = Opcode.INT_TO_LONG;
+ break;
+ case LONG_TO_FLOAT:
+ oppositeOpcode = Opcode.FLOAT_TO_LONG;
+ break;
+ case LONG_TO_DOUBLE:
+ oppositeOpcode = Opcode.DOUBLE_TO_LONG;
+ break;
+ case FLOAT_TO_INT:
+ oppositeOpcode = Opcode.INT_TO_FLOAT;
+ break;
+ case FLOAT_TO_LONG:
+ oppositeOpcode = Opcode.LONG_TO_FLOAT;
+ break;
+ case FLOAT_TO_DOUBLE:
+ oppositeOpcode = Opcode.DOUBLE_TO_FLOAT;
+ break;
+ case DOUBLE_TO_INT:
+ oppositeOpcode = Opcode.INT_TO_DOUBLE;
+ break;
+ case DOUBLE_TO_LONG:
+ oppositeOpcode = Opcode.LONG_TO_DOUBLE;
+ break;
+ case DOUBLE_TO_FLOAT:
+ oppositeOpcode = Opcode.FLOAT_TO_DOUBLE;
+ break;
+ default:
+ Log.errorAndQuit(
+ "Trying to repeat the conversion in an insn that is not a conversion insn.");
+ }
+ newInsnThere.insn.info = Instruction.getOpcodeInfo(oppositeOpcode);
+
+ // Swap the vregs.
+ long tempReg = newInsnThere.insn.vregA;
+ newInsnThere.insn.vregA = newInsnThere.insn.vregB;
+ newInsnThere.insn.vregB = tempReg;
+
+ // Create the "back" instruction.
+ MInsn newInsnBack = originalInsn.clone();
+
+ // Get the index into the MutatableCode's mInsns list for this insn.
+ int originalInsnIdx = mutatableCode.getInstructionIndex(originalInsn);
+
+ // Insert the new instructions.
+ mutatableCode.insertInstructionAfter(newInsnThere, originalInsnIdx);
+ mutatableCode.insertInstructionAfter(newInsnBack, originalInsnIdx + 1);
+
+ Log.info("Performing conversion repetition for " + originalInsn);
+
+ stats.incrementStat("Repeating conversion");
+
+ // Clear the cache.
+ conversionInsns = null;
+ }
+
+ private boolean isConversionInstruction(MInsn mInsn) {
+ Opcode opcode = mInsn.insn.info.opcode;
+ if (Opcode.isBetween(opcode, Opcode.INT_TO_LONG, Opcode.DOUBLE_TO_FLOAT)) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/FieldFlagChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/FieldFlagChanger.java
new file mode 100644
index 0000000..0849d12
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/FieldFlagChanger.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.EncodedField;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.formats.ContainsPoolIndex;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class FieldFlagChanger extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int fieldInsnIdx;
+ public boolean setVolatile;
+
+ @Override
+ public String getString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(fieldInsnIdx).append(" ");
+ builder.append(setVolatile);
+ return builder.toString();
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ fieldInsnIdx = Integer.parseInt(elements[2]);
+ setVolatile = Boolean.parseBoolean(elements[3]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public FieldFlagChanger() { }
+
+ public FieldFlagChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 40;
+ }
+
+ // A cache that should only exist between generateMutation() and applyMutation(),
+ // or be created at the start of applyMutation(), if we're reading in mutations from
+ // a file.
+ private List<MInsn> fieldInsns = null;
+
+ private void generateCachedFieldInsns(MutatableCode mutatableCode) {
+ if (fieldInsns != null) {
+ return;
+ }
+
+ fieldInsns = new ArrayList<MInsn>();
+
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (isFileDefinedFieldInstruction(mInsn, mutatableCode)) {
+ fieldInsns.add(mInsn);
+ }
+ }
+ }
+
+ @Override
+ protected boolean canMutate(MutatableCode mutatableCode) {
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (isFileDefinedFieldInstruction(mInsn, mutatableCode)) {
+ return true;
+ }
+ }
+
+ Log.debug("No field instructions in method, skipping...");
+ return false;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ generateCachedFieldInsns(mutatableCode);
+
+ int fieldInsnIdx = rng.nextInt(fieldInsns.size());
+
+ Instruction insn = fieldInsns.get(fieldInsnIdx).insn;
+ ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) insn.info.format;
+ int fieldIdx = containsPoolIndex.getPoolIndex(insn);
+ EncodedField encodedField = mutatableCode.program.getEncodedField(fieldIdx);
+
+ boolean setVolatile = false;
+ if (!encodedField.isVolatile()) {
+ setVolatile = true;
+ }
+ // TODO: Flip more flags?
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.fieldInsnIdx = fieldInsnIdx;
+ mutation.setVolatile = setVolatile;
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ generateCachedFieldInsns(mutatableCode);
+
+ Instruction insn = fieldInsns.get(mutation.fieldInsnIdx).insn;
+ ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) insn.info.format;
+ int fieldIdx = containsPoolIndex.getPoolIndex(insn);
+ EncodedField encodedField = mutatableCode.program.getEncodedField(fieldIdx);
+
+ if (mutation.setVolatile) {
+ encodedField.setVolatile(true);
+ Log.info("Set field idx " + fieldIdx + " as volatile");
+ } else {
+ encodedField.setVolatile(false);
+ Log.info("Set field idx " + fieldIdx + " as not volatile");
+ }
+
+ stats.incrementStat("Changed volatility of field");
+
+ // Clear cache.
+ fieldInsns = null;
+ }
+
+ private boolean isFileDefinedFieldInstruction(MInsn mInsn, MutatableCode mutatableCode) {
+ Opcode opcode = mInsn.insn.info.opcode;
+ if (Opcode.isBetween(opcode, Opcode.IGET, Opcode.SPUT_SHORT)) {
+ Instruction insn = mInsn.insn;
+ ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) insn.info.format;
+ int fieldIdx = containsPoolIndex.getPoolIndex(insn);
+ if (mutatableCode.program.getEncodedField(fieldIdx) != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDeleter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDeleter.java
new file mode 100644
index 0000000..8ffa4c5
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDeleter.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MInsnWithData;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Random;
+
+public class InstructionDeleter extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int insnToDeleteIdx;
+
+ @Override
+ public String getString() {
+ return Integer.toString(insnToDeleteIdx);
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ insnToDeleteIdx = Integer.parseInt(elements[2]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public InstructionDeleter() { }
+
+ public InstructionDeleter(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 40;
+ }
+
+ @Override
+ protected boolean canMutate(MutatableCode mutatableCode) {
+ if (mutatableCode.getInstructionCount() < 4) {
+ // TODO: Make this more sophisticated - right now this is to avoid problems with
+ // a method that has 3 instructions: fill-array-data; return-void; <data for fill-array-data>
+ Log.debug("Cannot delete insns in a method with only a few.");
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ // Pick an instruction at random...
+ int insnIdx = rng.nextInt(mutatableCode.getInstructionCount());
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.insnToDeleteIdx = insnIdx;
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ MInsn toBeDeleted =
+ mutatableCode.getInstructionAt(mutation.insnToDeleteIdx);
+
+ Log.info("Deleting " + toBeDeleted);
+
+ stats.incrementStat("Deleted instruction");
+
+ // Delete the instruction.
+ mutatableCode.deleteInstruction(mutation.insnToDeleteIdx);
+
+ // If we delete a with-data insn, we should delete the associated data insn as well.
+ if (toBeDeleted instanceof MInsnWithData) {
+ Log.info(toBeDeleted + " had associated data, so the data insn was deleted.");
+ // Get the data instruction.
+ MInsn dataInsn =
+ ((MInsnWithData)toBeDeleted).dataTarget;
+ mutatableCode.deleteInstruction(dataInsn);
+ stats.incrementStat("Deleted a with-data insn's data");
+ }
+ // If we delete a data insn, we should delete the associated with-data insn as well.
+ if (toBeDeleted.insn.justRaw) {
+ // .justRaw implies that this is a data insn.
+ Log.info(toBeDeleted
+ + " was data, so the associated with-data insn was deleted.");
+
+ // First, find the with-data insn that is pointing to this insn.
+ MInsn withDataInsn = null;
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (mInsn instanceof MInsnWithData) {
+ if (((MInsnWithData)mInsn).dataTarget == toBeDeleted) {
+ withDataInsn = mInsn;
+ break;
+ }
+ }
+ }
+
+ // Now delete the with-data insn.
+ if (withDataInsn != null) {
+ mutatableCode.deleteInstruction(withDataInsn);
+ stats.incrementStat("Deleted a data insn's with-data insn");
+ } else {
+ Log.errorAndQuit("Tried to delete a data insn, "
+ + "but it didn't have any with-data insn pointing at it?");
+ }
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDuplicator.java b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDuplicator.java
new file mode 100644
index 0000000..4917056
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDuplicator.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.List;
+import java.util.Random;
+
+public class InstructionDuplicator extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int insnToDuplicateIdx;
+
+ @Override
+ public String getString() {
+ return Integer.toString(insnToDuplicateIdx);
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ insnToDuplicateIdx = Integer.parseInt(elements[2]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public InstructionDuplicator() { }
+
+ public InstructionDuplicator(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 80;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ boolean foundInsn = false;
+ int insnIdx = 0;
+
+ while (!foundInsn) {
+ // Pick an instruction at random...
+ insnIdx = rng.nextInt(mutatableCode.getInstructionCount());
+ MInsn oldInsn = mutatableCode.getInstructionAt(insnIdx);
+ foundInsn = true;
+ Opcode opcode = oldInsn.insn.info.opcode;
+ // ...check it's a legal instruction to duplicate.
+ if (opcode == Opcode.SPARSE_SWITCH || opcode == Opcode.PACKED_SWITCH
+ || opcode == Opcode.FILL_ARRAY_DATA || oldInsn.insn.justRaw) {
+ foundInsn = false;
+ }
+ }
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.insnToDuplicateIdx = insnIdx;
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ MInsn oldInsn = mutatableCode.getInstructionAt(mutation.insnToDuplicateIdx);
+
+ MInsn newInsn = oldInsn.clone();
+
+ Log.info("Duplicating " + oldInsn);
+
+ stats.incrementStat("Duplicated instruction");
+
+ mutatableCode.insertInstructionAt(newInsn, mutation.insnToDuplicateIdx);
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionSwapper.java b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionSwapper.java
new file mode 100644
index 0000000..17ea939
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionSwapper.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Random;
+
+public class InstructionSwapper extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int swapInsnIdx;
+ public int swapWithInsnIdx;
+
+ @Override
+ public String getString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(swapInsnIdx).append(" ");
+ builder.append(swapWithInsnIdx);
+ return builder.toString();
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ swapInsnIdx = Integer.parseInt(elements[2]);
+ swapWithInsnIdx = Integer.parseInt(elements[3]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public InstructionSwapper() { }
+
+ public InstructionSwapper(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 80;
+ }
+
+ @Override
+ protected boolean canMutate(MutatableCode mutatableCode) {
+ if (mutatableCode.getInstructionCount() == 1) {
+ // Cannot swap one instruction.
+ Log.debug("Cannot swap insns in a method with only one.");
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ int swapInsnIdx = 0;
+ int swapWithInsnIdx = 0;
+
+ boolean foundFirstInsn = false;
+ boolean foundSecondInsn = false;
+
+ while (!foundFirstInsn || !foundSecondInsn) {
+ // Look for the first insn.
+ while (!foundFirstInsn) {
+ swapInsnIdx = rng.nextInt(mutatableCode.getInstructionCount());
+ MInsn toBeSwapped = mutatableCode.getInstructionAt(swapInsnIdx);
+ foundFirstInsn = true;
+ if (toBeSwapped.insn.justRaw) {
+ foundFirstInsn = false;
+ }
+ }
+
+ // Look for the second insn.
+ int secondInsnAttempts = 0;
+ while (!foundSecondInsn) {
+ int delta = rng.nextInt(5) - 1;
+
+ if (delta == 0) {
+ continue;
+ }
+
+ swapWithInsnIdx = swapInsnIdx + delta;
+ foundSecondInsn = true;
+
+ // Check insn is in valid range.
+ if (swapWithInsnIdx < 0) {
+ foundSecondInsn = false;
+ } else if (swapWithInsnIdx >= mutatableCode.getInstructionCount()) {
+ foundSecondInsn = false;
+ }
+
+ // Finally, check if we're swapping with a raw insn.
+ if (foundSecondInsn) {
+ if (mutatableCode.getInstructionAt(swapWithInsnIdx).insn.justRaw) {
+ foundSecondInsn = false;
+ }
+ }
+
+ // If we've checked 10 times for an insn to swap with,
+ // and still found nothing, then try a new first insn.
+ if (!foundSecondInsn) {
+ secondInsnAttempts++;
+ if (secondInsnAttempts == 10) {
+ foundFirstInsn = false;
+ break;
+ }
+ }
+ }
+ }
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.swapInsnIdx = swapInsnIdx;
+ mutation.swapWithInsnIdx = swapWithInsnIdx;
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ MInsn toBeSwapped = mutatableCode.getInstructionAt(mutation.swapInsnIdx);
+ MInsn swappedWith = mutatableCode.getInstructionAt(mutation.swapWithInsnIdx);
+
+ Log.info("Swapping " + toBeSwapped + " with " + swappedWith);
+
+ stats.incrementStat("Swapped two instructions");
+
+ mutatableCode.swapInstructionsByIndex(mutation.swapInsnIdx, mutation.swapWithInsnIdx);
+
+ Log.info("Now " + swappedWith + " is swapped with " + toBeSwapped);
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/NewMethodCaller.java b/tools/dexfuzz/src/dexfuzz/program/mutators/NewMethodCaller.java
new file mode 100644
index 0000000..88a2f9a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/NewMethodCaller.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Instruction.InvokeFormatInfo;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.List;
+import java.util.Random;
+
+public class NewMethodCaller extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public enum InvokeType {
+ VIRTUAL,
+ DIRECT,
+ SUPER,
+ STATIC,
+ INTERFACE
+ }
+
+ public int insertionIdx;
+ public InvokeType invokeType;
+ public String className;
+ public String methodName;
+ public String signature;
+ public int numArgs;
+ public int[] args;
+
+ @Override
+ public String getString() {
+ StringBuilder argsString = new StringBuilder();
+ for (int i = 0; i < numArgs; i++) {
+ argsString.append(args[i]);
+ if (i < (numArgs - 1)) {
+ argsString.append(" ");
+ }
+ }
+ String result = String.format("%d %d %s %s %s %d %s",
+ insertionIdx,
+ invokeType.ordinal(),
+ className,
+ methodName,
+ signature,
+ numArgs,
+ argsString);
+ return result;
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ insertionIdx = Integer.parseInt(elements[2]);
+ invokeType = InvokeType.values()[Integer.parseInt(elements[3])];
+ className = elements[4];
+ methodName = elements[5];
+ signature = elements[6];
+ numArgs = Integer.parseInt(elements[7]);
+ args = new int[numArgs];
+ for (int i = 0; i < numArgs; i++) {
+ args[i] = Integer.parseInt(elements[8 + i]);
+ }
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public NewMethodCaller() { }
+
+ public NewMethodCaller(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 10;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ // Find the insertion point.
+ int insertionIdx = 0;
+ boolean foundInsn = false;
+
+ while (!foundInsn) {
+ insertionIdx = rng.nextInt(mutatableCode.getInstructionCount());
+ MInsn insertionPoint =
+ mutatableCode.getInstructionAt(insertionIdx);
+ foundInsn = true;
+
+ // Don't want to insert instructions where there are raw instructions for now.
+ if (insertionPoint.insn.justRaw) {
+ foundInsn = false;
+ }
+ }
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.insertionIdx = insertionIdx;
+
+ // TODO: Right now this mutator can only insert calls to System.gc() Add more!
+
+ mutation.invokeType = AssociatedMutation.InvokeType.STATIC;
+ mutation.className = "Ljava/lang/System;";
+ mutation.methodName = "gc";
+ mutation.signature = "()V";
+ mutation.numArgs = 0;
+
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ MInsn newInsn = new MInsn();
+ newInsn.insn = new Instruction();
+
+ switch (mutation.invokeType) {
+ case VIRTUAL:
+ newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_VIRTUAL);
+ break;
+ case DIRECT:
+ newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_DIRECT);
+ break;
+ case SUPER:
+ newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_SUPER);
+ break;
+ case STATIC:
+ newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_STATIC);
+ break;
+ case INTERFACE:
+ newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_INTERFACE);
+ break;
+ default:
+ }
+
+ // TODO: Handle more than just static invokes.
+
+ int methodIdx = mutatableCode.program.getNewItemCreator()
+ .findOrCreateMethodId(mutation.className,
+ mutation.methodName, mutation.signature);
+
+ newInsn.insn.vregB = methodIdx;
+ newInsn.insn.invokeFormatInfo = new InvokeFormatInfo();
+
+ // TODO: More field population, when we call methods that take arguments.
+
+ MInsn insertionPoint =
+ mutatableCode.getInstructionAt(mutation.insertionIdx);
+
+ Log.info(String.format("Called new method %s %s %s, inserting at %s",
+ mutation.className, mutation.methodName, mutation.signature, insertionPoint));
+
+ stats.incrementStat("Called new method");
+
+ mutatableCode.insertInstructionAt(newInsn, mutation.insertionIdx);
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/NonsenseStringPrinter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/NonsenseStringPrinter.java
new file mode 100644
index 0000000..b4c9d7b
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/NonsenseStringPrinter.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.List;
+import java.util.Random;
+
+public class NonsenseStringPrinter extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int insertionIdx;
+ public String nonsenseString;
+
+ @Override
+ public String getString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(insertionIdx).append(" ");
+ builder.append(nonsenseString);
+ return builder.toString();
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ insertionIdx = Integer.parseInt(elements[2]);
+ nonsenseString = elements[3];
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public NonsenseStringPrinter() { }
+
+ public NonsenseStringPrinter(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 10;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ // Find the insertion point
+ int insertionIdx = 0;
+ boolean foundInsn = false;
+
+ while (!foundInsn) {
+ insertionIdx = rng.nextInt(mutatableCode.getInstructionCount());
+ MInsn insertionPoint =
+ mutatableCode.getInstructionAt(insertionIdx);
+ foundInsn = true;
+
+ // Don't want to insert instructions where there are raw instructions for now.
+ if (insertionPoint.insn.justRaw) {
+ foundInsn = false;
+ }
+ }
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.insertionIdx = insertionIdx;
+ mutation.nonsenseString = getRandomString();
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ int outFieldIdx = mutatableCode.program.getNewItemCreator().findOrCreateFieldId(
+ "Ljava/lang/System;",
+ "Ljava/io/PrintStream;",
+ "out");
+ int printMethodIdx = mutatableCode.program.getNewItemCreator().findOrCreateMethodId(
+ "Ljava/io/PrintStream;",
+ "print",
+ "(Ljava/lang/String;)V");
+ int nonsenseStringIdx = mutatableCode.program.getNewItemCreator().findOrCreateString(
+ mutation.nonsenseString);
+
+ MInsn insertionPoint = mutatableCode.getInstructionAt(mutation.insertionIdx);
+
+ mutatableCode.allocateTemporaryVRegs(2);
+
+ int streamRegister = mutatableCode.getTemporaryVReg(0);
+ int stringRegister = mutatableCode.getTemporaryVReg(1);
+
+ // Load into string and stream into the temporary registers.
+ // then call print(stream, string)
+ MInsn constStringInsn = new MInsn();
+ constStringInsn.insn = new Instruction();
+ constStringInsn.insn.info = Instruction.getOpcodeInfo(Opcode.CONST_STRING);
+ constStringInsn.insn.vregB = nonsenseStringIdx;
+ constStringInsn.insn.vregA = stringRegister;
+
+ MInsn streamLoadInsn = new MInsn();
+ streamLoadInsn.insn = new Instruction();
+ streamLoadInsn.insn.info = Instruction.getOpcodeInfo(Opcode.SGET_OBJECT);
+ streamLoadInsn.insn.vregB = outFieldIdx;
+ streamLoadInsn.insn.vregA = streamRegister;
+
+ MInsn invokeInsn = new MInsn();
+ invokeInsn.insn = new Instruction();
+ invokeInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_VIRTUAL_RANGE);
+ invokeInsn.insn.vregA = 2;
+ invokeInsn.insn.vregB = printMethodIdx;
+ invokeInsn.insn.vregC = streamRegister;
+
+ Log.info(String.format("Printing nonsense string '%s', inserting at %s",
+ mutation.nonsenseString, insertionPoint));
+
+ stats.incrementStat("Printed nonsense string");
+
+ mutatableCode.insertInstructionAt(invokeInsn, mutation.insertionIdx);
+ mutatableCode.insertInstructionAt(streamLoadInsn, mutation.insertionIdx);
+ mutatableCode.insertInstructionAt(constStringInsn, mutation.insertionIdx);
+
+ mutatableCode.finishedUsingTemporaryVRegs();
+ }
+
+ private String getRandomString() {
+ int size = rng.nextInt(10);
+ int start = (int) 'A';
+ int end = (int) 'Z';
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < size; i++) {
+ builder.append((char) (rng.nextInt((end + 1) - start) + start));
+ }
+ return builder.toString();
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/PoolIndexChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/PoolIndexChanger.java
new file mode 100644
index 0000000..cae5dc1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/PoolIndexChanger.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.formats.ContainsPoolIndex;
+import dexfuzz.rawdex.formats.ContainsPoolIndex.PoolIndexKind;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class PoolIndexChanger extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int poolIndexInsnIdx;
+ public int newPoolIndex;
+
+ @Override
+ public String getString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(poolIndexInsnIdx).append(" ");
+ builder.append(newPoolIndex);
+ return builder.toString();
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ poolIndexInsnIdx = Integer.parseInt(elements[2]);
+ newPoolIndex = Integer.parseInt(elements[3]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public PoolIndexChanger() { }
+
+ public PoolIndexChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 30;
+ }
+
+ // A cache that should only exist between generateMutation() and applyMutation(),
+ // or be created at the start of applyMutation(), if we're reading in mutations from
+ // a file.
+ private List<MInsn> poolIndexInsns = null;
+
+ private void generateCachedPoolIndexInsns(MutatableCode mutatableCode) {
+ if (poolIndexInsns != null) {
+ return;
+ }
+
+ poolIndexInsns = new ArrayList<MInsn>();
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (mInsn.insn.info.format instanceof ContainsPoolIndex) {
+ poolIndexInsns.add(mInsn);
+ }
+ }
+ }
+
+ @Override
+ protected boolean canMutate(MutatableCode mutatableCode) {
+ // Remember what kinds of pool indices we see.
+ List<PoolIndexKind> seenKinds = new ArrayList<PoolIndexKind>();
+
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (mInsn.insn.info.format instanceof ContainsPoolIndex) {
+
+ ContainsPoolIndex containsPoolIndex =
+ (ContainsPoolIndex)mInsn.insn.info.format;
+
+ PoolIndexKind newPoolIndexKind =
+ containsPoolIndex.getPoolIndexKind(mInsn.insn.info);
+
+ seenKinds.add(newPoolIndexKind);
+ }
+ }
+
+ // Now check that there exists a kind such that the max pool index for
+ // the kind is greater than 1 (i.e., something can be changed)
+ if (!seenKinds.isEmpty()) {
+
+ for (PoolIndexKind kind : seenKinds) {
+ int numPoolIndices = mutatableCode.program.getTotalPoolIndicesByKind(kind);
+ if (numPoolIndices > 1) {
+ return true;
+ }
+ }
+
+ Log.debug("Method does not contain any insns that index into a const pool size > 1");
+ return false;
+ }
+
+ Log.debug("Method contains no instructions that index into the constant pool.");
+ return false;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ generateCachedPoolIndexInsns(mutatableCode);
+
+ int poolIndexInsnIdx = 0;
+ boolean found = false;
+
+ int oldPoolIndex = 0;
+ int newPoolIndex = 0;
+ int maxPoolIndex = 0;
+
+ // Pick a random instruction.
+ while (!found) {
+ poolIndexInsnIdx = rng.nextInt(poolIndexInsns.size());
+ MInsn poolIndexInsn = poolIndexInsns.get(poolIndexInsnIdx);
+
+ found = true;
+
+ ContainsPoolIndex containsPoolIndex =
+ (ContainsPoolIndex)poolIndexInsn.insn.info.format;
+
+ // Get the pool index.
+ oldPoolIndex = containsPoolIndex.getPoolIndex(poolIndexInsn.insn);
+ newPoolIndex = oldPoolIndex;
+
+ // Get the largest pool index available for the provided kind of pool index.
+ PoolIndexKind poolIndexKind =
+ containsPoolIndex.getPoolIndexKind(poolIndexInsn.insn.info);
+ maxPoolIndex = mutatableCode.program.getTotalPoolIndicesByKind(poolIndexKind);
+
+ if (maxPoolIndex <= 1) {
+ found = false;
+ }
+ }
+
+ // Get a new pool index.
+ while (newPoolIndex == oldPoolIndex) {
+ newPoolIndex = rng.nextInt(maxPoolIndex);
+ }
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.poolIndexInsnIdx = poolIndexInsnIdx;
+ mutation.newPoolIndex = newPoolIndex;
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ generateCachedPoolIndexInsns(mutatableCode);
+
+ MInsn poolIndexInsn = poolIndexInsns.get(mutation.poolIndexInsnIdx);
+
+ ContainsPoolIndex containsPoolIndex =
+ (ContainsPoolIndex) poolIndexInsn.insn.info.format;
+
+ int oldPoolIndex = containsPoolIndex.getPoolIndex(poolIndexInsn.insn);
+
+ Log.info("Changed pool index " + oldPoolIndex + " to " + mutation.newPoolIndex
+ + " in " + poolIndexInsn);
+
+ stats.incrementStat("Changed constant pool index");
+
+ // Set the new pool index.
+ containsPoolIndex.setPoolIndex(poolIndexInsn.insn, mutation.newPoolIndex);
+
+ // Clear cache.
+ poolIndexInsns = null;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/RandomInstructionGenerator.java b/tools/dexfuzz/src/dexfuzz/program/mutators/RandomInstructionGenerator.java
new file mode 100644
index 0000000..ff43c7c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/RandomInstructionGenerator.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MBranchInsn;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+import dexfuzz.rawdex.formats.AbstractFormat;
+import dexfuzz.rawdex.formats.ContainsConst;
+import dexfuzz.rawdex.formats.ContainsPoolIndex;
+import dexfuzz.rawdex.formats.ContainsPoolIndex.PoolIndexKind;
+import dexfuzz.rawdex.formats.ContainsVRegs;
+
+import java.util.List;
+import java.util.Random;
+
+public class RandomInstructionGenerator extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int insertionIdx;
+ public int newOpcode;
+ public boolean hasConst;
+ public long constValue;
+ public boolean hasPoolIndex;
+ public int poolIndexValue;
+ public boolean hasVregs;
+ public int vregCount;
+ public int vregA;
+ public int vregB;
+ public int vregC;
+ public int branchTargetIdx;
+
+ @Override
+ public String getString() {
+ String result = String.format("%d %d %s %d %s %d %s %d %d %d %d %d",
+ insertionIdx,
+ newOpcode,
+ (hasConst ? "T" : "F"),
+ constValue,
+ (hasPoolIndex ? "T" : "F"),
+ poolIndexValue,
+ (hasVregs ? "T" : "F"),
+ vregCount,
+ vregA,
+ vregB,
+ vregC,
+ branchTargetIdx
+ );
+ return result;
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ insertionIdx = Integer.parseInt(elements[2]);
+ newOpcode = Integer.parseInt(elements[3]);
+ hasConst = (elements[4].equals("T"));
+ constValue = Long.parseLong(elements[5]);
+ hasPoolIndex = (elements[6].equals("T"));
+ poolIndexValue = Integer.parseInt(elements[7]);
+ hasVregs = (elements[8].equals("T"));
+ vregCount = Integer.parseInt(elements[9]);
+ vregA = Integer.parseInt(elements[10]);
+ vregB = Integer.parseInt(elements[11]);
+ vregC = Integer.parseInt(elements[12]);
+ branchTargetIdx = Integer.parseInt(elements[13]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public RandomInstructionGenerator() { }
+
+ public RandomInstructionGenerator(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 30;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ // Find the insertion point.
+ int insertionIdx = 0;
+ boolean foundInsn = false;
+
+ while (!foundInsn) {
+ insertionIdx = rng.nextInt(mutatableCode.getInstructionCount());
+ MInsn insertionPoint =
+ mutatableCode.getInstructionAt(insertionIdx);
+ foundInsn = true;
+
+ // Don't want to insert instructions where there are raw instructions for now.
+ if (insertionPoint.insn.justRaw) {
+ foundInsn = false;
+ }
+ }
+
+ Opcode newOpcode = null;
+ int opcodeCount = Opcode.values().length;
+ boolean foundOpcode = false;
+
+ while (!foundOpcode) {
+ newOpcode = Opcode.values()[rng.nextInt(opcodeCount)];
+ foundOpcode = true;
+ if (Opcode.isBetween(newOpcode, Opcode.FILLED_NEW_ARRAY, Opcode.FILL_ARRAY_DATA)
+ || Opcode.isBetween(newOpcode, Opcode.PACKED_SWITCH, Opcode.SPARSE_SWITCH)
+ || Opcode.isBetween(newOpcode, Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_INTERFACE)
+ || Opcode.isBetween(newOpcode,
+ Opcode.INVOKE_VIRTUAL_RANGE, Opcode.INVOKE_INTERFACE_RANGE)
+ // Can never accept these instructions at compile time.
+ || Opcode.isBetween(newOpcode, Opcode.IGET_QUICK, Opcode.IPUT_SHORT_QUICK)
+ // Unused opcodes...
+ || Opcode.isBetween(newOpcode, Opcode.UNUSED_3E, Opcode.UNUSED_43)
+ || Opcode.isBetween(newOpcode, Opcode.UNUSED_79, Opcode.UNUSED_7A)
+ || Opcode.isBetween(newOpcode, Opcode.UNUSED_EF, Opcode.UNUSED_FF)) {
+ foundOpcode = false;
+ }
+ }
+
+ OpcodeInfo newOpcodeInfo = Instruction.getOpcodeInfo(newOpcode);
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.insertionIdx = insertionIdx;
+ mutation.newOpcode = newOpcodeInfo.value;
+
+ AbstractFormat fmt = newOpcodeInfo.format;
+
+ if (fmt instanceof ContainsConst) {
+ mutation.hasConst = true;
+ mutation.constValue = rng.nextLong() % ((ContainsConst)fmt).getConstRange();
+ }
+ if (fmt instanceof ContainsPoolIndex) {
+ mutation.hasPoolIndex = true;
+ ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) fmt;
+ PoolIndexKind poolIndexKind = containsPoolIndex.getPoolIndexKind(newOpcodeInfo);
+ int maxPoolIndex = mutatableCode.program.getTotalPoolIndicesByKind(poolIndexKind);
+ if (maxPoolIndex > 0) {
+ mutation.poolIndexValue = rng.nextInt(maxPoolIndex);
+ } else {
+ mutation.poolIndexValue = 0;
+ }
+ }
+ if (mutatableCode.registersSize == 0) {
+ mutatableCode.registersSize = 1;
+ }
+ if (fmt instanceof ContainsVRegs) {
+ mutation.hasVregs = true;
+ ContainsVRegs containsVregs = (ContainsVRegs) fmt;
+ mutation.vregCount = containsVregs.getVRegCount();
+ switch (mutation.vregCount) {
+ case 3:
+ mutation.vregC = rng.nextInt(mutatableCode.registersSize);
+ // fallthrough
+ case 2:
+ mutation.vregB = rng.nextInt(mutatableCode.registersSize);
+ // fallthrough
+ case 1:
+ mutation.vregA = rng.nextInt(mutatableCode.registersSize);
+ break;
+ default:
+ Log.errorAndQuit("Invalid number of vregs specified.");
+ }
+ }
+ // If we have some kind of branch, pick a random target.
+ if (Opcode.isBetween(newOpcode, Opcode.IF_EQ, Opcode.IF_LEZ)
+ || Opcode.isBetween(newOpcode, Opcode.GOTO, Opcode.GOTO_32)) {
+ mutation.branchTargetIdx = rng.nextInt(mutatableCode.getInstructionCount());
+ }
+
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ Opcode newOpcode = Instruction.getOpcodeInfo(mutation.newOpcode).opcode;
+
+ boolean isBranch = false;
+ if (Opcode.isBetween(newOpcode, Opcode.IF_EQ, Opcode.IF_LEZ)
+ || Opcode.isBetween(newOpcode, Opcode.GOTO, Opcode.GOTO_32)) {
+ isBranch = true;
+ }
+
+ MInsn newInsn = null;
+ if (!isBranch) {
+ newInsn = new MInsn();
+ } else {
+ newInsn = new MBranchInsn();
+ }
+ newInsn.insn = new Instruction();
+ newInsn.insn.info = Instruction.getOpcodeInfo(mutation.newOpcode);
+ AbstractFormat fmt = newInsn.insn.info.format;
+
+ if (mutation.hasConst) {
+ ContainsConst containsConst = (ContainsConst) fmt;
+ containsConst.setConst(newInsn.insn, mutation.constValue);
+ }
+ if (mutation.hasPoolIndex) {
+ ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) fmt;
+ containsPoolIndex.setPoolIndex(newInsn.insn, mutation.poolIndexValue);
+ }
+ if (mutation.hasVregs) {
+ switch (mutation.vregCount) {
+ case 3:
+ newInsn.insn.vregC = mutation.vregC;
+ // fallthrough
+ case 2:
+ newInsn.insn.vregB = mutation.vregB;
+ // fallthrough
+ case 1:
+ newInsn.insn.vregA = mutation.vregA;
+ break;
+ default:
+ Log.errorAndQuit("Invalid number of vregs specified.");
+ }
+ }
+
+ if (isBranch) {
+ // We have a branch instruction, point it at its target.
+ MBranchInsn newBranchInsn = (MBranchInsn) newInsn;
+ newBranchInsn.target = mutatableCode.getInstructionAt(mutation.branchTargetIdx);
+ }
+
+ MInsn insertionPoint =
+ mutatableCode.getInstructionAt(mutation.insertionIdx);
+
+ Log.info("Generated random instruction: " + newInsn
+ + ", inserting at " + insertionPoint);
+
+ stats.incrementStat("Generated random instruction");
+
+ mutatableCode.insertInstructionAt(newInsn, mutation.insertionIdx);
+
+ // If we've generated a monitor insn, generate the matching opposing insn.
+ if (newInsn.insn.info.opcode == Opcode.MONITOR_ENTER) {
+ MInsn exitInsn = newInsn.clone();
+ exitInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MONITOR_EXIT);
+ mutatableCode.insertInstructionAfter(exitInsn, mutation.insertionIdx);
+ Log.info("Generated matching monitor-exit: " + exitInsn);
+ } else if (newInsn.insn.info.opcode == Opcode.MONITOR_EXIT) {
+ MInsn enterInsn = newInsn.clone();
+ enterInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MONITOR_ENTER);
+ mutatableCode.insertInstructionAt(enterInsn, mutation.insertionIdx);
+ Log.info("Generated matching monitor-enter: " + enterInsn);
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/SwitchBranchShifter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/SwitchBranchShifter.java
new file mode 100644
index 0000000..6981034
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/SwitchBranchShifter.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MSwitchInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class SwitchBranchShifter extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int switchInsnIdx;
+ public int switchTargetIdx;
+ public int newTargetIdx;
+
+ @Override
+ public String getString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(switchInsnIdx).append(" ");
+ builder.append(switchTargetIdx).append(" ");
+ builder.append(newTargetIdx);
+ return builder.toString();
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ switchInsnIdx = Integer.parseInt(elements[2]);
+ switchTargetIdx = Integer.parseInt(elements[3]);
+ newTargetIdx = Integer.parseInt(elements[4]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public SwitchBranchShifter() { }
+
+ public SwitchBranchShifter(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 30;
+ }
+
+ // A cache that should only exist between generateMutation() and applyMutation(),
+ // or be created at the start of applyMutation(), if we're reading in mutations from
+ // a file.
+ private List<MSwitchInsn> switchInsns;
+
+ private void generateCachedSwitchInsns(MutatableCode mutatableCode) {
+ if (switchInsns != null) {
+ return;
+ }
+
+ switchInsns = new ArrayList<MSwitchInsn>();
+
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (mInsn instanceof MSwitchInsn) {
+ switchInsns.add((MSwitchInsn) mInsn);
+ }
+ }
+ }
+
+ @Override
+ protected boolean canMutate(MutatableCode mutatableCode) {
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (mInsn instanceof MSwitchInsn) {
+ return true;
+ }
+ }
+
+ Log.debug("Method contains no switch instructions.");
+ return false;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ generateCachedSwitchInsns(mutatableCode);
+
+ // Pick a random switch instruction.
+ int switchInsnIdx = rng.nextInt(switchInsns.size());
+ MSwitchInsn switchInsn = switchInsns.get(switchInsnIdx);
+
+ // Pick a random one of its targets.
+ int switchTargetIdx = rng.nextInt(switchInsn.targets.size());
+
+ // Get the original target, find its index.
+ MInsn oldTargetInsn = switchInsn.targets.get(switchTargetIdx);
+ int oldTargetInsnIdx = mutatableCode.getInstructionIndex(oldTargetInsn);
+
+ int newTargetIdx = oldTargetInsnIdx;
+
+ int delta = 0;
+
+ // Keep searching for a new index.
+ while (newTargetIdx == oldTargetInsnIdx) {
+ // Vary by +/- 2 instructions.
+ delta = 0;
+ while (delta == 0) {
+ delta = (rng.nextInt(5) - 2);
+ }
+
+ newTargetIdx = oldTargetInsnIdx + delta;
+
+ // Check the new index is legal
+ if (newTargetIdx < 0) {
+ newTargetIdx = 0;
+ } else if (newTargetIdx >= mutatableCode.getInstructionCount()) {
+ newTargetIdx = mutatableCode.getInstructionCount() - 1;
+ }
+ }
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.switchInsnIdx = switchInsnIdx;
+ mutation.switchTargetIdx = switchTargetIdx;
+ mutation.newTargetIdx = newTargetIdx;
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ generateCachedSwitchInsns(mutatableCode);
+
+ MSwitchInsn switchInsn = switchInsns.get(mutation.switchInsnIdx);
+
+ // Get the new target.
+ MInsn newTargetInsn =
+ mutatableCode.getInstructionAt(mutation.newTargetIdx);
+
+ // Set the new target.
+ switchInsn.targets.remove(mutation.switchTargetIdx);
+ switchInsn.targets.add(mutation.switchTargetIdx, newTargetInsn);
+
+ Log.info("Shifted target #" + mutation.switchTargetIdx + " of " + switchInsn
+ + " to point to " + newTargetInsn);
+
+ stats.incrementStat("Shifted switch target");
+
+ // Clear cache.
+ switchInsns = null;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/TryBlockShifter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/TryBlockShifter.java
new file mode 100644
index 0000000..1bf6463
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/TryBlockShifter.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MTryBlock;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Random;
+
+public class TryBlockShifter extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int tryIdx;
+ public boolean shiftingTryBlock; // false => shifting handler
+ public boolean shiftingStart; // false => shifting end (try block only)
+ public boolean shiftingHandlerCatchall;
+ public int shiftingHandlerIdx;
+ public int newShiftedInsnIdx;
+
+ @Override
+ public String getString() {
+ String result = String.format("%d %s %s %s %d %d",
+ tryIdx,
+ (shiftingTryBlock ? "T" : "F"),
+ (shiftingStart ? "T" : "F"),
+ (shiftingHandlerCatchall ? "T" : "F"),
+ shiftingHandlerIdx,
+ newShiftedInsnIdx
+ );
+ return result;
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ tryIdx = Integer.parseInt(elements[2]);
+ shiftingTryBlock = elements[3].equals("T");
+ shiftingStart = elements[4].equals("T");
+ shiftingHandlerCatchall = elements[5].equals("T");
+ shiftingHandlerIdx = Integer.parseInt(elements[6]);
+ newShiftedInsnIdx = Integer.parseInt(elements[7]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public TryBlockShifter() { }
+
+ public TryBlockShifter(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 40;
+ }
+
+ @Override
+ protected boolean canMutate(MutatableCode mutatableCode) {
+ if (mutatableCode.triesSize > 0) {
+ return true;
+ }
+
+ Log.debug("Method contains no tries.");
+ return false;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ // Pick a random try.
+ int tryIdx = rng.nextInt(mutatableCode.triesSize);
+ MTryBlock tryBlock = mutatableCode.mutatableTries.get(tryIdx);
+
+ boolean shiftingTryBlock = rng.nextBoolean();
+ boolean shiftingStart = false;
+ boolean shiftingHandlerCatchall = false;
+ int shiftingHandlerIdx = -1;
+ if (shiftingTryBlock) {
+ // We're shifting the boundaries of the try block
+ // determine if we shift the start or the end.
+ shiftingStart = rng.nextBoolean();
+ } else {
+ // We're shifting the start of a handler of the try block.
+ if (tryBlock.handlers.isEmpty()) {
+ // No handlers, so we MUST mutate the catchall
+ shiftingHandlerCatchall = true;
+ } else if (tryBlock.catchAllHandler != null) {
+ // There is a catchall handler, so potentially mutate it.
+ shiftingHandlerCatchall = rng.nextBoolean();
+ }
+ // If we're not going to shift the catchall handler, then
+ // pick an explicit handler to shift.
+ if (!shiftingHandlerCatchall) {
+ shiftingHandlerIdx = rng.nextInt(tryBlock.handlers.size());
+ }
+ }
+
+ // Get the original instruction wherever we're shifting.
+ MInsn oldInsn = null;
+ if (shiftingTryBlock && shiftingStart) {
+ oldInsn = tryBlock.startInsn;
+ } else if (shiftingTryBlock && !(shiftingStart)) {
+ oldInsn = tryBlock.endInsn;
+ } else if (!(shiftingTryBlock) && shiftingHandlerCatchall) {
+ oldInsn = tryBlock.catchAllHandler;
+ } else if (!(shiftingTryBlock) && !(shiftingHandlerCatchall)
+ && (shiftingHandlerIdx != -1)) {
+ oldInsn = tryBlock.handlers.get(shiftingHandlerIdx);
+ } else {
+ Log.errorAndQuit("Faulty logic in TryBlockShifter!");
+ }
+
+ // Find the index of this instruction.
+ int oldInsnIdx = mutatableCode.getInstructionIndex(oldInsn);
+
+ int newInsnIdx = oldInsnIdx;
+
+ int delta = 0;
+
+ // Keep searching for a new index.
+ while (newInsnIdx == oldInsnIdx) {
+ // Vary by +/- 2 instructions.
+ delta = 0;
+ while (delta == 0) {
+ delta = (rng.nextInt(5) - 2);
+ }
+
+ newInsnIdx = oldInsnIdx + delta;
+
+ // Check the new index is legal.
+ if (newInsnIdx < 0) {
+ newInsnIdx = 0;
+ } else if (newInsnIdx >= mutatableCode.getInstructionCount()) {
+ newInsnIdx = mutatableCode.getInstructionCount() - 1;
+ }
+ }
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.tryIdx = tryIdx;
+ mutation.shiftingTryBlock = shiftingTryBlock;
+ mutation.shiftingStart = shiftingStart;
+ mutation.shiftingHandlerCatchall = shiftingHandlerCatchall;
+ mutation.shiftingHandlerIdx = shiftingHandlerIdx;
+ mutation.newShiftedInsnIdx = newInsnIdx;
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ MTryBlock tryBlock = mutatableCode.mutatableTries.get(mutation.tryIdx);
+
+ MInsn newInsn =
+ mutatableCode.getInstructionAt(mutation.newShiftedInsnIdx);
+
+ // Find the right mutatable instruction in try block, and point it at the new instruction.
+ if (mutation.shiftingTryBlock && mutation.shiftingStart) {
+ tryBlock.startInsn = newInsn;
+ Log.info("Shifted the start of try block #" + mutation.tryIdx
+ + " to be at " + newInsn);
+ } else if (mutation.shiftingTryBlock && !(mutation.shiftingStart)) {
+ tryBlock.endInsn = newInsn;
+ Log.info("Shifted the end of try block #" + mutation.tryIdx
+ + " to be at " + newInsn);
+ } else if (!(mutation.shiftingTryBlock) && mutation.shiftingHandlerCatchall) {
+ tryBlock.catchAllHandler = newInsn;
+ Log.info("Shifted the catch all handler of try block #" + mutation.tryIdx
+ + " to be at " + newInsn);
+ } else if (!(mutation.shiftingTryBlock) && !(mutation.shiftingHandlerCatchall)
+ && (mutation.shiftingHandlerIdx != -1)) {
+ tryBlock.handlers.set(mutation.shiftingHandlerIdx, newInsn);
+ Log.info("Shifted handler #" + mutation.shiftingHandlerIdx
+ + " of try block #" + mutation.tryIdx + " to be at " + newInsn);
+ } else {
+ Log.errorAndQuit("faulty logic in TryBlockShifter");
+ }
+
+ stats.incrementStat("Shifted boundary in a try block");
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/VRegChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/VRegChanger.java
new file mode 100644
index 0000000..d685f7d
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/VRegChanger.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.formats.ContainsVRegs;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class VRegChanger extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int vregInsnIdx;
+ public int mutatingVreg;
+ public int newVregValue;
+
+ @Override
+ public String getString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(vregInsnIdx).append(" ");
+ builder.append(mutatingVreg).append(" ");
+ builder.append(newVregValue);
+ return builder.toString();
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ vregInsnIdx = Integer.parseInt(elements[2]);
+ mutatingVreg = Integer.parseInt(elements[3]);
+ newVregValue = Integer.parseInt(elements[4]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public VRegChanger() { }
+
+ public VRegChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 60;
+ }
+
+ // A cache that should only exist between generateMutation() and applyMutation(),
+ // or be created at the start of applyMutation(), if we're reading in mutations from
+ // a file.
+ private List<MInsn> vregInsns = null;
+
+ private void generateCachedVRegInsns(MutatableCode mutatableCode) {
+ if (vregInsns != null) {
+ return;
+ }
+
+ vregInsns = new ArrayList<MInsn>();
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (mInsn.insn.info.format instanceof ContainsVRegs) {
+ vregInsns.add(mInsn);
+ }
+ }
+ }
+
+ @Override
+ protected boolean canMutate(MutatableCode mutatableCode) {
+ if (mutatableCode.registersSize < 2) {
+ Log.debug("Impossible to change vregs in a method with fewer than 2 registers.");
+ return false;
+ }
+
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (mInsn.insn.info.format instanceof ContainsVRegs) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ generateCachedVRegInsns(mutatableCode);
+
+ // Pick a random vreg instruction.
+ int vregInsnIdx = rng.nextInt(vregInsns.size());
+ MInsn vregInsn = vregInsns.get(vregInsnIdx);
+
+ // Get the number of VRegs this instruction uses.
+ int numVregs = ((ContainsVRegs)vregInsn.insn.info.format).getVRegCount();
+
+ // Pick which vreg to mutate.
+ int mutatingVreg = rng.nextInt(numVregs);
+
+ // Find the old index.
+ int oldVregValue = 0;
+
+ switch (mutatingVreg) {
+ case 0:
+ oldVregValue = (int) vregInsn.insn.vregA;
+ break;
+ case 1:
+ oldVregValue = (int) vregInsn.insn.vregB;
+ break;
+ case 2:
+ oldVregValue = (int) vregInsn.insn.vregC;
+ break;
+ default:
+ Log.errorAndQuit("Invalid number of vregs reported by a Format.");
+ }
+
+ // Search for a new vreg value.
+ int newVregValue = oldVregValue;
+ while (newVregValue == oldVregValue) {
+ newVregValue = rng.nextInt(mutatableCode.registersSize);
+ }
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.vregInsnIdx = vregInsnIdx;
+ mutation.mutatingVreg = mutatingVreg;
+ mutation.newVregValue = newVregValue;
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ generateCachedVRegInsns(mutatableCode);
+
+ MInsn vregInsn = vregInsns.get(mutation.vregInsnIdx);
+
+ // Remember what the instruction used to look like.
+ String oldInsnString = vregInsn.toString();
+
+ int oldVregValue = 0;
+
+ String vregId = "A";
+ switch (mutation.mutatingVreg) {
+ case 0:
+ oldVregValue = (int) vregInsn.insn.vregA;
+ vregInsn.insn.vregA = (long) mutation.newVregValue;
+ break;
+ case 1:
+ oldVregValue = (int) vregInsn.insn.vregB;
+ vregInsn.insn.vregB = (long) mutation.newVregValue;
+ vregId = "B";
+ break;
+ case 2:
+ oldVregValue = (int) vregInsn.insn.vregC;
+ vregInsn.insn.vregC = (long) mutation.newVregValue;
+ vregId = "C";
+ break;
+ default:
+ Log.errorAndQuit("Invalid number of vregs specified in a VRegChanger mutation.");
+ }
+
+ Log.info("In " + oldInsnString + " changed v" + vregId + ": v" + oldVregValue
+ + " to v" + mutation.newVregValue);
+
+ stats.incrementStat("Changed a virtual register");
+
+ // Clear cache.
+ vregInsns = null;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/ValuePrinter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/ValuePrinter.java
new file mode 100644
index 0000000..271aca3
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/ValuePrinter.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.List;
+import java.util.Random;
+
+public class ValuePrinter extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int printedOutputIdx;
+
+ @Override
+ public String getString() {
+ return Integer.toString(printedOutputIdx);
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ printedOutputIdx = Integer.parseInt(elements[2]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public ValuePrinter() { }
+
+ public ValuePrinter(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 40;
+ }
+
+ @Override
+ protected boolean canMutate(MutatableCode mutatableCode) {
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (getInstructionOutputType(mInsn) != OutputType.UNKNOWN) {
+ return true;
+ }
+ }
+
+ Log.debug("No instructions with legible output in method, skipping.");
+ return false;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ // Find an instruction whose output we wish to print.
+ int printedOutputIdx = 0;
+ boolean foundInsn = false;
+
+ while (!foundInsn) {
+ printedOutputIdx = rng.nextInt(mutatableCode.getInstructionCount());
+ MInsn insnOutputToPrint =
+ mutatableCode.getInstructionAt(printedOutputIdx);
+ foundInsn = true;
+
+ // Don't want to insert instructions where there are raw instructions for now.
+ if (insnOutputToPrint.insn.justRaw) {
+ foundInsn = false;
+ }
+
+ if (getInstructionOutputType(insnOutputToPrint) == OutputType.UNKNOWN) {
+ foundInsn = false;
+ }
+ }
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.printedOutputIdx = printedOutputIdx;
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ MInsn insnOutputToPrint =
+ mutatableCode.getInstructionAt(mutation.printedOutputIdx);
+
+ int outFieldIdx = mutatableCode.program.getNewItemCreator().findOrCreateFieldId(
+ "Ljava/lang/System;",
+ "Ljava/io/PrintStream;",
+ "out");
+
+ OutputType outputType = getInstructionOutputType(insnOutputToPrint);
+
+ if (outputType == OutputType.UNKNOWN) {
+ Log.errorAndQuit("Requested to print output of an instruction, whose output"
+ + " type is unknown.");
+ }
+ int printMethodIdx = mutatableCode.program.getNewItemCreator().findOrCreateMethodId(
+ "Ljava/io/PrintStream;",
+ "print",
+ outputType.getSignatureForPrintln());
+
+ boolean isWide = false;
+ boolean isRef = false;
+ if (outputType == OutputType.LONG || outputType == OutputType.DOUBLE) {
+ isWide = true;
+ }
+ if (outputType == OutputType.STRING) {
+ isRef = true;
+ }
+
+ // If we're printing a wide value, we need to allocate 3 registers!
+ if (isWide) {
+ mutatableCode.allocateTemporaryVRegs(3);
+ } else {
+ mutatableCode.allocateTemporaryVRegs(2);
+ }
+
+ int streamRegister = mutatableCode.getTemporaryVReg(0);
+ int valueRegister = mutatableCode.getTemporaryVReg(1);
+
+ // Copy the value we want to print to the 2nd temporary register
+ // Then load the out stream
+ // Then call print(out stream, value)
+
+ MInsn valueCopyInsn = new MInsn();
+ valueCopyInsn.insn = new Instruction();
+ if (isRef) {
+ valueCopyInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_OBJECT_16);
+ } else if (isWide) {
+ valueCopyInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_WIDE_16);
+ } else {
+ valueCopyInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_16);
+ }
+ valueCopyInsn.insn.vregB = insnOutputToPrint.insn.vregA;
+ valueCopyInsn.insn.vregA = valueRegister;
+
+ MInsn streamLoadInsn = new MInsn();
+ streamLoadInsn.insn = new Instruction();
+ streamLoadInsn.insn.info = Instruction.getOpcodeInfo(Opcode.SGET_OBJECT);
+ streamLoadInsn.insn.vregB = outFieldIdx;
+ streamLoadInsn.insn.vregA = streamRegister;
+
+ MInsn invokeInsn = new MInsn();
+ invokeInsn.insn = new Instruction();
+ invokeInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_VIRTUAL_RANGE);
+ if (isWide) {
+ invokeInsn.insn.vregA = 3;
+ } else {
+ invokeInsn.insn.vregA = 2;
+ }
+ invokeInsn.insn.vregB = printMethodIdx;
+ invokeInsn.insn.vregC = streamRegister;
+
+ Log.info(String.format("Printing output value of instruction %s", insnOutputToPrint));
+
+ stats.incrementStat("Printed output value");
+
+ mutatableCode.insertInstructionAfter(invokeInsn, mutation.printedOutputIdx);
+ mutatableCode.insertInstructionAfter(streamLoadInsn, mutation.printedOutputIdx);
+ mutatableCode.insertInstructionAfter(valueCopyInsn, mutation.printedOutputIdx);
+
+ mutatableCode.finishedUsingTemporaryVRegs();
+ }
+
+ private static enum OutputType {
+ STRING("(Ljava/lang/String;)V"),
+ BOOLEAN("(Z)V"),
+ BYTE("(B)V"),
+ CHAR("(C)V"),
+ SHORT("(S)V"),
+ INT("(I)V"),
+ LONG("(J)V"),
+ FLOAT("(F)V"),
+ DOUBLE("(D)V"),
+ UNKNOWN("UNKNOWN");
+
+ private String printingSignature;
+ private OutputType(String s) {
+ printingSignature = s;
+ }
+
+ public String getSignatureForPrintln() {
+ return printingSignature;
+ }
+ }
+
+ private OutputType getInstructionOutputType(MInsn mInsn) {
+ Opcode opcode = mInsn.insn.info.opcode;
+ if (opcode == Opcode.CONST_STRING || opcode == Opcode.CONST_STRING_JUMBO) {
+ return OutputType.STRING;
+ }
+ if (opcode == Opcode.IGET_BOOLEAN || opcode == Opcode.SGET_BOOLEAN) {
+ return OutputType.BOOLEAN;
+ }
+ if (opcode == Opcode.IGET_BYTE || opcode == Opcode.SGET_BYTE
+ || opcode == Opcode.INT_TO_BYTE) {
+ return OutputType.BYTE;
+ }
+ if (opcode == Opcode.IGET_CHAR || opcode == Opcode.SGET_CHAR
+ || opcode == Opcode.INT_TO_CHAR) {
+ return OutputType.CHAR;
+ }
+ if (opcode == Opcode.IGET_SHORT || opcode == Opcode.SGET_SHORT
+ || opcode == Opcode.INT_TO_SHORT) {
+ return OutputType.SHORT;
+ }
+ if (opcode == Opcode.NEG_INT || opcode == Opcode.NOT_INT
+ || opcode == Opcode.LONG_TO_INT || opcode == Opcode.FLOAT_TO_INT
+ || opcode == Opcode.DOUBLE_TO_INT
+ || Opcode.isBetween(opcode, Opcode.ADD_INT, Opcode.USHR_INT)
+ || Opcode.isBetween(opcode, Opcode.ADD_INT_2ADDR, Opcode.USHR_INT_2ADDR)
+ || Opcode.isBetween(opcode, Opcode.ADD_INT_LIT16, Opcode.USHR_INT_LIT8)) {
+ return OutputType.INT;
+ }
+ if (opcode == Opcode.NEG_LONG || opcode == Opcode.NOT_LONG
+ || opcode == Opcode.INT_TO_LONG || opcode == Opcode.FLOAT_TO_LONG
+ || opcode == Opcode.DOUBLE_TO_LONG
+ || Opcode.isBetween(opcode, Opcode.ADD_LONG, Opcode.USHR_LONG)
+ || Opcode.isBetween(opcode, Opcode.ADD_LONG_2ADDR, Opcode.USHR_LONG_2ADDR)) {
+ return OutputType.LONG;
+ }
+ if (opcode == Opcode.NEG_FLOAT
+ || opcode == Opcode.INT_TO_FLOAT || opcode == Opcode.LONG_TO_FLOAT
+ || opcode == Opcode.DOUBLE_TO_FLOAT
+ || Opcode.isBetween(opcode, Opcode.ADD_FLOAT, Opcode.REM_FLOAT)
+ || Opcode.isBetween(opcode, Opcode.ADD_FLOAT_2ADDR, Opcode.REM_FLOAT_2ADDR)) {
+ return OutputType.FLOAT;
+ }
+ if (opcode == Opcode.NEG_DOUBLE
+ || opcode == Opcode.INT_TO_DOUBLE || opcode == Opcode.LONG_TO_DOUBLE
+ || opcode == Opcode.FLOAT_TO_DOUBLE
+ || Opcode.isBetween(opcode, Opcode.ADD_DOUBLE, Opcode.REM_DOUBLE)
+ || Opcode.isBetween(opcode, Opcode.ADD_DOUBLE_2ADDR, Opcode.REM_DOUBLE_2ADDR)) {
+ return OutputType.DOUBLE;
+ }
+ return OutputType.UNKNOWN;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationElement.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationElement.java
new file mode 100644
index 0000000..6bb2f96
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationElement.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationElement implements RawDexObject {
+ public int nameIdx;
+ public EncodedValue value;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ nameIdx = file.readUleb128();
+ (value = new EncodedValue()).read(file);
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.writeUleb128(nameIdx);
+ value.write(file);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (kind == IndexUpdateKind.STRING_ID && nameIdx >= insertedIdx) {
+ nameIdx++;
+ }
+ value.incrementIndex(kind, insertedIdx);
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationItem.java
new file mode 100644
index 0000000..40cf5e4
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationItem.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationItem implements RawDexObject {
+ public int visibility;
+ public EncodedAnnotation annotation;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().getNewOffsettable(file, this);
+ visibility = file.readUnsignedByte();
+ (annotation = new EncodedAnnotation()).read(file);
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ file.writeByte(visibility);
+ annotation.write(file);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ annotation.incrementIndex(kind, insertedIdx);
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationOffItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationOffItem.java
new file mode 100644
index 0000000..b44cd18
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationOffItem.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationOffItem implements RawDexObject {
+ public Offset annotationOff;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ annotationOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().tryToWriteOffset(annotationOff, file, false /* ULEB128 */);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ // Do nothing.
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetItem.java
new file mode 100644
index 0000000..1e1c540
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetItem.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationSetItem implements RawDexObject {
+ public int size;
+ public AnnotationOffItem[] entries;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ file.alignForwards(4);
+ file.getOffsetTracker().getNewOffsettable(file, this);
+ size = file.readUInt();
+ entries = new AnnotationOffItem[size];
+ for (int i = 0; i < size; i++) {
+ (entries[i] = new AnnotationOffItem()).read(file);
+ }
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.alignForwards(4);
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ file.writeUInt(size);
+ for (AnnotationOffItem annotationOffItem : entries) {
+ annotationOffItem.write(file);
+ }
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ // Do nothing.
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefItem.java
new file mode 100644
index 0000000..cb543de
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefItem.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationSetRefItem implements RawDexObject {
+ public Offset annotationsOff;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ annotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().tryToWriteOffset(annotationsOff, file, false /* ULEB128 */);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ // Do nothing.
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefList.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefList.java
new file mode 100644
index 0000000..1d27053
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefList.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationSetRefList implements RawDexObject {
+ public int size;
+ public AnnotationSetRefItem[] list;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ file.alignForwards(4);
+ file.getOffsetTracker().getNewOffsettable(file, this);
+ size = file.readUInt();
+ list = new AnnotationSetRefItem[size];
+ for (int i = 0; i < size; i++) {
+ (list[i] = new AnnotationSetRefItem()).read(file);
+ }
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.alignForwards(4);
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ file.writeUInt(size);
+ for (AnnotationSetRefItem annotationSetRefItem : list) {
+ annotationSetRefItem.write(file);
+ }
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ // Do nothing.
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationsDirectoryItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationsDirectoryItem.java
new file mode 100644
index 0000000..8285472
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationsDirectoryItem.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationsDirectoryItem implements RawDexObject {
+ public Offset classAnnotationsOff;
+ public int fieldsSize;
+ public int annotatedMethodsSize;
+ public int annotatedParametersSize;
+ public FieldAnnotation[] fieldAnnotations;
+ public MethodAnnotation[] methodAnnotations;
+ public ParameterAnnotation[] parameterAnnotations;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ file.alignForwards(4);
+ file.getOffsetTracker().getNewOffsettable(file, this);
+ classAnnotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ fieldsSize = file.readUInt();
+ annotatedMethodsSize = file.readUInt();
+ annotatedParametersSize = file.readUInt();
+ if (fieldsSize != 0) {
+ fieldAnnotations = new FieldAnnotation[fieldsSize];
+ for (int i = 0; i < fieldsSize; i++) {
+ (fieldAnnotations[i] = new FieldAnnotation()).read(file);
+ }
+ }
+ if (annotatedMethodsSize != 0) {
+ methodAnnotations = new MethodAnnotation[annotatedMethodsSize];
+ for (int i = 0; i < annotatedMethodsSize; i++) {
+ (methodAnnotations[i] = new MethodAnnotation()).read(file);
+ }
+ }
+ if (annotatedParametersSize != 0) {
+ parameterAnnotations = new ParameterAnnotation[annotatedParametersSize];
+ for (int i = 0; i < annotatedParametersSize; i++) {
+ (parameterAnnotations[i] = new ParameterAnnotation()).read(file);
+ }
+ }
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.alignForwards(4);
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ file.getOffsetTracker().tryToWriteOffset(classAnnotationsOff, file, false /* ULEB128 */);
+ file.writeUInt(fieldsSize);
+ file.writeUInt(annotatedMethodsSize);
+ file.writeUInt(annotatedParametersSize);
+ if (fieldAnnotations != null) {
+ for (FieldAnnotation fieldAnnotation : fieldAnnotations) {
+ fieldAnnotation.write(file);
+ }
+ }
+ if (methodAnnotations != null) {
+ for (MethodAnnotation methodAnnotation : methodAnnotations) {
+ methodAnnotation.write(file);
+ }
+ }
+ if (parameterAnnotations != null) {
+ for (ParameterAnnotation parameterAnnotation : parameterAnnotations) {
+ parameterAnnotation.write(file);
+ }
+ }
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (fieldAnnotations != null) {
+ for (FieldAnnotation fieldAnnotation : fieldAnnotations) {
+ fieldAnnotation.incrementIndex(kind, insertedIdx);
+ }
+ }
+ if (methodAnnotations != null) {
+ for (MethodAnnotation methodAnnotation : methodAnnotations) {
+ methodAnnotation.incrementIndex(kind, insertedIdx);
+ }
+ }
+ if (parameterAnnotations != null) {
+ for (ParameterAnnotation parameterAnnotation : parameterAnnotations) {
+ parameterAnnotation.incrementIndex(kind, insertedIdx);
+ }
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/ClassDataItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/ClassDataItem.java
new file mode 100644
index 0000000..79564bc
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/ClassDataItem.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class ClassDataItem implements RawDexObject {
+ public int staticFieldsSize;
+ public int instanceFieldsSize;
+ public int directMethodsSize;
+ public int virtualMethodsSize;
+
+ public EncodedField[] staticFields;
+ public EncodedField[] instanceFields;
+ public EncodedMethod[] directMethods;
+ public EncodedMethod[] virtualMethods;
+
+ public static class MetaInfo {
+ public ClassDefItem classDefItem;
+ }
+
+ public MetaInfo meta = new MetaInfo();
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().getNewOffsettable(file, this);
+ staticFieldsSize = file.readUleb128();
+ instanceFieldsSize = file.readUleb128();
+ directMethodsSize = file.readUleb128();
+ virtualMethodsSize = file.readUleb128();
+
+ staticFields = new EncodedField[staticFieldsSize];
+ for (int i = 0; i < staticFieldsSize; i++) {
+ (staticFields[i] = new EncodedField()).read(file);
+ }
+ instanceFields = new EncodedField[instanceFieldsSize];
+ for (int i = 0; i < instanceFieldsSize; i++) {
+ (instanceFields[i] = new EncodedField()).read(file);
+ }
+ directMethods = new EncodedMethod[directMethodsSize];
+ for (int i = 0; i < directMethodsSize; i++) {
+ (directMethods[i] = new EncodedMethod()).read(file);
+ }
+ virtualMethods = new EncodedMethod[virtualMethodsSize];
+ for (int i = 0; i < virtualMethodsSize; i++) {
+ (virtualMethods[i] = new EncodedMethod()).read(file);
+ }
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ file.writeUleb128(staticFieldsSize);
+ file.writeUleb128(instanceFieldsSize);
+ file.writeUleb128(directMethodsSize);
+ file.writeUleb128(virtualMethodsSize);
+ for (int i = 0; i < staticFieldsSize; i++) {
+ staticFields[i].write(file);
+ }
+ for (int i = 0; i < instanceFieldsSize; i++) {
+ instanceFields[i].write(file);
+ }
+ for (int i = 0; i < directMethodsSize; i++) {
+ directMethods[i].write(file);
+ }
+ for (int i = 0; i < virtualMethodsSize; i++) {
+ virtualMethods[i].write(file);
+ }
+ }
+
+ private void incrementEncodedFields(int insertedIdx, EncodedField[] fields) {
+ int fieldIdx = 0;
+ for (EncodedField field : fields) {
+ fieldIdx = field.fieldIdxDiff;
+ if (fieldIdx >= insertedIdx) {
+ field.fieldIdxDiff++;
+ // Only need to increment one, as all the others are diffed from the previous.
+ break;
+ }
+ }
+ }
+
+ private void incrementEncodedMethods(int insertedIdx, EncodedMethod[] methods) {
+ int methodIdx = 0;
+ for (EncodedMethod method : methods) {
+ methodIdx = method.methodIdxDiff;
+ if (methodIdx >= insertedIdx) {
+ method.methodIdxDiff++;
+ // Only need to increment one, as all the others are diffed from the previous.
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (kind == IndexUpdateKind.FIELD_ID) {
+ incrementEncodedFields(insertedIdx, staticFields);
+ incrementEncodedFields(insertedIdx, instanceFields);
+ }
+ if (kind == IndexUpdateKind.METHOD_ID) {
+ incrementEncodedMethods(insertedIdx, directMethods);
+ incrementEncodedMethods(insertedIdx, virtualMethods);
+ }
+ }
+
+ /**
+ * For a given field index, search this ClassDataItem for a definition of this field.
+ * @return null if the field isn't in this ClassDataItem.
+ */
+ public EncodedField getEncodedFieldWithIndex(int fieldIdx) {
+ int searchFieldIdx = 0;
+ for (EncodedField field : instanceFields) {
+ searchFieldIdx += field.fieldIdxDiff;
+ if (searchFieldIdx == fieldIdx) {
+ return field;
+ }
+ }
+ searchFieldIdx = 0;
+ for (EncodedField field : staticFields) {
+ searchFieldIdx += field.fieldIdxDiff;
+ if (searchFieldIdx == fieldIdx) {
+ return field;
+ }
+ }
+ return null;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/ClassDefItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/ClassDefItem.java
new file mode 100644
index 0000000..5e68d24
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/ClassDefItem.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class ClassDefItem implements RawDexObject {
+ public static int data_size = 0x20;
+
+ public int classIdx;
+ public int accessFlags;
+ public int superclassIdx;
+ public Offset interfacesOff;
+ public int sourceFileIdx;
+ public Offset annotationsOff;
+ public Offset classDataOff;
+ public Offset staticValuesOff;
+
+ public static class MetaInfo {
+ public ClassDataItem classDataItem;
+ }
+
+ public MetaInfo meta = new MetaInfo();
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().getNewOffsettable(file, this);
+ classIdx = file.readUInt();
+ accessFlags = file.readUInt();
+ superclassIdx = file.readUInt();
+ interfacesOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ sourceFileIdx = file.readUInt();
+ annotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ classDataOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ staticValuesOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ file.writeUInt(classIdx);
+ file.writeUInt(accessFlags);
+ file.writeUInt(superclassIdx);
+ file.getOffsetTracker().tryToWriteOffset(interfacesOff, file, false /* ULEB128 */);
+ file.writeUInt(sourceFileIdx);
+ file.getOffsetTracker().tryToWriteOffset(annotationsOff, file, false /* ULEB128 */);
+ file.getOffsetTracker().tryToWriteOffset(classDataOff, file, false /* ULEB128 */);
+ file.getOffsetTracker().tryToWriteOffset(staticValuesOff, file, false /* ULEB128 */);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (kind == IndexUpdateKind.TYPE_ID && classIdx >= insertedIdx) {
+ classIdx++;
+ }
+ if (kind == IndexUpdateKind.TYPE_ID && superclassIdx >= insertedIdx) {
+ superclassIdx++;
+ }
+ if (kind == IndexUpdateKind.STRING_ID && sourceFileIdx >= insertedIdx) {
+ sourceFileIdx++;
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/CodeItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/CodeItem.java
new file mode 100644
index 0000000..af3c377
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/CodeItem.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+import dexfuzz.program.MutatableCode;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+public class CodeItem implements RawDexObject {
+ public short registersSize;
+ public short insSize;
+ public short outsSize;
+ public short triesSize;
+ public int debugInfoOff; // NB: this is a special case
+ public int insnsSize;
+ public List<Instruction> insns;
+ public TryItem[] tries;
+ public EncodedCatchHandlerList handlers;
+
+ private MutatableCode mutatableCode;
+
+ public static class MethodMetaInfo {
+ public String methodName;
+ public boolean isStatic;
+ public String shorty;
+ }
+
+ public MethodMetaInfo meta = new MethodMetaInfo();
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ file.alignForwards(4);
+ file.getOffsetTracker().getNewOffsettable(file, this);
+ registersSize = file.readUShort();
+ insSize = file.readUShort();
+ outsSize = file.readUShort();
+ triesSize = file.readUShort();
+ debugInfoOff = file.readUInt();
+ insnsSize = file.readUInt();
+ populateInstructionList(file);
+ if (triesSize > 0) {
+ if ((insnsSize % 2) != 0) {
+ // Consume padding.
+ file.readUShort();
+ }
+ tries = new TryItem[triesSize];
+ for (int i = 0; i < triesSize; i++) {
+ (tries[i] = new TryItem()).read(file);
+ }
+ (handlers = new EncodedCatchHandlerList()).read(file);
+ }
+ }
+
+ private void populateInstructionList(DexRandomAccessFile file) throws IOException {
+ insns = new LinkedList<Instruction>();
+ long insnsOffset = file.getFilePointer();
+ if (insnsOffset != 0) {
+ long finger = insnsOffset;
+ long insnsEnd = insnsOffset + (2 * insnsSize);
+
+ while (finger < insnsEnd) {
+ file.seek(finger);
+ Instruction newInsn = new Instruction();
+ newInsn.read(file);
+ insns.add(newInsn);
+ finger += (2 * newInsn.getSize());
+ }
+
+ file.seek(finger);
+ }
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.alignForwards(4);
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ file.writeUShort(registersSize);
+ file.writeUShort(insSize);
+ file.writeUShort(outsSize);
+ file.writeUShort(triesSize);
+ // We do not support retaining debug info currently.
+ file.writeUInt(0 /*debug_info_off*/);
+ file.writeUInt(insnsSize);
+ for (Instruction insn : insns) {
+ insn.write(file);
+ }
+ if (triesSize > 0) {
+ if ((insnsSize % 2) != 0) {
+ // produce padding
+ file.writeUShort((short) 0);
+ }
+ for (TryItem tryItem : tries) {
+ tryItem.write(file);
+ }
+ handlers.write(file);
+ }
+ }
+
+ /**
+ * CodeTranslator should call this to notify a CodeItem about its
+ * mutatable code, so it can always get the "latest" view of its
+ * instructions.
+ */
+ public void registerMutatableCode(MutatableCode mutatableCode) {
+ this.mutatableCode = mutatableCode;
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (kind == IndexUpdateKind.TYPE_ID && triesSize > 0) {
+ // EncodedCatchHandlerList (well, the EncodedTypeAddrPairs it owns)
+ // are only interested in TYPE_IDs.
+ handlers.incrementIndex(kind, insertedIdx);
+ }
+
+ if (kind == IndexUpdateKind.PROTO_ID) {
+ // The only kind we can't encounter in an instruction.
+ return;
+ }
+
+ List<Instruction> insnsToIncrement = insns;
+
+ // If we have an associated MutatableCode, then it may have created some new insns
+ // that we won't know about yet, during the mutation phase.
+ //
+ // Ask for the latest view of the insns.
+ if (mutatableCode != null) {
+ insnsToIncrement = mutatableCode.requestLatestInstructions();
+ }
+
+ for (Instruction insn : insnsToIncrement) {
+ Opcode opcode = insn.info.opcode;
+ switch (kind) {
+ case STRING_ID:
+ if (opcode == Opcode.CONST_STRING || opcode == Opcode.CONST_STRING_JUMBO) {
+ // STRING@BBBB
+ if (insn.vregB >= insertedIdx) {
+ insn.vregB++;
+ }
+ }
+ break;
+ case TYPE_ID:
+ if (opcode == Opcode.CONST_CLASS
+ || opcode == Opcode.CHECK_CAST
+ || opcode == Opcode.NEW_INSTANCE
+ || opcode == Opcode.FILLED_NEW_ARRAY
+ || opcode == Opcode.FILLED_NEW_ARRAY_RANGE) {
+ // TYPE@BBBB
+ if (insn.vregB >= insertedIdx) {
+ insn.vregB++;
+ }
+ } else if (opcode == Opcode.INSTANCE_OF || opcode == Opcode.NEW_ARRAY) {
+ // TYPE@CCCC
+ if (insn.vregC >= insertedIdx) {
+ insn.vregC++;
+ }
+ }
+ break;
+ case FIELD_ID:
+ if (Opcode.isBetween(opcode, Opcode.SGET, Opcode.SPUT_SHORT)) {
+ // FIELD@BBBB
+ if (insn.vregB >= insertedIdx) {
+ insn.vregB++;
+ }
+ } else if (Opcode.isBetween(opcode, Opcode.IGET, Opcode.IPUT_SHORT)) {
+ // FIELD@CCCC
+ if (insn.vregC >= insertedIdx) {
+ insn.vregC++;
+ }
+ }
+ break;
+ case METHOD_ID:
+ if (Opcode.isBetween(opcode, Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_INTERFACE)
+ || Opcode.isBetween(opcode,
+ Opcode.INVOKE_VIRTUAL_RANGE, Opcode.INVOKE_INTERFACE_RANGE)) {
+ // METHOD@BBBB
+ if (insn.vregB >= insertedIdx) {
+ insn.vregB++;
+ }
+ }
+ break;
+ default:
+ Log.errorAndQuit("Unexpected IndexUpdateKind requested "
+ + "in Instruction.incrementIndex()");
+ }
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/DebugInfoItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/DebugInfoItem.java
new file mode 100644
index 0000000..922ee58
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/DebugInfoItem.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+// Right now we are not parsing debug_info_item, just take the raw size
+public class DebugInfoItem implements RawDexObject {
+ private int size;
+ private byte[] data;
+
+ public DebugInfoItem(int size) {
+ this.size = size;
+ }
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().getNewOffsettable(file, this);
+ data = new byte[size];
+ file.read(data);
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ file.write(data);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ // Do nothing.
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/DexRandomAccessFile.java b/tools/dexfuzz/src/dexfuzz/rawdex/DexRandomAccessFile.java
new file mode 100644
index 0000000..ec75585
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/DexRandomAccessFile.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/**
+ * An extension to RandomAccessFile that allows reading/writing
+ * DEX files in little-endian form, the variable-length LEB format
+ * and also provides word-alignment functions.
+ */
+public class DexRandomAccessFile extends RandomAccessFile {
+ private OffsetTracker offsetTracker;
+
+ public OffsetTracker getOffsetTracker() {
+ return offsetTracker;
+ }
+
+ public void setOffsetTracker(OffsetTracker offsetTracker) {
+ this.offsetTracker = offsetTracker;
+ }
+
+ /**
+ * Constructor, passes straight on to RandomAccessFile currently.
+ * @param filename The file to open.
+ * @param mode Strings "r" or "rw" work best.
+ */
+ public DexRandomAccessFile(String filename, String mode)
+ throws FileNotFoundException {
+ super(filename, mode);
+ }
+
+ /**
+ * @return A 16-bit number, read from the file as little-endian.
+ */
+ public short readUShort() throws IOException {
+ int b1 = readUnsignedByte();
+ int b2 = readUnsignedByte();
+ return (short) ((b2 << 8) | b1);
+ }
+
+ /**
+ * @param value A 16-bit number to be written to the file in little-endian.
+ */
+ public void writeUShort(short value) throws IOException {
+ int b1 = value & 0xff;
+ int b2 = (value & 0xff00) >> 8;
+ writeByte(b1);
+ writeByte(b2);
+ }
+
+ /**
+ * @return A 32-bit number, read from the file as little-endian.
+ */
+ public int readUInt() throws IOException {
+ int b1 = readUnsignedByte();
+ int b2 = readUnsignedByte();
+ int b3 = readUnsignedByte();
+ int b4 = readUnsignedByte();
+ return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
+ }
+
+ /**
+ * @param value A 32-bit number to be written to the file in little-endian.
+ */
+ public void writeUInt(int value) throws IOException {
+ int b1 = value & 0xff;
+ writeByte(b1);
+ int b2 = (value & 0xff00) >> 8;
+ writeByte(b2);
+ int b3 = (value & 0xff0000) >> 16;
+ writeByte(b3);
+ int b4 = (value & 0xff000000) >> 24;
+ writeByte(b4);
+ }
+
+ /**
+ * @return An up to 32-bit number, read from the file in ULEB128 form.
+ */
+ public int readUleb128() throws IOException {
+ int shift = 0;
+ int value = 0;
+ int rawByte = readUnsignedByte();
+ boolean done = false;
+ while (!done) {
+ // Get the lower seven bits.
+ // 0x7f = 0111 1111
+ value |= ((rawByte & 0x7f) << shift);
+ shift += 7;
+ // Check the 8th bit - if it's 0, we're done.
+ // 0x80 = 1000 0000
+ if ((rawByte & 0x80) == 0) {
+ done = true;
+ } else {
+ rawByte = readUnsignedByte();
+ }
+ }
+ return value;
+ }
+
+ /**
+ * @param value A 32-bit number to be written to the file in ULEB128 form.
+ */
+ public void writeUleb128(int value) throws IOException {
+ if (value == 0) {
+ writeByte(0);
+ return;
+ }
+
+ while (value != 0) {
+ int marker = 1;
+ // If we're down to the last 7 bits, the marker will be 0.
+ if ((value & 0xffffff80) == 0) {
+ marker = 0;
+ }
+ // Get the lowest 7 bits, add on the marker in the high bit.
+ int nextByte = value & 0x7f | (marker << 7);
+ writeByte(nextByte);
+ value >>>= 7;
+ }
+ }
+
+ /**
+ * Write out ULEB128 value always using 5 bytes.
+ * A version of ULEB128 that will always write out 5 bytes, because this
+ * value will be patched later, and if we used a smaller encoding, the new value
+ * may overflow the previously selected encoding size.
+ * The largest encoding for 0 in ULEB128 would be:
+ * 0x80 0x80 0x80 0x80 0x00
+ * and for 1 would be:
+ * 0x81 0x80 0x80 0x80 0x00
+ */
+ public void writeLargestUleb128(int value) throws IOException {
+ Log.debug("Writing " + value + " using the largest possible ULEB128 encoding.");
+ if (value == 0) {
+ writeByte(0x80);
+ writeByte(0x80);
+ writeByte(0x80);
+ writeByte(0x80);
+ writeByte(0x0);
+ return;
+ }
+
+ for (int i = 0; i < 5; i++) {
+ int marker = 1;
+ // If we're writing the 5th byte, the marker is 0.
+ if (i == 4) {
+ marker = 0;
+ }
+ // Get the lowest 7 bits, add on the marker in the high bit.
+ int nextByte = value & 0x7f | (marker << 7);
+ writeByte(nextByte);
+ value >>>= 7;
+ }
+ }
+
+ /**
+ * @return An up to 32-bit number, read from the file in SLEB128 form.
+ */
+ public int readSleb128() throws IOException {
+ int shift = 0;
+ int value = 0;
+ int rawByte = readUnsignedByte();
+ boolean done = false;
+ boolean mustSignExtend = false;
+ while (!done) {
+ // Get the lower seven bits.
+ // 0x7f = 0111 1111
+ value |= ((rawByte & 0x7f) << shift);
+ shift += 7;
+ // Check the 8th bit - if it's 0, we're done.
+ // 0x80 = 1000 0000
+ if ((rawByte & 0x80) == 0) {
+ // Check the 7th bit - if it's a 1, we need to sign extend.
+ if ((rawByte & 0x60) != 0) {
+ mustSignExtend = true;
+ }
+ done = true;
+ } else {
+ rawByte = readUnsignedByte();
+ }
+ }
+ if (mustSignExtend) {
+ // Example:
+ // say we shifted 7 bits, we need
+ // to make all the upper 25 bits 1s.
+ // load a 1...
+ // 00000000 00000000 00000000 00000001
+ // << 7
+ // 00000000 00000000 00000000 10000000
+ // - 1
+ // 00000000 00000000 00000000 01111111
+ // ~
+ // 11111111 11111111 11111111 10000000
+ int upperOnes = ~((1 << shift) - 1);
+ value |= (upperOnes);
+ }
+ return value;
+ }
+
+ /**
+ * @param value A 32-bit number to be written to the file in SLEB128 form.
+ */
+ public void writeSleb128(int value) throws IOException {
+ if (value == 0) {
+ writeByte(0);
+ return;
+ }
+ if (value > 0) {
+ writeUleb128(value);
+ return;
+ }
+ if (value == -1) {
+ writeByte(0x7f);
+ }
+
+ // When it's all 1s (0xffffffff), we're done!
+ while (value != 0xffffffff) {
+ int marker = 1;
+ // If we're down to the last 7 bits (i.e., shifting a further 7 is all 1s),
+ // the marker will be 0.
+ if ((value >> 7) == 0xffffffff) {
+ marker = 0;
+ }
+ // Get the lowest 7 bits, add on the marker in the high bit.
+ int nextByte = value & 0x7f | (marker << 7);
+ writeByte(nextByte);
+ value >>= 7;
+ }
+ }
+
+ /**
+ * In DEX format, strings are in MUTF-8 format, the first ULEB128 value is the decoded size
+ * (i.e., string.length), and then follows a null-terminated series of characters.
+ * @param decodedSize The ULEB128 value that should have been read just before this.
+ * @return The raw bytes of the string, not including the null character.
+ */
+ public byte[] readDexUtf(int decodedSize) throws IOException {
+ // In the dex MUTF-8, the encoded size can never be larger than 3 times
+ // the actual string's length (which is the ULEB128 value just before this
+ // string, the "decoded size")
+
+ // Therefore, allocate as much space as we might need.
+ byte[] str = new byte[decodedSize * 3];
+
+ // Get our first byte.
+ int encodedSize = 0;
+ byte rawByte = readByte();
+
+ // Keep reading until we find the end marker.
+ while (rawByte != 0) {
+ str[encodedSize++] = rawByte;
+ rawByte = readByte();
+ }
+
+ // Copy everything we read into str into the correctly-sized actual string.
+ byte[] actualString = new byte[encodedSize];
+ for (int i = 0; i < encodedSize; i++) {
+ actualString[i] = str[i];
+ }
+
+ return actualString;
+ }
+
+ /**
+ * Writes out raw bytes that would have been read by readDexUTF().
+ * Will automatically write out the null-byte at the end.
+ * @param data Bytes to be written out.
+ */
+ public void writeDexUtf(byte[] data) throws IOException {
+ write(data);
+ // Remember to add the end marker.
+ writeByte(0);
+ }
+
+ /**
+ * Align the file handle's seek pointer to the next N bytes.
+ * @param alignment N to align to.
+ */
+ public void alignForwards(int alignment) throws IOException {
+ long offset = getFilePointer();
+ long mask = alignment - 1;
+ if ((offset & mask) != 0) {
+ int extra = alignment - (int) (offset & mask);
+ seek(offset + extra);
+ }
+ }
+
+ /**
+ * Align the file handle's seek pointer backwards to the previous N bytes.
+ * @param alignment N to align to.
+ */
+ public void alignBackwards(int alignment) throws IOException {
+ long offset = getFilePointer();
+ long mask = alignment - 1;
+ if ((offset & mask) != 0) {
+ offset &= (~mask);
+ seek(offset);
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedAnnotation.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedAnnotation.java
new file mode 100644
index 0000000..085a4a8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedAnnotation.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedAnnotation implements RawDexObject {
+ public int typeIdx;
+ public int size;
+ public AnnotationElement[] elements;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ typeIdx = file.readUleb128();
+ size = file.readUleb128();
+ if (size != 0) {
+ elements = new AnnotationElement[size];
+ for (int i = 0; i < size; i++) {
+ (elements[i] = new AnnotationElement()).read(file);
+ }
+ }
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.writeUleb128(typeIdx);
+ file.writeUleb128(size);
+ if (size != 0) {
+ for (AnnotationElement annotationElement : elements) {
+ annotationElement.write(file);
+ }
+ }
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (kind == IndexUpdateKind.TYPE_ID && typeIdx >= insertedIdx) {
+ typeIdx++;
+ }
+ if (size != 0) {
+ for (AnnotationElement element : elements) {
+ element.incrementIndex(kind, insertedIdx);
+ }
+ }
+ }
+
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArray.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArray.java
new file mode 100644
index 0000000..267ff09
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArray.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedArray implements RawDexObject {
+ public int size;
+ public EncodedValue[] values;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ size = file.readUleb128();
+ if (size != 0) {
+ values = new EncodedValue[size];
+ for (int i = 0; i < size; i++) {
+ (values[i] = new EncodedValue()).read(file);
+ }
+ }
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.writeUleb128(size);
+ if (size != 0) {
+ for (EncodedValue encodedValue : values) {
+ encodedValue.write(file);
+ }
+ }
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (size != 0) {
+ for (EncodedValue value : values) {
+ value.incrementIndex(kind, insertedIdx);
+ }
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArrayItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArrayItem.java
new file mode 100644
index 0000000..e461a54
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArrayItem.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedArrayItem implements RawDexObject {
+ public EncodedArray value;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().getNewOffsettable(file, this);
+ (value = new EncodedArray()).read(file);
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ value.write(file);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ // Do nothing.
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandler.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandler.java
new file mode 100644
index 0000000..83786c8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandler.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedCatchHandler implements RawDexObject {
+ public int size;
+ public EncodedTypeAddrPair[] handlers;
+ public int catchAllAddr;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ size = file.readSleb128();
+ int absoluteSize = Math.abs(size);
+ if (absoluteSize > 0) {
+ handlers = new EncodedTypeAddrPair[absoluteSize];
+ for (int i = 0; i < Math.abs(size); i++) {
+ (handlers[i] = new EncodedTypeAddrPair()).read(file);
+ }
+ }
+ if (size <= 0) {
+ catchAllAddr = file.readUleb128();
+ }
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.writeSleb128(size);
+ if (handlers != null) {
+ for (EncodedTypeAddrPair encodedTypeAddrPair : handlers) {
+ encodedTypeAddrPair.write(file);
+ }
+ }
+ if (size <= 0) {
+ file.writeUleb128(catchAllAddr);
+ }
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (handlers != null) {
+ for (EncodedTypeAddrPair handler : handlers) {
+ handler.incrementIndex(kind, insertedIdx);
+ }
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandlerList.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandlerList.java
new file mode 100644
index 0000000..5619b5f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandlerList.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedCatchHandlerList implements RawDexObject {
+ public int size;
+ public EncodedCatchHandler[] list;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ size = file.readUleb128();
+ list = new EncodedCatchHandler[size];
+ for (int i = 0; i < size; i++) {
+ (list[i] = new EncodedCatchHandler()).read(file);
+ }
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.writeUleb128(size);
+ for (EncodedCatchHandler encodedCatchHandler : list) {
+ encodedCatchHandler.write(file);
+ }
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ for (EncodedCatchHandler handler : list) {
+ handler.incrementIndex(kind, insertedIdx);
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedField.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedField.java
new file mode 100644
index 0000000..18c1d43
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedField.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedField implements RawDexObject {
+ public int fieldIdxDiff;
+ public int accessFlags;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ fieldIdxDiff = file.readUleb128();
+ accessFlags = file.readUleb128();
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.writeUleb128(fieldIdxDiff);
+ file.writeUleb128(accessFlags);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ // Do nothing.
+ // NB: our idx_diff is handled in ClassDataItem...
+ }
+
+ public boolean isVolatile() {
+ return ((accessFlags & Flag.ACC_VOLATILE.getValue()) != 0);
+ }
+
+ /**
+ * Set/unset the volatile flag for this EncodedField.
+ */
+ public void setVolatile(boolean turnOn) {
+ if (turnOn) {
+ accessFlags |= Flag.ACC_VOLATILE.getValue();
+ } else {
+ accessFlags &= ~(Flag.ACC_VOLATILE.getValue());
+ }
+ }
+
+ private static enum Flag {
+ ACC_PUBLIC(0x1),
+ ACC_PRIVATE(0x2),
+ ACC_PROTECTED(0x4),
+ ACC_STATIC(0x8),
+ ACC_FINAL(0x10),
+ ACC_VOLATILE(0x40),
+ ACC_TRANSIENT(0x80),
+ ACC_SYNTHETIC(0x1000),
+ ACC_ENUM(0x4000);
+
+ private int value;
+
+ private Flag(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedMethod.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedMethod.java
new file mode 100644
index 0000000..3a8163a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedMethod.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+
+public class EncodedMethod implements RawDexObject {
+ public int methodIdxDiff;
+ public int accessFlags;
+ public Offset codeOff;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ methodIdxDiff = file.readUleb128();
+ accessFlags = file.readUleb128();
+ codeOff = file.getOffsetTracker().getNewOffset(file.readUleb128());
+ if (isNative()) {
+ Log.errorAndQuit("Sorry, DEX files with native methods are not supported yet.");
+ }
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.writeUleb128(methodIdxDiff);
+ file.writeUleb128(accessFlags);
+ file.getOffsetTracker().tryToWriteOffset(codeOff, file, true /* ULEB128 */);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ // Do nothing.
+ // NB: our idx_diff is handled in ClassDataItem...
+ }
+
+ public boolean isStatic() {
+ return ((accessFlags & Flag.ACC_STATIC.getValue()) != 0);
+ }
+
+ public boolean isNative() {
+ return ((accessFlags & Flag.ACC_NATIVE.getValue()) != 0);
+ }
+
+ /**
+ * Set/unset the static flag for this EncodedMethod.
+ */
+ public void setStatic(boolean turnOn) {
+ if (turnOn) {
+ accessFlags |= Flag.ACC_STATIC.getValue();
+ } else {
+ accessFlags &= ~(Flag.ACC_STATIC.getValue());
+ }
+ }
+
+ private static enum Flag {
+ ACC_PUBLIC(0x1),
+ ACC_PRIVATE(0x2),
+ ACC_PROTECTED(0x4),
+ ACC_STATIC(0x8),
+ ACC_FINAL(0x10),
+ ACC_SYNCHRONIZED(0x20),
+ ACC_VARARGS(0x80),
+ ACC_NATIVE(0x100),
+ ACC_ABSTRACT(0x400),
+ ACC_STRICT(0x800),
+ ACC_SYNTHETIC(0x1000),
+ ACC_ENUM(0x4000),
+ ACC_CONSTRUCTOR(0x10000);
+
+ private int value;
+
+ private Flag(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedTypeAddrPair.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedTypeAddrPair.java
new file mode 100644
index 0000000..ea49ae1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedTypeAddrPair.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedTypeAddrPair implements RawDexObject {
+ public int typeIdx;
+ public int addr;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ typeIdx = file.readUleb128();
+ addr = file.readUleb128();
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.writeUleb128(typeIdx);
+ file.writeUleb128(addr);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (kind == IndexUpdateKind.TYPE_ID && typeIdx >= insertedIdx) {
+ typeIdx++;
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedValue.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedValue.java
new file mode 100644
index 0000000..fdf133f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedValue.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedValue implements RawDexObject {
+ public byte valueArg;
+ public byte valueType;
+ public byte[] value;
+ public EncodedArray encodedArray;
+ public EncodedAnnotation encodedAnnotation;
+
+ private static final byte VALUE_BYTE = 0x00;
+ private static final byte VALUE_ARRAY = 0x1c;
+ private static final byte VALUE_ANNOTATION = 0x1d;
+ private static final byte VALUE_NULL = 0x1e;
+ private static final byte VALUE_BOOLEAN = 0x1f;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ int valueArgAndType = file.readUnsignedByte();
+
+ // Get lower 5 bits.
+ valueType = (byte) (valueArgAndType & 0x1f);
+ // Get upper 3 bits.
+ valueArg = (byte) ((valueArgAndType & 0xe0) >> 5);
+
+ int size = 0;
+
+ switch (valueType) {
+ case VALUE_BYTE:
+ size = 1;
+ break;
+ case VALUE_ARRAY:
+ (encodedArray = new EncodedArray()).read(file);
+ size = 0; // So we don't read into value.
+ break;
+ case VALUE_ANNOTATION:
+ (encodedAnnotation = new EncodedAnnotation()).read(file);
+ size = 0; // So we don't read into value.
+ break;
+ case VALUE_NULL:
+ case VALUE_BOOLEAN:
+ // No value
+ size = 0;
+ break;
+ default:
+ // All others encode value_arg as (size - 1), so...
+ size = valueArg + 1;
+ break;
+ }
+
+ if (size != 0) {
+ value = new byte[size];
+ for (int i = 0; i < size; i++) {
+ value[i] = file.readByte();
+ }
+ }
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ int valueArgAndType = ((valueType) | (valueArg << 5));
+ file.writeByte(valueArgAndType);
+
+ if (encodedArray != null) {
+ encodedArray.write(file);
+ } else if (encodedAnnotation != null) {
+ encodedAnnotation.write(file);
+ } else if (value != null) {
+ file.write(value);
+ }
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (encodedArray != null) {
+ encodedArray.incrementIndex(kind, insertedIdx);
+ } else if (encodedAnnotation != null) {
+ encodedAnnotation.incrementIndex(kind, insertedIdx);
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/FieldAnnotation.java b/tools/dexfuzz/src/dexfuzz/rawdex/FieldAnnotation.java
new file mode 100644
index 0000000..98f812e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/FieldAnnotation.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class FieldAnnotation implements RawDexObject {
+ public int fieldIdx;
+ public Offset annotationsOff;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ fieldIdx = file.readUInt();
+ annotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.writeUInt(fieldIdx);
+ file.getOffsetTracker().tryToWriteOffset(annotationsOff, file, false /* ULEB128 */);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (kind == IndexUpdateKind.FIELD_ID && fieldIdx >= insertedIdx) {
+ fieldIdx++;
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/FieldIdItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/FieldIdItem.java
new file mode 100644
index 0000000..75f2078
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/FieldIdItem.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class FieldIdItem implements RawDexObject {
+ public short classIdx;
+ public short typeIdx;
+ public int nameIdx;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().getNewOffsettable(file, this);
+ classIdx = file.readUShort();
+ typeIdx = file.readUShort();
+ nameIdx = file.readUInt();
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ file.writeUShort(classIdx);
+ file.writeUShort(typeIdx);
+ file.writeUInt(nameIdx);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (kind == IndexUpdateKind.TYPE_ID && classIdx >= insertedIdx) {
+ classIdx++;
+ }
+ if (kind == IndexUpdateKind.TYPE_ID && typeIdx >= insertedIdx) {
+ typeIdx++;
+ }
+ if (kind == IndexUpdateKind.STRING_ID && nameIdx >= insertedIdx) {
+ nameIdx++;
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/HeaderItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/HeaderItem.java
new file mode 100644
index 0000000..e6b290c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/HeaderItem.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+
+public class HeaderItem implements RawDexObject {
+ public byte[] magic;
+ public int checksum;
+ public byte[] signature; // Verification doesn't depend on this, so we don't update it.
+ public int fileSize;
+ public int headerSize;
+ public int endianTag;
+ public int linkSize;
+ public Offset linkOff;
+ public Offset mapOff;
+ public int stringIdsSize;
+ public Offset stringIdsOff;
+ public int typeIdsSize;
+ public Offset typeIdsOff;
+ public int protoIdsSize;
+ public Offset protoIdsOff;
+ public int fieldIdsSize;
+ public Offset fieldIdsOff;
+ public int methodIdsSize;
+ public Offset methodIdsOff;
+ public int classDefsSize;
+ public Offset classDefsOff;
+ public int dataSize;
+ public Offset dataOff;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().getNewOffsettable(file, this);
+ magic = new byte[8];
+ for (int i = 0; i < 8; i++) {
+ magic[i] = file.readByte();
+ }
+ checksum = file.readUInt();
+ signature = new byte[20];
+ for (int i = 0; i < 20; i++) {
+ signature[i] = file.readByte();
+ }
+ fileSize = file.readUInt();
+ headerSize = file.readUInt();
+ endianTag = file.readUInt();
+ linkSize = file.readUInt();
+ linkOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ mapOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ stringIdsSize = file.readUInt();
+ stringIdsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ typeIdsSize = file.readUInt();
+ typeIdsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ protoIdsSize = file.readUInt();
+ protoIdsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ fieldIdsSize = file.readUInt();
+ fieldIdsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ methodIdsSize = file.readUInt();
+ methodIdsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ classDefsSize = file.readUInt();
+ classDefsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ dataSize = file.readUInt();
+ dataOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ if (headerSize != 0x70) {
+ Log.errorAndQuit("Invalid header size in header.");
+ }
+ if (file.getFilePointer() != headerSize) {
+ Log.errorAndQuit("Read a different amount than expected in header: "
+ + file.getFilePointer());
+ }
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ for (int i = 0; i < 8; i++) {
+ file.writeByte(magic[i]);
+ }
+ // Will be recalculated later!
+ file.writeUInt(checksum);
+ for (int i = 0; i < 20; i++) {
+ file.writeByte(signature[i]);
+ }
+ // Will be recalculated later!
+ file.writeUInt(fileSize);
+ file.writeUInt(headerSize);
+ file.writeUInt(endianTag);
+ file.writeUInt(linkSize);
+ file.getOffsetTracker().tryToWriteOffset(linkOff, file, false /* ULEB128 */);
+ file.getOffsetTracker().tryToWriteOffset(mapOff, file, false /* ULEB128 */);
+ file.writeUInt(stringIdsSize);
+ file.getOffsetTracker().tryToWriteOffset(stringIdsOff, file, false /* ULEB128 */);
+ file.writeUInt(typeIdsSize);
+ file.getOffsetTracker().tryToWriteOffset(typeIdsOff, file, false /* ULEB128 */);
+ file.writeUInt(protoIdsSize);
+ file.getOffsetTracker().tryToWriteOffset(protoIdsOff, file, false /* ULEB128 */);
+ file.writeUInt(fieldIdsSize);
+ file.getOffsetTracker().tryToWriteOffset(fieldIdsOff, file, false /* ULEB128 */);
+ file.writeUInt(methodIdsSize);
+ file.getOffsetTracker().tryToWriteOffset(methodIdsOff, file, false /* ULEB128 */);
+ file.writeUInt(classDefsSize);
+ file.getOffsetTracker().tryToWriteOffset(classDefsOff, file, false /* ULEB128 */);
+ // will be recalculated later
+ file.writeUInt(dataSize);
+ file.getOffsetTracker().tryToWriteOffset(dataOff, file, false /* ULEB128 */);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ // Do nothing
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/Instruction.java b/tools/dexfuzz/src/dexfuzz/rawdex/Instruction.java
new file mode 100644
index 0000000..2dda78f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/Instruction.java
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+import dexfuzz.rawdex.formats.AbstractFormat;
+import dexfuzz.rawdex.formats.ContainsConst;
+import dexfuzz.rawdex.formats.ContainsPoolIndex;
+import dexfuzz.rawdex.formats.ContainsTarget;
+import dexfuzz.rawdex.formats.ContainsVRegs;
+import dexfuzz.rawdex.formats.Format10t;
+import dexfuzz.rawdex.formats.Format10x;
+import dexfuzz.rawdex.formats.Format11n;
+import dexfuzz.rawdex.formats.Format11x;
+import dexfuzz.rawdex.formats.Format12x;
+import dexfuzz.rawdex.formats.Format20t;
+import dexfuzz.rawdex.formats.Format21c;
+import dexfuzz.rawdex.formats.Format21h;
+import dexfuzz.rawdex.formats.Format21s;
+import dexfuzz.rawdex.formats.Format21t;
+import dexfuzz.rawdex.formats.Format22b;
+import dexfuzz.rawdex.formats.Format22c;
+import dexfuzz.rawdex.formats.Format22s;
+import dexfuzz.rawdex.formats.Format22t;
+import dexfuzz.rawdex.formats.Format22x;
+import dexfuzz.rawdex.formats.Format23x;
+import dexfuzz.rawdex.formats.Format30t;
+import dexfuzz.rawdex.formats.Format31c;
+import dexfuzz.rawdex.formats.Format31i;
+import dexfuzz.rawdex.formats.Format31t;
+import dexfuzz.rawdex.formats.Format32x;
+import dexfuzz.rawdex.formats.Format35c;
+import dexfuzz.rawdex.formats.Format3rc;
+import dexfuzz.rawdex.formats.Format51l;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Instruction implements RawDexObject {
+ // Only used by Format35* instructions
+ public static class InvokeFormatInfo {
+ public byte vregD;
+ public byte vregE;
+ public byte vregF;
+ public byte vregG;
+ }
+
+ // Immutable information about this class of instruction.
+ public OpcodeInfo info;
+
+ // The raw bytes of the instruction.
+ // Only used during reading, and writing out is done from the decoded instruction data.
+ // Except in the case of the 3 "data" instructions.
+ public byte[] rawBytes;
+
+ public static final int RAW_TYPE_PACKED_SWITCH_DATA = 1;
+ public static final int RAW_TYPE_SPARSE_SWITCH_DATA = 2;
+ public static final int RAW_TYPE_FILL_ARRAY_DATA_DATA = 3;
+
+ public int rawType;
+ public boolean justRaw;
+ public int rawSize;
+
+ public long vregA = 0;
+ public long vregB = 0;
+ public long vregC = 0;
+
+ public InvokeFormatInfo invokeFormatInfo;
+
+ /**
+ * Clone an instruction.
+ */
+ public Instruction clone() {
+ Instruction newInsn = new Instruction();
+ // If we've generated a new instruction, we won't have calculated its raw array.
+ if (newInsn.rawBytes != null) {
+ newInsn.rawBytes = new byte[rawBytes.length];
+ for (int i = 0; i < rawBytes.length; i++) {
+ newInsn.rawBytes[i] = rawBytes[i];
+ }
+ }
+ newInsn.justRaw = justRaw;
+ newInsn.rawType = rawType;
+ newInsn.rawSize = rawSize;
+
+ newInsn.vregA = vregA;
+ newInsn.vregB = vregB;
+ newInsn.vregC = vregC;
+ newInsn.info = info;
+ if (invokeFormatInfo != null) {
+ newInsn.invokeFormatInfo = new InvokeFormatInfo();
+ newInsn.invokeFormatInfo.vregD = invokeFormatInfo.vregD;
+ newInsn.invokeFormatInfo.vregE = invokeFormatInfo.vregE;
+ newInsn.invokeFormatInfo.vregF = invokeFormatInfo.vregF;
+ newInsn.invokeFormatInfo.vregG = invokeFormatInfo.vregG;
+ }
+ return newInsn;
+ }
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ // Remember the offset, so after reading the opcode, we can read the whole
+ // insn into raw_bytes.
+ long offset = file.getFilePointer();
+ int opcodeValue = readOpcode(file);
+ info = getOpcodeInfo(opcodeValue);
+ if (info == null) {
+ Log.errorAndQuit("Couldn't find OpcodeInfo for opcode with value: "
+ + opcodeValue);
+ }
+
+ rawBytes = new byte[2 * getSize()];
+ file.seek(offset);
+ file.read(rawBytes);
+
+ vregA = info.format.getA(rawBytes);
+ vregB = info.format.getB(rawBytes);
+ vregC = info.format.getC(rawBytes);
+
+ // Special case for 35* formats.
+ if (info.format.needsInvokeFormatInfo()) {
+ invokeFormatInfo = new InvokeFormatInfo();
+ invokeFormatInfo.vregD = (byte) (rawBytes[4] >> 4);
+ invokeFormatInfo.vregE = (byte) (rawBytes[5] & 0xf);
+ invokeFormatInfo.vregF = (byte) (rawBytes[5] >> 4);
+ invokeFormatInfo.vregG = (byte) (rawBytes[1] & 0xf);
+ }
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ if (justRaw) {
+ // It is the responsibility of the CodeTranslator to make
+ // sure the raw bytes have been updated.
+ file.write(rawBytes);
+ } else {
+ info.format.writeToFile(file, this);
+ }
+ }
+
+ /**
+ * Get the size of an instruction, in code-words. (Code-words are 16-bits.)
+ */
+ public int getSize() {
+ if (justRaw) {
+ // It is the responsibility of the CodeTranslator to make sure
+ // the raw size has been updated.
+ return rawSize;
+ }
+ return info.format.getSize();
+ }
+
+ private int readOpcode(DexRandomAccessFile file) throws IOException {
+ short firstCodeWord = file.readUShort();
+ int opcode = (firstCodeWord & 0xff);
+ int upperBits = (firstCodeWord & 0xff00) >> 8;
+ if (opcode == 0x0 && upperBits != 0x0) {
+ justRaw = true;
+ rawType = upperBits;
+ // Need to calculate special sizes.
+ switch (rawType) {
+ case RAW_TYPE_PACKED_SWITCH_DATA:
+ rawSize = (file.readUShort() * 2) + 4;
+ break;
+ case RAW_TYPE_SPARSE_SWITCH_DATA:
+ rawSize = (file.readUShort() * 4) + 2;
+ break;
+ case RAW_TYPE_FILL_ARRAY_DATA_DATA:
+ {
+ int elementWidth = file.readUShort();
+ rawSize = ((file.readUInt() * elementWidth + 1) / 2) + 4;
+ break;
+ }
+ default:
+ Log.errorAndQuit("Unrecognised ident in data-payload instruction: " + rawType);
+ }
+ }
+ return opcode;
+ }
+
+ @Override
+ public String toString() {
+ if (justRaw) {
+ switch (rawType) {
+ case RAW_TYPE_PACKED_SWITCH_DATA:
+ return "PACKED SWITCH DATA";
+ case RAW_TYPE_SPARSE_SWITCH_DATA:
+ return "SPARSE SWITCH DATA";
+ case RAW_TYPE_FILL_ARRAY_DATA_DATA:
+ return "FILL ARRAY DATA DATA";
+ default:
+ }
+
+ }
+
+ String repr = info.name;
+
+ AbstractFormat format = info.format;
+
+ if (invokeFormatInfo != null) {
+ String vregs = "";
+
+ int numVregs = (int) vregA;
+
+ if (numVregs > 5) {
+ Log.debug("vA in an 35c invoke was greater than 5? Assuming 5.");
+ numVregs = 5;
+ } else if (numVregs < 0) {
+ Log.debug("vA in an 35c invoke was less than 0? Assuming 0.");
+ numVregs = 0;
+ }
+
+ switch (numVregs) {
+ case 5:
+ vregs = ", v" + invokeFormatInfo.vregG;
+ // fallthrough
+ case 4:
+ vregs = ", v" + invokeFormatInfo.vregF + vregs;
+ // fallthrough
+ case 3:
+ vregs = ", v" + invokeFormatInfo.vregE + vregs;
+ // fallthrough
+ case 2:
+ vregs = ", v" + invokeFormatInfo.vregD + vregs;
+ // fallthrough
+ case 1:
+ vregs = "v" + vregC + vregs;
+ break;
+ default:
+ }
+
+ repr += "(" + vregs + ")";
+
+ long poolIndex = ((ContainsPoolIndex)format).getPoolIndex(this);
+ repr += " meth@" + poolIndex;
+
+ return repr;
+ }
+
+
+
+ if (format instanceof ContainsVRegs) {
+ String vregs = "";
+ switch (((ContainsVRegs)format).getVRegCount()) {
+ case 3:
+ vregs = ", v" + vregC;
+ // fallthrough
+ case 2:
+ vregs = ", v" + vregB + vregs;
+ // fallthrough
+ case 1:
+ vregs = "v" + vregA + vregs;
+ break;
+ default:
+ Log.errorAndQuit("Invalid number of vregs reported by a Format.");
+ }
+
+ repr += " " + vregs;
+ }
+ if (format instanceof ContainsConst) {
+ long constant = ((ContainsConst)format).getConst(this);
+ repr += " #" + constant;
+ }
+ if (format instanceof ContainsPoolIndex) {
+ long poolIndex = ((ContainsPoolIndex)format).getPoolIndex(this);
+ repr += " pool@" + poolIndex;
+ }
+ if (format instanceof ContainsTarget) {
+ long target = ((ContainsTarget)format).getTarget(this);
+ repr += " +" + target;
+ }
+
+ return repr;
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ // Do nothing.
+ }
+
+ // STATIC INSTRUCTION CODE
+ private static Map<Integer,OpcodeInfo> opcode_map_by_int = new HashMap<Integer,OpcodeInfo>();
+ private static Map<Opcode,OpcodeInfo> opcode_map_by_enum = new HashMap<Opcode,OpcodeInfo>();
+
+ public static OpcodeInfo getOpcodeInfo(Opcode opcode) {
+ return opcode_map_by_enum.get(opcode);
+ }
+
+ public static OpcodeInfo getOpcodeInfo(int opcodeValue) {
+ return opcode_map_by_int.get(opcodeValue);
+ }
+
+ private static void addOpcodeInfo(Opcode opcode, String name,
+ int opcodeValue, AbstractFormat fmt) {
+ OpcodeInfo info = new OpcodeInfo(opcode, name, opcodeValue, fmt);
+ if (opcode.ordinal() != opcodeValue) {
+ Log.errorAndQuit(String.format("Opcode: %s (enum ordinal 0x%x) != (value 0x%x)",
+ opcode.toString(), opcode.ordinal(), opcodeValue));
+ }
+ opcode_map_by_int.put(opcodeValue, info);
+ opcode_map_by_enum.put(opcode, info);
+ }
+
+ static {
+ addOpcodeInfo(Opcode.NOP, "nop", 0x00, new Format10x());
+ addOpcodeInfo(Opcode.MOVE, "move", 0x01, new Format12x());
+ addOpcodeInfo(Opcode.MOVE_FROM16, "move/from16", 0x02, new Format22x());
+ addOpcodeInfo(Opcode.MOVE_16, "move/16", 0x03, new Format32x());
+ addOpcodeInfo(Opcode.MOVE_WIDE, "move-wide", 0x04, new Format12x());
+ addOpcodeInfo(Opcode.MOVE_WIDE_FROM16, "move-wide/from16", 0x05, new Format22x());
+ addOpcodeInfo(Opcode.MOVE_WIDE_16, "move-wide/16", 0x06, new Format32x());
+ addOpcodeInfo(Opcode.MOVE_OBJECT, "move-object", 0x07, new Format12x());
+ addOpcodeInfo(Opcode.MOVE_OBJECT_FROM16, "move-object/from16", 0x08, new Format22x());
+ addOpcodeInfo(Opcode.MOVE_OBJECT_16, "move-object/16", 0x09, new Format32x());
+ addOpcodeInfo(Opcode.MOVE_RESULT, "move-result", 0x0a, new Format11x());
+ addOpcodeInfo(Opcode.MOVE_RESULT_WIDE, "move-result-wide", 0x0b, new Format11x());
+ addOpcodeInfo(Opcode.MOVE_RESULT_OBJECT, "move-result-object", 0x0c, new Format11x());
+ addOpcodeInfo(Opcode.MOVE_EXCEPTION, "move-exception", 0x0d, new Format11x());
+ addOpcodeInfo(Opcode.RETURN_VOID, "return-void", 0x0e, new Format10x());
+ addOpcodeInfo(Opcode.RETURN, "return", 0x0f, new Format11x());
+ addOpcodeInfo(Opcode.RETURN_WIDE, "return-wide", 0x10, new Format11x());
+ addOpcodeInfo(Opcode.RETURN_OBJECT, "return-object", 0x11, new Format11x());
+ addOpcodeInfo(Opcode.CONST_4, "const/4", 0x12, new Format11n());
+ addOpcodeInfo(Opcode.CONST_16, "const/16", 0x13, new Format21s());
+ addOpcodeInfo(Opcode.CONST, "const", 0x14, new Format31i());
+ addOpcodeInfo(Opcode.CONST_HIGH16, "const/high16", 0x15, new Format21h());
+ addOpcodeInfo(Opcode.CONST_WIDE_16, "const-wide/16", 0x16, new Format21s());
+ addOpcodeInfo(Opcode.CONST_WIDE_32, "const-wide/32", 0x17, new Format31i());
+ addOpcodeInfo(Opcode.CONST_WIDE, "const-wide", 0x18, new Format51l());
+ addOpcodeInfo(Opcode.CONST_WIDE_HIGH16, "const-wide/high16", 0x19, new Format21h());
+ addOpcodeInfo(Opcode.CONST_STRING, "const-string", 0x1a, new Format21c());
+ addOpcodeInfo(Opcode.CONST_STRING_JUMBO, "const-string/jumbo", 0x1b, new Format31c());
+ addOpcodeInfo(Opcode.CONST_CLASS, "const-class", 0x1c, new Format21c());
+ addOpcodeInfo(Opcode.MONITOR_ENTER, "monitor-enter", 0x1d, new Format11x());
+ addOpcodeInfo(Opcode.MONITOR_EXIT, "monitor-exit", 0x1e, new Format11x());
+ addOpcodeInfo(Opcode.CHECK_CAST, "check-cast", 0x1f, new Format21c());
+ addOpcodeInfo(Opcode.INSTANCE_OF, "instance-of", 0x20, new Format22c());
+ addOpcodeInfo(Opcode.ARRAY_LENGTH, "array-length", 0x21, new Format12x());
+ addOpcodeInfo(Opcode.NEW_INSTANCE, "new-instance", 0x22, new Format21c());
+ addOpcodeInfo(Opcode.NEW_ARRAY, "new-array", 0x23, new Format22c());
+ addOpcodeInfo(Opcode.FILLED_NEW_ARRAY, "filled-new-array", 0x24, new Format35c());
+ addOpcodeInfo(Opcode.FILLED_NEW_ARRAY_RANGE, "filled-new-array/range",
+ 0x25, new Format3rc());
+ addOpcodeInfo(Opcode.FILL_ARRAY_DATA, "fill-array-data", 0x26, new Format31t());
+ addOpcodeInfo(Opcode.THROW, "throw", 0x27, new Format11x());
+ addOpcodeInfo(Opcode.GOTO, "goto", 0x28, new Format10t());
+ addOpcodeInfo(Opcode.GOTO_16, "goto/16", 0x29, new Format20t());
+ addOpcodeInfo(Opcode.GOTO_32, "goto/32", 0x2a, new Format30t());
+ addOpcodeInfo(Opcode.PACKED_SWITCH, "packed-switch", 0x2b, new Format31t());
+ addOpcodeInfo(Opcode.SPARSE_SWITCH, "sparse-switch", 0x2c, new Format31t());
+ addOpcodeInfo(Opcode.CMPL_FLOAT, "cmpl-float", 0x2d, new Format23x());
+ addOpcodeInfo(Opcode.CMPG_FLOAT, "cmpg-float", 0x2e, new Format23x());
+ addOpcodeInfo(Opcode.CMPL_DOUBLE, "cmpl-double", 0x2f, new Format23x());
+ addOpcodeInfo(Opcode.CMPG_DOUBLE, "cmpg-double", 0x30, new Format23x());
+ addOpcodeInfo(Opcode.CMP_LONG, "cmp-long", 0x31, new Format23x());
+ addOpcodeInfo(Opcode.IF_EQ, "if-eq", 0x32, new Format22t());
+ addOpcodeInfo(Opcode.IF_NE, "if-ne", 0x33, new Format22t());
+ addOpcodeInfo(Opcode.IF_LT, "if-lt", 0x34, new Format22t());
+ addOpcodeInfo(Opcode.IF_GE, "if-ge", 0x35, new Format22t());
+ addOpcodeInfo(Opcode.IF_GT, "if-gt", 0x36, new Format22t());
+ addOpcodeInfo(Opcode.IF_LE, "if-le", 0x37, new Format22t());
+ addOpcodeInfo(Opcode.IF_EQZ, "if-eqz", 0x38, new Format21t());
+ addOpcodeInfo(Opcode.IF_NEZ, "if-nez", 0x39, new Format21t());
+ addOpcodeInfo(Opcode.IF_LTZ, "if-ltz", 0x3a, new Format21t());
+ addOpcodeInfo(Opcode.IF_GEZ, "if-gez", 0x3b, new Format21t());
+ addOpcodeInfo(Opcode.IF_GTZ, "if-gtz", 0x3c, new Format21t());
+ addOpcodeInfo(Opcode.IF_LEZ, "if-lez", 0x3d, new Format21t());
+ addOpcodeInfo(Opcode.UNUSED_3E, "unused-3e", 0x3e, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_3F, "unused-3f", 0x3f, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_40, "unused-40", 0x40, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_41, "unused-41", 0x41, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_42, "unused-42", 0x42, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_43, "unused-43", 0x43, new Format10x());
+ addOpcodeInfo(Opcode.AGET, "aget", 0x44, new Format23x());
+ addOpcodeInfo(Opcode.AGET_WIDE, "aget-wide", 0x45, new Format23x());
+ addOpcodeInfo(Opcode.AGET_WIDE, "aget-wide", 0x45, new Format23x());
+ addOpcodeInfo(Opcode.AGET_OBJECT, "aget-object", 0x46, new Format23x());
+ addOpcodeInfo(Opcode.AGET_BOOLEAN, "aget-boolean", 0x47, new Format23x());
+ addOpcodeInfo(Opcode.AGET_BYTE, "aget-byte", 0x48, new Format23x());
+ addOpcodeInfo(Opcode.AGET_CHAR, "aget-char", 0x49, new Format23x());
+ addOpcodeInfo(Opcode.AGET_SHORT, "aget-short", 0x4a, new Format23x());
+ addOpcodeInfo(Opcode.APUT, "aput", 0x4b, new Format23x());
+ addOpcodeInfo(Opcode.APUT_WIDE, "aput-wide", 0x4c, new Format23x());
+ addOpcodeInfo(Opcode.APUT_OBJECT, "aput-object", 0x4d, new Format23x());
+ addOpcodeInfo(Opcode.APUT_BOOLEAN, "aput-boolean", 0x4e, new Format23x());
+ addOpcodeInfo(Opcode.APUT_BYTE, "aput-byte", 0x4f, new Format23x());
+ addOpcodeInfo(Opcode.APUT_CHAR, "aput-char", 0x50, new Format23x());
+ addOpcodeInfo(Opcode.APUT_SHORT, "aput-short", 0x51, new Format23x());
+ addOpcodeInfo(Opcode.IGET, "iget", 0x52, new Format22c());
+ addOpcodeInfo(Opcode.IGET_WIDE, "iget-wide", 0x53, new Format22c());
+ addOpcodeInfo(Opcode.IGET_OBJECT, "iget-object", 0x54, new Format22c());
+ addOpcodeInfo(Opcode.IGET_BOOLEAN, "iget-boolean", 0x55, new Format22c());
+ addOpcodeInfo(Opcode.IGET_BYTE, "iget-byte", 0x56, new Format22c());
+ addOpcodeInfo(Opcode.IGET_CHAR, "iget-char", 0x57, new Format22c());
+ addOpcodeInfo(Opcode.IGET_SHORT, "iget-short", 0x58, new Format22c());
+ addOpcodeInfo(Opcode.IPUT, "iput", 0x59, new Format22c());
+ addOpcodeInfo(Opcode.IPUT_WIDE, "iput-wide", 0x5a, new Format22c());
+ addOpcodeInfo(Opcode.IPUT_OBJECT, "iput-object", 0x5b, new Format22c());
+ addOpcodeInfo(Opcode.IPUT_BOOLEAN, "iput-boolean", 0x5c, new Format22c());
+ addOpcodeInfo(Opcode.IPUT_BYTE, "iput-byte", 0x5d, new Format22c());
+ addOpcodeInfo(Opcode.IPUT_CHAR, "iput-char", 0x5e, new Format22c());
+ addOpcodeInfo(Opcode.IPUT_SHORT, "iput-short", 0x5f, new Format22c());
+ addOpcodeInfo(Opcode.SGET, "sget", 0x60, new Format21c());
+ addOpcodeInfo(Opcode.SGET_WIDE, "sget-wide", 0x61, new Format21c());
+ addOpcodeInfo(Opcode.SGET_OBJECT, "sget-object", 0x62, new Format21c());
+ addOpcodeInfo(Opcode.SGET_BOOLEAN, "sget-boolean", 0x63, new Format21c());
+ addOpcodeInfo(Opcode.SGET_BYTE, "sget-byte", 0x64, new Format21c());
+ addOpcodeInfo(Opcode.SGET_CHAR, "sget-char", 0x65, new Format21c());
+ addOpcodeInfo(Opcode.SGET_SHORT, "sget-short", 0x66, new Format21c());
+ addOpcodeInfo(Opcode.SPUT, "sput", 0x67, new Format21c());
+ addOpcodeInfo(Opcode.SPUT_WIDE, "sput-wide", 0x68, new Format21c());
+ addOpcodeInfo(Opcode.SPUT_OBJECT, "sput-object", 0x69, new Format21c());
+ addOpcodeInfo(Opcode.SPUT_BOOLEAN, "sput-boolean", 0x6a, new Format21c());
+ addOpcodeInfo(Opcode.SPUT_BYTE, "sput-byte", 0x6b, new Format21c());
+ addOpcodeInfo(Opcode.SPUT_CHAR, "sput-char", 0x6c, new Format21c());
+ addOpcodeInfo(Opcode.SPUT_SHORT, "sput-short", 0x6d, new Format21c());
+ addOpcodeInfo(Opcode.INVOKE_VIRTUAL, "invoke-virtual", 0x6e, new Format35c());
+ addOpcodeInfo(Opcode.INVOKE_SUPER, "invoke-super", 0x6f, new Format35c());
+ addOpcodeInfo(Opcode.INVOKE_DIRECT, "invoke-direct", 0x70, new Format35c());
+ addOpcodeInfo(Opcode.INVOKE_STATIC, "invoke-static", 0x71, new Format35c());
+ addOpcodeInfo(Opcode.INVOKE_INTERFACE, "invoke-interface", 0x72, new Format35c());
+ addOpcodeInfo(Opcode.RETURN_VOID_BARRIER, "return-void-barrier", 0x73, new Format10x());
+ addOpcodeInfo(Opcode.INVOKE_VIRTUAL_RANGE, "invoke-virtual/range", 0x74, new Format3rc());
+ addOpcodeInfo(Opcode.INVOKE_SUPER_RANGE, "invoke-super/range", 0x75, new Format3rc());
+ addOpcodeInfo(Opcode.INVOKE_DIRECT_RANGE, "invoke-direct/range", 0x76, new Format3rc());
+ addOpcodeInfo(Opcode.INVOKE_STATIC_RANGE, "invoke-static/range", 0x77, new Format3rc());
+ addOpcodeInfo(Opcode.INVOKE_INTERFACE_RANGE, "invoke-interface/range",
+ 0x78, new Format3rc());
+ addOpcodeInfo(Opcode.UNUSED_79, "unused-79", 0x79, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_7A, "unused-7a", 0x7a, new Format10x());
+ addOpcodeInfo(Opcode.NEG_INT, "neg-int", 0x7b, new Format12x());
+ addOpcodeInfo(Opcode.NOT_INT, "not-int", 0x7c, new Format12x());
+ addOpcodeInfo(Opcode.NEG_LONG, "neg-long", 0x7d, new Format12x());
+ addOpcodeInfo(Opcode.NOT_LONG, "not-long", 0x7e, new Format12x());
+ addOpcodeInfo(Opcode.NEG_FLOAT, "neg-float", 0x7f, new Format12x());
+ addOpcodeInfo(Opcode.NEG_DOUBLE, "neg-double", 0x80, new Format12x());
+ addOpcodeInfo(Opcode.INT_TO_LONG, "int-to-long", 0x81, new Format12x());
+ addOpcodeInfo(Opcode.INT_TO_FLOAT, "int-to-float", 0x82, new Format12x());
+ addOpcodeInfo(Opcode.INT_TO_DOUBLE, "int-to-double", 0x83, new Format12x());
+ addOpcodeInfo(Opcode.LONG_TO_INT, "long-to-int", 0x84, new Format12x());
+ addOpcodeInfo(Opcode.LONG_TO_FLOAT, "long-to-float", 0x85, new Format12x());
+ addOpcodeInfo(Opcode.LONG_TO_DOUBLE, "long-to-double", 0x86, new Format12x());
+ addOpcodeInfo(Opcode.FLOAT_TO_INT, "float-to-int", 0x87, new Format12x());
+ addOpcodeInfo(Opcode.FLOAT_TO_LONG, "float-to-long", 0x88, new Format12x());
+ addOpcodeInfo(Opcode.FLOAT_TO_DOUBLE, "float-to-double", 0x89, new Format12x());
+ addOpcodeInfo(Opcode.DOUBLE_TO_INT, "double-to-int", 0x8a, new Format12x());
+ addOpcodeInfo(Opcode.DOUBLE_TO_LONG, "double-to-long", 0x8b, new Format12x());
+ addOpcodeInfo(Opcode.DOUBLE_TO_FLOAT, "double-to-float", 0x8c, new Format12x());
+ addOpcodeInfo(Opcode.INT_TO_BYTE, "int-to-byte", 0x8d, new Format12x());
+ addOpcodeInfo(Opcode.INT_TO_CHAR, "int-to-char", 0x8e, new Format12x());
+ addOpcodeInfo(Opcode.INT_TO_SHORT, "int-to-short", 0x8f, new Format12x());
+ addOpcodeInfo(Opcode.ADD_INT, "add-int", 0x90, new Format23x());
+ addOpcodeInfo(Opcode.SUB_INT, "sub-int", 0x91, new Format23x());
+ addOpcodeInfo(Opcode.MUL_INT, "mul-int", 0x92, new Format23x());
+ addOpcodeInfo(Opcode.DIV_INT, "div-int", 0x93, new Format23x());
+ addOpcodeInfo(Opcode.REM_INT, "rem-int", 0x94, new Format23x());
+ addOpcodeInfo(Opcode.AND_INT, "and-int", 0x95, new Format23x());
+ addOpcodeInfo(Opcode.OR_INT, "or-int", 0x96, new Format23x());
+ addOpcodeInfo(Opcode.XOR_INT, "xor-int", 0x97, new Format23x());
+ addOpcodeInfo(Opcode.SHL_INT, "shl-int", 0x98, new Format23x());
+ addOpcodeInfo(Opcode.SHR_INT, "shr-int", 0x99, new Format23x());
+ addOpcodeInfo(Opcode.USHR_INT, "ushr-int", 0x9a, new Format23x());
+ addOpcodeInfo(Opcode.ADD_LONG, "add-long", 0x9b, new Format23x());
+ addOpcodeInfo(Opcode.SUB_LONG, "sub-long", 0x9c, new Format23x());
+ addOpcodeInfo(Opcode.MUL_LONG, "mul-long", 0x9d, new Format23x());
+ addOpcodeInfo(Opcode.DIV_LONG, "div-long", 0x9e, new Format23x());
+ addOpcodeInfo(Opcode.REM_LONG, "rem-long", 0x9f, new Format23x());
+ addOpcodeInfo(Opcode.AND_LONG, "and-long", 0xa0, new Format23x());
+ addOpcodeInfo(Opcode.OR_LONG, "or-long", 0xa1, new Format23x());
+ addOpcodeInfo(Opcode.XOR_LONG, "xor-long", 0xa2, new Format23x());
+ addOpcodeInfo(Opcode.SHL_LONG, "shl-long", 0xa3, new Format23x());
+ addOpcodeInfo(Opcode.SHR_LONG, "shr-long", 0xa4, new Format23x());
+ addOpcodeInfo(Opcode.USHR_LONG, "ushr-long", 0xa5, new Format23x());
+ addOpcodeInfo(Opcode.ADD_FLOAT, "add-float", 0xa6, new Format23x());
+ addOpcodeInfo(Opcode.SUB_FLOAT, "sub-float", 0xa7, new Format23x());
+ addOpcodeInfo(Opcode.MUL_FLOAT, "mul-float", 0xa8, new Format23x());
+ addOpcodeInfo(Opcode.DIV_FLOAT, "div-float", 0xa9, new Format23x());
+ addOpcodeInfo(Opcode.REM_FLOAT, "rem-float", 0xaa, new Format23x());
+ addOpcodeInfo(Opcode.ADD_DOUBLE, "add-double", 0xab, new Format23x());
+ addOpcodeInfo(Opcode.SUB_DOUBLE, "sub-double", 0xac, new Format23x());
+ addOpcodeInfo(Opcode.MUL_DOUBLE, "mul-double", 0xad, new Format23x());
+ addOpcodeInfo(Opcode.DIV_DOUBLE, "div-double", 0xae, new Format23x());
+ addOpcodeInfo(Opcode.REM_DOUBLE, "rem-double", 0xaf, new Format23x());
+ addOpcodeInfo(Opcode.ADD_INT_2ADDR, "add-int/2addr", 0xb0, new Format12x());
+ addOpcodeInfo(Opcode.SUB_INT_2ADDR, "sub-int/2addr", 0xb1, new Format12x());
+ addOpcodeInfo(Opcode.MUL_INT_2ADDR, "mul-int/2addr", 0xb2, new Format12x());
+ addOpcodeInfo(Opcode.DIV_INT_2ADDR, "div-int/2addr", 0xb3, new Format12x());
+ addOpcodeInfo(Opcode.REM_INT_2ADDR, "rem-int/2addr", 0xb4, new Format12x());
+ addOpcodeInfo(Opcode.AND_INT_2ADDR, "and-int/2addr", 0xb5, new Format12x());
+ addOpcodeInfo(Opcode.OR_INT_2ADDR, "or-int/2addr", 0xb6, new Format12x());
+ addOpcodeInfo(Opcode.XOR_INT_2ADDR, "xor-int/2addr", 0xb7, new Format12x());
+ addOpcodeInfo(Opcode.SHL_INT_2ADDR, "shl-int/2addr", 0xb8, new Format12x());
+ addOpcodeInfo(Opcode.SHR_INT_2ADDR, "shr-int/2addr", 0xb9, new Format12x());
+ addOpcodeInfo(Opcode.USHR_INT_2ADDR, "ushr-int/2addr", 0xba, new Format12x());
+ addOpcodeInfo(Opcode.ADD_LONG_2ADDR, "add-long/2addr", 0xbb, new Format12x());
+ addOpcodeInfo(Opcode.SUB_LONG_2ADDR, "sub-long/2addr", 0xbc, new Format12x());
+ addOpcodeInfo(Opcode.MUL_LONG_2ADDR, "mul-long/2addr", 0xbd, new Format12x());
+ addOpcodeInfo(Opcode.DIV_LONG_2ADDR, "div-long/2addr", 0xbe, new Format12x());
+ addOpcodeInfo(Opcode.REM_LONG_2ADDR, "rem-long/2addr", 0xbf, new Format12x());
+ addOpcodeInfo(Opcode.AND_LONG_2ADDR, "and-long/2addr", 0xc0, new Format12x());
+ addOpcodeInfo(Opcode.OR_LONG_2ADDR, "or-long/2addr", 0xc1, new Format12x());
+ addOpcodeInfo(Opcode.XOR_LONG_2ADDR, "xor-long/2addr", 0xc2, new Format12x());
+ addOpcodeInfo(Opcode.SHL_LONG_2ADDR, "shl-long/2addr", 0xc3, new Format12x());
+ addOpcodeInfo(Opcode.SHR_LONG_2ADDR, "shr-long/2addr", 0xc4, new Format12x());
+ addOpcodeInfo(Opcode.USHR_LONG_2ADDR, "ushr-long/2addr", 0xc5, new Format12x());
+ addOpcodeInfo(Opcode.ADD_FLOAT_2ADDR, "add-float/2addr", 0xc6, new Format12x());
+ addOpcodeInfo(Opcode.SUB_FLOAT_2ADDR, "sub-float/2addr", 0xc7, new Format12x());
+ addOpcodeInfo(Opcode.MUL_FLOAT_2ADDR, "mul-float/2addr", 0xc8, new Format12x());
+ addOpcodeInfo(Opcode.DIV_FLOAT_2ADDR, "div-float/2addr", 0xc9, new Format12x());
+ addOpcodeInfo(Opcode.REM_FLOAT_2ADDR, "rem-float/2addr", 0xca, new Format12x());
+ addOpcodeInfo(Opcode.ADD_DOUBLE_2ADDR, "add-double/2addr", 0xcb, new Format12x());
+ addOpcodeInfo(Opcode.SUB_DOUBLE_2ADDR, "sub-double/2addr", 0xcc, new Format12x());
+ addOpcodeInfo(Opcode.MUL_DOUBLE_2ADDR, "mul-double/2addr", 0xcd, new Format12x());
+ addOpcodeInfo(Opcode.DIV_DOUBLE_2ADDR, "div-double/2addr", 0xce, new Format12x());
+ addOpcodeInfo(Opcode.REM_DOUBLE_2ADDR, "rem-double/2addr", 0xcf, new Format12x());
+ addOpcodeInfo(Opcode.ADD_INT_LIT16, "add-int/lit16", 0xd0, new Format22s());
+ addOpcodeInfo(Opcode.RSUB_INT, "rsub-int", 0xd1, new Format22s());
+ addOpcodeInfo(Opcode.MUL_INT_LIT16, "mul-int/lit16", 0xd2, new Format22s());
+ addOpcodeInfo(Opcode.DIV_INT_LIT16, "div-int/lit16", 0xd3, new Format22s());
+ addOpcodeInfo(Opcode.REM_INT_LIT16, "rem-int/lit16", 0xd4, new Format22s());
+ addOpcodeInfo(Opcode.AND_INT_LIT16, "and-int/lit16", 0xd5, new Format22s());
+ addOpcodeInfo(Opcode.OR_INT_LIT16, "or-int/lit16", 0xd6, new Format22s());
+ addOpcodeInfo(Opcode.XOR_INT_LIT16, "xor-int/lit16", 0xd7, new Format22s());
+ addOpcodeInfo(Opcode.ADD_INT_LIT8, "add-int/lit8", 0xd8, new Format22b());
+ addOpcodeInfo(Opcode.RSUB_INT_LIT8, "rsub-int/lit8", 0xd9, new Format22b());
+ addOpcodeInfo(Opcode.MUL_INT_LIT8, "mul-int/lit8", 0xda, new Format22b());
+ addOpcodeInfo(Opcode.DIV_INT_LIT8, "div-int/lit8", 0xdb, new Format22b());
+ addOpcodeInfo(Opcode.REM_INT_LIT8, "rem-int/lit8", 0xdc, new Format22b());
+ addOpcodeInfo(Opcode.AND_INT_LIT8, "and-int/lit8", 0xdd, new Format22b());
+ addOpcodeInfo(Opcode.OR_INT_LIT8, "or-int/lit8", 0xde, new Format22b());
+ addOpcodeInfo(Opcode.XOR_INT_LIT8, "xor-int/lit8", 0xdf, new Format22b());
+ addOpcodeInfo(Opcode.SHL_INT_LIT8, "shl-int/lit8", 0xe0, new Format22b());
+ addOpcodeInfo(Opcode.SHR_INT_LIT8, "shr-int/lit8", 0xe1, new Format22b());
+ addOpcodeInfo(Opcode.USHR_INT_LIT8, "ushr-int/lit8", 0xe2, new Format22b());
+ addOpcodeInfo(Opcode.IGET_QUICK, "+iget-quick", 0xe3, new Format22c());
+ addOpcodeInfo(Opcode.IGET_WIDE_QUICK, "+iget-wide-quick", 0xe4, new Format22c());
+ addOpcodeInfo(Opcode.IGET_OBJECT_QUICK, "+iget-object-quick", 0xe5, new Format22c());
+ addOpcodeInfo(Opcode.IPUT_QUICK, "+iput-quick", 0xe6, new Format22c());
+ addOpcodeInfo(Opcode.IPUT_WIDE_QUICK, "+iput-wide-quick", 0xe7, new Format22c());
+ addOpcodeInfo(Opcode.IPUT_OBJECT_QUICK, "+iput-object-quick", 0xe8, new Format22c());
+ addOpcodeInfo(Opcode.INVOKE_VIRTUAL_QUICK, "+invoke-virtual-quick", 0xe9, new Format35c());
+ addOpcodeInfo(Opcode.INVOKE_VIRTUAL_QUICK_RANGE, "+invoke-virtual-quick/range",
+ 0xea, new Format3rc());
+ addOpcodeInfo(Opcode.IPUT_BOOLEAN_QUICK, "+iput-boolean-quick", 0xeb, new Format22c());
+ addOpcodeInfo(Opcode.IPUT_BYTE_QUICK, "+iput-byte-quick", 0xec, new Format22c());
+ addOpcodeInfo(Opcode.IPUT_CHAR_QUICK, "+iput-char-quick", 0xed, new Format22c());
+ addOpcodeInfo(Opcode.IPUT_SHORT_QUICK, "+iput-short-quick", 0xee, new Format22c());
+ addOpcodeInfo(Opcode.UNUSED_EF, "unused-ef", 0xef, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_F0, "unused-f0", 0xf0, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_F1, "unused-f1", 0xf1, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_F2, "unused-f2", 0xf2, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_F3, "unused-f3", 0xf3, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_F4, "unused-f4", 0xf4, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_F5, "unused-f5", 0xf5, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_F6, "unused-f6", 0xf6, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_F7, "unused-f7", 0xf7, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_F8, "unused-f8", 0xf8, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_F9, "unused-f9", 0xf9, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_FA, "unused-fa", 0xfa, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_FB, "unused-fb", 0xfb, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_FC, "unused-fc", 0xfc, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_FD, "unused-fd", 0xfd, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_FE, "unused-fe", 0xfe, new Format10x());
+ addOpcodeInfo(Opcode.UNUSED_FF, "unused-ff", 0xff, new Format10x());
+ if (opcode_map_by_int.size() != 256) {
+ Log.errorAndQuit("Incorrect number of bytecodes defined.");
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/MapItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/MapItem.java
new file mode 100644
index 0000000..4ca2463
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/MapItem.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class MapItem implements RawDexObject {
+ public static final int TYPE_HEADER_ITEM = 0x0;
+ public static final int TYPE_STRING_ID_ITEM = 0x1;
+ public static final int TYPE_TYPE_ID_ITEM = 0x2;
+ public static final int TYPE_PROTO_ID_ITEM = 0x3;
+ public static final int TYPE_FIELD_ID_ITEM = 0x4;
+ public static final int TYPE_METHOD_ID_ITEM = 0x5;
+ public static final int TYPE_CLASS_DEF_ITEM = 0x6;
+ public static final int TYPE_MAP_LIST = 0x1000;
+ public static final int TYPE_TYPE_LIST = 0x1001;
+ public static final int TYPE_ANNOTATION_SET_REF_LIST = 0x1002;
+ public static final int TYPE_ANNOTATION_SET_ITEM = 0x1003;
+ public static final int TYPE_CLASS_DATA_ITEM = 0x2000;
+ public static final int TYPE_CODE_ITEM = 0x2001;
+ public static final int TYPE_STRING_DATA_ITEM = 0x2002;
+ public static final int TYPE_DEBUG_INFO_ITEM = 0x2003;
+ public static final int TYPE_ANNOTATION_ITEM = 0x2004;
+ public static final int TYPE_ENCODED_ARRAY_ITEM = 0x2005;
+ public static final int TYPE_ANNOTATIONS_DIRECTORY_ITEM = 0x2006;
+
+ public short type;
+ public int size;
+ public Offset offset;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ type = file.readUShort();
+ file.readUShort(); // Unused padding.
+ size = file.readUInt();
+ if (type == TYPE_HEADER_ITEM) {
+ offset = file.getOffsetTracker().getNewHeaderOffset(file.readUInt());
+ } else {
+ offset = file.getOffsetTracker().getNewOffset(file.readUInt());
+ }
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.writeUShort(type);
+ file.writeUShort((short) 0); // Unused padding.
+ file.writeUInt(size);
+ file.getOffsetTracker().tryToWriteOffset(offset, file, false /* ULEB128 */);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ // Do nothing.
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/MapList.java b/tools/dexfuzz/src/dexfuzz/rawdex/MapList.java
new file mode 100644
index 0000000..080b5a4
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/MapList.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MapList implements RawDexObject {
+
+ private RawDexFile rawDexFile;
+
+ public int size;
+ public List<MapItem> mapItems;
+
+ public MapList(RawDexFile rawDexFile) {
+ this.rawDexFile = rawDexFile;
+ }
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ // Find the map list.
+ file.seek(rawDexFile.header.mapOff.getOriginalOffset());
+
+ file.getOffsetTracker().getNewOffsettable(file, this);
+
+ // Get the number of entries.
+ size = file.readUInt();
+
+ // Allocate and populate the array.
+ mapItems = new ArrayList<MapItem>(size);
+ for (int i = 0; i < size; i++) {
+ MapItem mapItem = new MapItem();
+ mapItems.add(mapItem);
+ mapItem.read(file);
+ }
+
+ file.getOffsetTracker().rememberPointAfterMapList();
+
+ // NB: We track the current index into the MapList, so when we encounter the DebugInfoItem
+ // MapItem, we know how to find the next MapItem, so we know how large the DebugInfo
+ // area is, so we can copy it as a blob.
+ int mapItemIdx = 0;
+
+ // Iterate through the list, and create all the other data structures.
+ for (MapItem mapItem : mapItems) {
+ file.seek(mapItem.offset.getOriginalOffset());
+ switch (mapItem.type) {
+ case MapItem.TYPE_HEADER_ITEM:
+ // Already read it; skip.
+ break;
+ case MapItem.TYPE_STRING_ID_ITEM:
+ for (int i = 0; i < mapItem.size; i++) {
+ StringIdItem newStringId = new StringIdItem();
+ rawDexFile.stringIds.add(newStringId);
+ newStringId.read(file);
+ }
+ break;
+ case MapItem.TYPE_TYPE_ID_ITEM:
+ for (int i = 0; i < mapItem.size; i++) {
+ TypeIdItem newTypeId = new TypeIdItem();
+ rawDexFile.typeIds.add(newTypeId);
+ newTypeId.read(file);
+ }
+ break;
+ case MapItem.TYPE_PROTO_ID_ITEM:
+ for (int i = 0; i < mapItem.size; i++) {
+ ProtoIdItem newProtoId = new ProtoIdItem();
+ rawDexFile.protoIds.add(newProtoId);
+ newProtoId.read(file);
+ }
+ break;
+ case MapItem.TYPE_FIELD_ID_ITEM:
+ for (int i = 0; i < mapItem.size; i++) {
+ FieldIdItem newFieldId = new FieldIdItem();
+ rawDexFile.fieldIds.add(newFieldId);
+ newFieldId.read(file);
+ }
+ break;
+ case MapItem.TYPE_METHOD_ID_ITEM:
+ for (int i = 0; i < mapItem.size; i++) {
+ MethodIdItem newMethodId = new MethodIdItem();
+ rawDexFile.methodIds.add(newMethodId);
+ newMethodId.read(file);
+ }
+ break;
+ case MapItem.TYPE_CLASS_DEF_ITEM:
+ for (int i = 0; i < mapItem.size; i++) {
+ ClassDefItem newClassDef = new ClassDefItem();
+ rawDexFile.classDefs.add(newClassDef);
+ newClassDef.read(file);
+ }
+ break;
+ case MapItem.TYPE_MAP_LIST:
+ // Already read it; skip.
+ break;
+ case MapItem.TYPE_TYPE_LIST:
+ rawDexFile.typeLists = new ArrayList<TypeList>(mapItem.size);
+ for (int i = 0; i < mapItem.size; i++) {
+ TypeList newTypeList = new TypeList();
+ rawDexFile.typeLists.add(newTypeList);
+ newTypeList.read(file);
+ }
+ break;
+ case MapItem.TYPE_ANNOTATION_SET_REF_LIST:
+ rawDexFile.annotationSetRefLists =
+ new ArrayList<AnnotationSetRefList>(mapItem.size);
+ for (int i = 0; i < mapItem.size; i++) {
+ AnnotationSetRefList newAnnotationSetRefList = new AnnotationSetRefList();
+ rawDexFile.annotationSetRefLists.add(newAnnotationSetRefList);
+ newAnnotationSetRefList.read(file);
+ }
+ break;
+ case MapItem.TYPE_ANNOTATION_SET_ITEM:
+ rawDexFile.annotationSetItems = new ArrayList<AnnotationSetItem>(mapItem.size);
+ for (int i = 0; i < mapItem.size; i++) {
+ AnnotationSetItem newAnnotationSetItem = new AnnotationSetItem();
+ rawDexFile.annotationSetItems.add(newAnnotationSetItem);
+ newAnnotationSetItem.read(file);
+ }
+ break;
+ case MapItem.TYPE_CLASS_DATA_ITEM:
+ rawDexFile.classDatas = new ArrayList<ClassDataItem>(mapItem.size);
+ for (int i = 0; i < mapItem.size; i++) {
+ ClassDataItem newClassData = new ClassDataItem();
+ rawDexFile.classDatas.add(newClassData);
+ newClassData.read(file);
+ }
+ break;
+ case MapItem.TYPE_CODE_ITEM:
+ rawDexFile.codeItems = new ArrayList<CodeItem>(mapItem.size);
+ for (int i = 0; i < mapItem.size; i++) {
+ CodeItem newCodeItem = new CodeItem();
+ rawDexFile.codeItems.add(newCodeItem);
+ newCodeItem.read(file);
+ }
+ break;
+ case MapItem.TYPE_STRING_DATA_ITEM:
+ rawDexFile.stringDatas = new ArrayList<StringDataItem>(mapItem.size);
+ for (int i = 0; i < mapItem.size; i++) {
+ StringDataItem newStringData = new StringDataItem();
+ rawDexFile.stringDatas.add(newStringData);
+ newStringData.read(file);
+ }
+ break;
+ case MapItem.TYPE_DEBUG_INFO_ITEM:
+ {
+ // We aren't interested in updating the debug data, so just read it as a blob.
+ int start = mapItem.offset.getOriginalOffset();
+ int end = mapItems.get(mapItemIdx + 1).offset.getOriginalOffset();
+ int size = end - start;
+ rawDexFile.debugInfoItem = new DebugInfoItem(size);
+ rawDexFile.debugInfoItem.read(file);
+ break;
+ }
+ case MapItem.TYPE_ANNOTATION_ITEM:
+ rawDexFile.annotationItems = new ArrayList<AnnotationItem>(mapItem.size);
+ for (int i = 0; i < mapItem.size; i++) {
+ AnnotationItem newAnnotationItem = new AnnotationItem();
+ rawDexFile.annotationItems.add(newAnnotationItem);
+ newAnnotationItem.read(file);
+ }
+ break;
+ case MapItem.TYPE_ENCODED_ARRAY_ITEM:
+ rawDexFile.encodedArrayItems = new ArrayList<EncodedArrayItem>(mapItem.size);
+ for (int i = 0; i < mapItem.size; i++) {
+ EncodedArrayItem newEncodedArrayItem = new EncodedArrayItem();
+ rawDexFile.encodedArrayItems.add(newEncodedArrayItem);
+ newEncodedArrayItem.read(file);
+ }
+ break;
+ case MapItem.TYPE_ANNOTATIONS_DIRECTORY_ITEM:
+ rawDexFile.annotationsDirectoryItems =
+ new ArrayList<AnnotationsDirectoryItem>(mapItem.size);
+ for (int i = 0; i < mapItem.size; i++) {
+ AnnotationsDirectoryItem newAnnotationsDirectoryItem = new AnnotationsDirectoryItem();
+ rawDexFile.annotationsDirectoryItems.add(newAnnotationsDirectoryItem);
+ newAnnotationsDirectoryItem.read(file);
+ }
+ break;
+ default:
+ Log.errorAndQuit("Encountered unknown map item when reading map item list.");
+ }
+ mapItemIdx++;
+ }
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.alignForwards(4);
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ file.writeUInt(mapItems.size());
+ for (MapItem mapItem : mapItems) {
+ mapItem.write(file);
+ }
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ // Do nothing.
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/MethodAnnotation.java b/tools/dexfuzz/src/dexfuzz/rawdex/MethodAnnotation.java
new file mode 100644
index 0000000..1a24fb6
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/MethodAnnotation.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class MethodAnnotation implements RawDexObject {
+ public int methodIdx;
+ public Offset annotationsOff;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ methodIdx = file.readUInt();
+ annotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.writeUInt(methodIdx);
+ file.getOffsetTracker().tryToWriteOffset(annotationsOff, file, false /* ULEB128 */);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (kind == IndexUpdateKind.METHOD_ID && methodIdx >= insertedIdx) {
+ methodIdx++;
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/MethodIdItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/MethodIdItem.java
new file mode 100644
index 0000000..12d0187
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/MethodIdItem.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class MethodIdItem implements RawDexObject {
+ public short classIdx;
+ public short protoIdx;
+ public int nameIdx;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().getNewOffsettable(file, this);
+ classIdx = file.readUShort();
+ protoIdx = file.readUShort();
+ nameIdx = file.readUInt();
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ file.writeUShort(classIdx);
+ file.writeUShort(protoIdx);
+ file.writeUInt(nameIdx);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (kind == IndexUpdateKind.TYPE_ID && classIdx >= insertedIdx) {
+ classIdx++;
+ }
+ if (kind == IndexUpdateKind.PROTO_ID && protoIdx >= insertedIdx) {
+ protoIdx++;
+ }
+ if (kind == IndexUpdateKind.STRING_ID && nameIdx >= insertedIdx) {
+ nameIdx++;
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/Offset.java b/tools/dexfuzz/src/dexfuzz/rawdex/Offset.java
new file mode 100644
index 0000000..b6718e3
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/Offset.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+public class Offset {
+ /**
+ * The absolute value of this offset as it was originally read.
+ */
+ private int originalOffset;
+
+ /**
+ * The Offsettable that this Offset points to.
+ */
+ private Offsettable offsettable;
+
+ /**
+ * The location of this Offset in the new file, ONLY SET IF the Offset
+ * couldn't be written because what it points to hasn't been written
+ * yet.
+ */
+ private int outputLocation;
+
+ /**
+ * Was the output location for this Offset set?.
+ */
+ private boolean outputLocationSet;
+
+ /**
+ * Does this Offset need to be written out using ULEB128?.
+ */
+ private boolean useUleb128;
+
+ /**
+ * Was this Offset created after reading, during mutation?.
+ */
+ private boolean isNewOffset;
+
+ /**
+ * Only one Offset should have this flag set, the MapItem that points
+ * to the HeaderItem.
+ */
+ private boolean pointsAtHeader;
+
+ /**
+ * If an Offset pointed at 0 (because it is not actually a valid Offset),
+ * and it's not pointing at the header, then this is set.
+ */
+ private boolean pointsAtNull;
+
+ public Offset(boolean header) {
+ pointsAtHeader = header;
+ }
+
+ public RawDexObject getPointedToItem() {
+ return offsettable.getItem();
+ }
+
+ public boolean pointsToSomething() {
+ return offsettable != null;
+ }
+
+ public boolean pointsAtNull() {
+ return pointsAtNull;
+ }
+
+ public boolean pointsAtHeader() {
+ return pointsAtHeader;
+ }
+
+ /**
+ * Returns true if this Offset points at the provided RawDexObject.
+ */
+ public boolean pointsToThisItem(RawDexObject thisItem) {
+ if (!pointsToSomething()) {
+ return false;
+ }
+ return (offsettable.getItem().equals(thisItem));
+ }
+
+ /**
+ * Returns true if this Offset points at the provided Offsettable.
+ */
+ public boolean pointsToThisOffsettable(Offsettable thisOffsettable) {
+ if (!pointsToSomething()) {
+ return false;
+ }
+ return (offsettable.equals(thisOffsettable));
+ }
+
+ /**
+ * Makes this Offset point at a new Offsettable.
+ */
+ public void pointTo(Offsettable offsettableItem) {
+ if (offsettable != null) {
+ Log.debug("Updating what an Offset points to...");
+ }
+ offsettable = offsettableItem;
+ }
+
+ /**
+ * Call this to make an Offset that pointed at null before now point at something.
+ * An Offset may have previously pointed at null before...
+ * Example: if there are no fields referred to in a DEX file, then header.field_ids_off
+ * will point at null. We distinguish when Offsets point at null, and are not pointing
+ * at the header (only the header MapItem should do this) with a flag. Therefore, this
+ * method is needed to indicate that this Offset now points at something.
+ */
+ public void unsetNullAndPointTo(Offsettable offsettableItem) {
+ pointsAtNull = false;
+ if (offsettable != null) {
+ Log.debug("Updating what an Offset points to...");
+ }
+ offsettable = offsettableItem;
+ }
+
+ public void pointToNew(Offsettable offsettableItem) {
+ offsettable = offsettableItem;
+ isNewOffset = true;
+ }
+
+ public int getNewPositionOfItem() {
+ return offsettable.getNewPosition();
+ }
+
+ public boolean usesUleb128() {
+ return useUleb128;
+ }
+
+ /**
+ * Mark this Offset as using the ULEB128 encoding.
+ */
+ public void setUsesUleb128() {
+ if (useUleb128) {
+ throw new Error("Offset is already marked as using ULEB128!");
+ }
+ useUleb128 = true;
+ }
+
+ public boolean isNewOffset() {
+ return isNewOffset;
+ }
+
+ public void setPointsAtNull() {
+ pointsAtNull = true;
+ }
+
+ public void setOutputLocation(int loc) {
+ outputLocation = loc;
+ outputLocationSet = true;
+ }
+
+ /**
+ * Get the location in the output DEX file where this offset has been written.
+ * (This is used when patching Offsets when the Offsettable position was not
+ * known at the time of writing out the Offset.)
+ */
+ public int getOutputLocation() {
+ if (!outputLocationSet) {
+ throw new Error("Output location was not set yet!");
+ }
+ return outputLocation;
+ }
+
+ public void setOriginalOffset(int offset) {
+ originalOffset = offset;
+ }
+
+ public int getOriginalOffset() {
+ return originalOffset;
+ }
+
+ public boolean readyForWriting() {
+ return offsettable.readyForFinalOffsetToBeWritten();
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/OffsetTracker.java b/tools/dexfuzz/src/dexfuzz/rawdex/OffsetTracker.java
new file mode 100644
index 0000000..34d609a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/OffsetTracker.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class allows the recording of both Offsettable items (that is, items that can be
+ * referred to by an offset somewhere else in the file - RawDexObjects) and Offsets.
+ * The idea in a nutshell is that for every Offsettable item we read, we remember
+ * its original position in the file using a map, and the order in which the Offsettables were
+ * written out. We also remember every Offset we read in, and its value. Then, after reading
+ * the whole file, we use the map to find the Offsettable it pointed at.
+ * Then, as we write out the file, for every Offsettable we write out, we record its new position,
+ * using the order we collected earlier. For every Offset we write out, we look at its Offsettable
+ * to see where it was written. If it hasn't been written yet, then we write out a blank value
+ * for the time being, remember where that blank value was written, and put the Offset into a
+ * table for patching once everything has been written out.
+ * There are some variables (index_after_map_list, restore_point) used for remembering certain
+ * points to jump forward and back to, because we cannot read and write the file out in exactly
+ * the same order.
+ * TODO: Perhaps it makes more sense to just reorder the offsettable_table once it's been read,
+ * in preparation for the order in which the file is written out?
+ * Finally, we provide methods for adding new Offsettable items into the right place in the order
+ * table.
+ */
+public class OffsetTracker {
+ /**
+ * A map from the original offset in the input DEX file to
+ * the Offsettable it points to. (That Offsettable will contain
+ * the actual item, and later on the new offset for the item when
+ * the item is written out.
+ */
+ private Map<Integer, Offsettable> offsettableMap;
+
+ /**
+ * A table of all Offsettables. We need to ensure we write out
+ * all items in the same order we read them in, to make sure we update
+ * the Offsettable.new_position field with the correct value wrt to
+ * the original_position field.
+ */
+ private List<Offsettable> offsettableTable;
+
+ /**
+ * A table of all offsets that is populated as we read in the DEX file.
+ * As the end, we find the correct Offsettable for the Offset in the above
+ * map, and associate them.
+ */
+ private List<Offset> needsAssociationTable;
+
+ /**
+ * A table of all offsets that we could not write out an updated offset for
+ * as we write out a DEX file. Will be checked after writing is complete,
+ * to allow specific patching of each offset's location as at that point
+ * all Offsettables will have been updated with their new position.
+ */
+ private List<Offset> needsUpdateTable;
+
+ /**
+ * Tracks how far we are through the offsettable_table as we write out the file.
+ */
+ private int offsettableTableIdx;
+
+ /**
+ * Because we write in a slightly different order to how we read
+ * (why? to read, we read the header, then the map list, and then use the map
+ * list to read everything else.
+ * when we write, we write the header, and then we cannot write the map list
+ * because we don't know where it will go yet, so we write everything else first)
+ * so: we remember the position in the offsettable_table after we read the map list,
+ * so we can skip there after we write out the header.
+ */
+ private int indexAfterMapList;
+
+ /**
+ * Related to index_after_map_list, this is the index we save when we're jumping back to
+ * write the map list.
+ */
+ private int restorePoint;
+
+ /**
+ * Create a new OffsetTracker. Should persist between parsing a DEX file, and outputting
+ * the mutated DEX file.
+ */
+ public OffsetTracker() {
+ offsettableMap = new HashMap<Integer,Offsettable>();
+ offsettableTable = new ArrayList<Offsettable>();
+ needsAssociationTable = new ArrayList<Offset>();
+ needsUpdateTable = new ArrayList<Offset>();
+ }
+
+ /**
+ * Lookup an Item by the offset it had in the input DEX file.
+ * @param offset The offset in the input DEX file.
+ * @return The corresponding Item.
+ */
+ public RawDexObject getItemByOffset(int offset) {
+ return offsettableMap.get(offset).getItem();
+ }
+
+ /**
+ * As Items are read in, they call this function once they have word-aligned the file pointer,
+ * to record their position and themselves into an Offsettable object, that will be tracked.
+ * @param file Used for recording position into the new Offsettable.
+ * @param item Used for recording the relevant Item into the new Offsettable.
+ */
+ public void getNewOffsettable(DexRandomAccessFile file, RawDexObject item) throws IOException {
+ Offsettable offsettable = new Offsettable(item, false);
+ offsettable.setOriginalPosition((int) file.getFilePointer());
+ offsettableMap.put(offsettable.getOriginalPosition(), offsettable);
+ offsettableTable.add(offsettable);
+ }
+
+ /**
+ * As Items read in Offsets, they call this function with the offset they originally
+ * read from the file, to allow later association with an Offsettable.
+ * @param originalOffset The original offset read from the input DEX file.
+ * @return An Offset that will later be associated with an Offsettable.
+ */
+ public Offset getNewOffset(int originalOffset) throws IOException {
+ Offset offset = new Offset(false);
+ offset.setOriginalOffset(originalOffset);
+ needsAssociationTable.add(offset);
+ return offset;
+ }
+
+ /**
+ * Only MapItem should call this method, when the MapItem that points to the header
+ * is read.
+ */
+ public Offset getNewHeaderOffset(int originalOffset) throws IOException {
+ Offset offset = new Offset(true);
+ offset.setOriginalOffset(originalOffset);
+ needsAssociationTable.add(offset);
+ return offset;
+ }
+
+ /**
+ * Call this after reading, to associate Offsets with Offsettables.
+ */
+ public void associateOffsets() {
+ for (Offset offset : needsAssociationTable) {
+ if (offset.getOriginalOffset() == 0 && !(offset.pointsAtHeader())) {
+ offset.setPointsAtNull();
+ } else {
+ offset.pointTo(offsettableMap.get(offset.getOriginalOffset()));
+ if (!offset.pointsToSomething()) {
+ Log.error(String.format("Couldn't find original offset 0x%x!",
+ offset.getOriginalOffset()));
+ }
+ }
+ }
+ needsAssociationTable.clear();
+ }
+
+ /**
+ * As Items are written out into the output DEX file, this function is called
+ * to update the next Offsettable with the file pointer's current position.
+ * This should allow the tracking of new offset locations.
+ * This also requires that reading and writing of all items happens in the same order
+ * (with the exception of the map list, see above)
+ * @param file Used for recording the new position.
+ */
+ public void updatePositionOfNextOffsettable(DexRandomAccessFile file) throws IOException {
+ if (offsettableTableIdx == offsettableTable.size()) {
+ Log.errorAndQuit("Not all created Offsettable items have been added to the "
+ + "Offsettable Table!");
+ }
+ Offsettable offsettable = offsettableTable.get(offsettableTableIdx);
+ offsettable.setNewPosition((int) file.getFilePointer());
+ offsettableTableIdx++;
+ }
+
+ /**
+ * As Items are written out, any writing out of an offset must call this function, passing
+ * in the relevant offset. This function will write out the offset, if the associated
+ * Offsettable has been updated with its new position, or else will write out a null value, and
+ * the Offset will be stored for writing after all Items have been written, and all
+ * Offsettables MUST have been updated.
+ * @param offset The offset received from getNewOffset().
+ * @param file Used for writing out to the file.
+ * @param useUleb128 Whether or not the offset should be written in UINT or ULEB128 form.
+ */
+ public void tryToWriteOffset(Offset offset, DexRandomAccessFile file, boolean useUleb128)
+ throws IOException {
+ if (!offset.isNewOffset() && (!offset.pointsToSomething())) {
+ if (useUleb128) {
+ file.writeUleb128(0);
+ } else {
+ file.writeUInt(0);
+ }
+ return;
+ }
+
+ if (offset.readyForWriting()) {
+ if (useUleb128) {
+ file.writeUleb128(offset.getNewPositionOfItem());
+ } else {
+ file.writeUInt(offset.getNewPositionOfItem());
+ }
+ } else {
+ offset.setOutputLocation((int) file.getFilePointer());
+ if (useUleb128) {
+ file.writeLargestUleb128(offset.getOriginalOffset());
+ offset.setUsesUleb128();
+ } else {
+ file.writeUInt(offset.getOriginalOffset());
+ }
+ needsUpdateTable.add(offset);
+ }
+ }
+
+ /**
+ * This is called after all writing has finished, to write out any Offsets
+ * that could not be written out during the original writing phase, because their
+ * associated Offsettables hadn't had their new positions calculated yet.
+ * @param file Used for writing out to the file.
+ */
+ public void updateOffsets(DexRandomAccessFile file) throws IOException {
+ if (offsettableTableIdx != offsettableTable.size()) {
+ Log.errorAndQuit("Being asked to update dangling offsets but the "
+ + "correct number of offsettables has not been written out!");
+ }
+ for (Offset offset : needsUpdateTable) {
+ file.seek(offset.getOutputLocation());
+ if (offset.usesUleb128()) {
+ file.writeLargestUleb128(offset.getNewPositionOfItem());
+ } else {
+ file.writeUInt(offset.getNewPositionOfItem());
+ }
+ }
+ needsUpdateTable.clear();
+ }
+
+ /**
+ * Called after writing out the header, to skip to after the map list.
+ */
+ public void skipToAfterMapList() {
+ offsettableTableIdx = indexAfterMapList;
+ }
+
+ /**
+ * Called once the map list needs to be written out, to set the
+ * offsettable table index back to the right place.
+ */
+ public void goBackToMapList() {
+ restorePoint = offsettableTableIdx;
+ offsettableTableIdx = (indexAfterMapList - 1);
+ }
+
+ /**
+ * Called once the map list has been written out, to set the
+ * offsettable table index back to where it was before.
+ */
+ public void goBackToPreviousPoint() {
+ if (offsettableTableIdx != indexAfterMapList) {
+ Log.errorAndQuit("Being asked to go to the point before the MapList was written out,"
+ + " but we're not in the right place.");
+ }
+ offsettableTableIdx = restorePoint;
+ }
+
+ /**
+ * Called after reading in the map list, to remember the point to be skipped
+ * to later.
+ */
+ public void rememberPointAfterMapList() {
+ indexAfterMapList = offsettableTable.size();
+ }
+
+ private void updateHeaderOffsetIfValid(Offset offset, Offsettable previousFirst,
+ Offsettable newFirst, String offsetName) {
+ if (offset.pointsToThisOffsettable(previousFirst)) {
+ offset.pointToNew(newFirst);
+ } else {
+ Log.errorAndQuit("Header " + offsetName + " offset not pointing at first element?");
+ }
+ }
+
+ private void addTypeListsToMapFile(RawDexFile rawDexFile, Offsettable typeListOffsettable) {
+ // Create a MapItem for the TypeLists
+ MapItem typeListMapItem = new MapItem();
+ typeListMapItem.offset = new Offset(false);
+ typeListMapItem.offset.pointToNew(typeListOffsettable);
+ typeListMapItem.type = MapItem.TYPE_TYPE_LIST;
+ typeListMapItem.size = 1;
+
+ // Insert into the MapList.
+ // (first, find the MapItem that points to StringDataItems...)
+ int idx = 0;
+ for (MapItem mapItem : rawDexFile.mapList.mapItems) {
+ if (mapItem.type == MapItem.TYPE_STRING_DATA_ITEM) {
+ break;
+ }
+ idx++;
+ }
+ // (now insert the TypeList MapItem just before the StringDataItem one...)
+ rawDexFile.mapList.mapItems.add(idx, typeListMapItem);
+ }
+
+ private void addFieldIdsToHeaderAndMapFile(RawDexFile rawDexFile,
+ Offsettable fieldOffsettable) {
+ // Add the field IDs to the header.
+ rawDexFile.header.fieldIdsOff.unsetNullAndPointTo(fieldOffsettable);
+ rawDexFile.header.fieldIdsSize = 1;
+
+ // Create a MapItem for the field IDs.
+ MapItem fieldMapItem = new MapItem();
+ fieldMapItem.offset = new Offset(false);
+ fieldMapItem.offset.pointToNew(fieldOffsettable);
+ fieldMapItem.type = MapItem.TYPE_FIELD_ID_ITEM;
+ fieldMapItem.size = 1;
+
+ // Insert into the MapList.
+ // (first, find the MapItem that points to MethodIdItems...)
+ int idx = 0;
+ for (MapItem mapItem : rawDexFile.mapList.mapItems) {
+ if (mapItem.type == MapItem.TYPE_METHOD_ID_ITEM) {
+ break;
+ }
+ idx++;
+ }
+ // (now insert the FieldIdItem MapItem just before the MethodIdItem one...)
+ rawDexFile.mapList.mapItems.add(idx, fieldMapItem);
+ }
+
+
+ private void updateOffsetsInHeaderAndMapFile(RawDexFile rawDexFile,
+ Offsettable newFirstOffsettable) {
+ Offsettable prevFirstOffsettable = null;
+ for (int i = 0; i < offsettableTable.size(); i++) {
+ if (offsettableTable.get(i) == newFirstOffsettable) {
+ prevFirstOffsettable = offsettableTable.get(i + 1);
+ break;
+ }
+ }
+ if (prevFirstOffsettable == null) {
+ Log.errorAndQuit("When calling updateMapListOffsets, could not find new "
+ + "first offsettable?");
+ }
+
+ // Based on the type of the item we just added, check the relevant Offset in the header
+ // and if it pointed at the prev_first_offsettable, make it point at the new one.
+ // NB: if it isn't pointing at the prev one, something is wrong.
+ HeaderItem header = rawDexFile.header;
+ if (newFirstOffsettable.getItem() instanceof StringIdItem) {
+ updateHeaderOffsetIfValid(header.stringIdsOff, prevFirstOffsettable,
+ newFirstOffsettable, "StringID");
+ } else if (newFirstOffsettable.getItem() instanceof TypeIdItem) {
+ updateHeaderOffsetIfValid(header.typeIdsOff, prevFirstOffsettable,
+ newFirstOffsettable, "TypeID");
+ } else if (newFirstOffsettable.getItem() instanceof ProtoIdItem) {
+ updateHeaderOffsetIfValid(header.protoIdsOff, prevFirstOffsettable,
+ newFirstOffsettable, "ProtoID");
+ } else if (newFirstOffsettable.getItem() instanceof FieldIdItem) {
+ updateHeaderOffsetIfValid(header.fieldIdsOff, prevFirstOffsettable,
+ newFirstOffsettable, "FieldID");
+ } else if (newFirstOffsettable.getItem() instanceof MethodIdItem) {
+ updateHeaderOffsetIfValid(header.methodIdsOff, prevFirstOffsettable,
+ newFirstOffsettable, "MethodID");
+ } else if (newFirstOffsettable.getItem() instanceof ClassDefItem) {
+ updateHeaderOffsetIfValid(header.classDefsOff, prevFirstOffsettable,
+ newFirstOffsettable, "ClassDef");
+ }
+
+ // Now iterate through the MapList's MapItems, and see if their Offsets pointed at the
+ // prev_first_offsettable, and if so, make them now point at the new_first_offsettable.
+ for (MapItem mapItem : rawDexFile.mapList.mapItems) {
+ if (mapItem.offset.pointsToThisOffsettable(prevFirstOffsettable)) {
+ Log.info("Updating offset in MapItem (type: " + mapItem.type + ") after "
+ + "we called insertNewOffsettableAsFirstOfType()");
+ mapItem.offset.pointToNew(newFirstOffsettable);
+ }
+ }
+ }
+
+ private void insertOffsettableAt(int idx, Offsettable offsettable) {
+ offsettableTable.add(idx, offsettable);
+ if (indexAfterMapList > idx) {
+ indexAfterMapList++;
+ }
+ if (restorePoint > idx) {
+ restorePoint++;
+ }
+ }
+
+ /**
+ * If we're creating our first TypeList, then IdCreator has to call this method to
+ * ensure it gets put into the correct place in the offsettable table.
+ * This assumes TypeLists always come before StringDatas.
+ */
+ public Offsettable insertNewOffsettableAsFirstEverTypeList(RawDexObject item,
+ RawDexFile rawDexFile) {
+ // We find the first StringDataItem, the type lists will come before this.
+ Log.info("Calling insertNewOffsettableAsFirstEverTypeList()");
+ for (int i = 0; i < offsettableTable.size(); i++) {
+ if (offsettableTable.get(i).getItem() instanceof StringDataItem) {
+ Offsettable offsettable = new Offsettable(item, true);
+ insertOffsettableAt(i, offsettable);
+ addTypeListsToMapFile(rawDexFile, offsettable);
+ return offsettable;
+ }
+ }
+ Log.errorAndQuit("Could not find any StringDataItems to insert the type list before.");
+ return null;
+ }
+
+ /**
+ * If we're creating our first FieldId, then IdCreator has to call this method to
+ * ensure it gets put into the correct place in the offsettable table.
+ * This assumes FieldIds always come before MethodIds.
+ */
+ public Offsettable insertNewOffsettableAsFirstEverField(RawDexObject item,
+ RawDexFile rawDexFile) {
+ // We find the first MethodIdItem, the fields will come before this.
+ Log.info("Calling insertNewOffsettableAsFirstEverField()");
+ for (int i = 0; i < offsettableTable.size(); i++) {
+ if (offsettableTable.get(i).getItem() instanceof MethodIdItem) {
+ Offsettable offsettable = new Offsettable(item, true);
+ insertOffsettableAt(i, offsettable);
+ addFieldIdsToHeaderAndMapFile(rawDexFile, offsettable);
+ return offsettable;
+ }
+ }
+ Log.errorAndQuit("Could not find any MethodIdItems to insert the field before.");
+ return null;
+ }
+
+ /**
+ * If we're creating a new Item (such as FieldId, MethodId) that is placed into the
+ * first position of the relevant ID table, then IdCreator has to call this method to
+ * ensure it gets put into the correct place in the offsettable table.
+ */
+ public Offsettable insertNewOffsettableAsFirstOfType(RawDexObject item,
+ RawDexFile rawDexFile) {
+ Log.debug("Calling insertNewOffsettableAsFirstOfType()");
+ int index = getOffsettableIndexForFirstItemType(item);
+ if (index == -1) {
+ Log.errorAndQuit("Could not find any object of class: " + item.getClass());
+ }
+ Offsettable offsettable = new Offsettable(item, true);
+ insertOffsettableAt(index, offsettable);
+ updateOffsetsInHeaderAndMapFile(rawDexFile, offsettable);
+ return offsettable;
+ }
+
+ /**
+ * IdCreator has to call this method when it creates a new IdItem, to make sure it
+ * gets put into the correct place in the offsettable table. IdCreator should
+ * provide the IdItem that should come before this new IdItem.
+ */
+ public Offsettable insertNewOffsettableAfter(RawDexObject item, RawDexObject itemBefore) {
+ Log.debug("Calling insertNewOffsettableAfter()");
+ int index = getOffsettableIndexForItem(itemBefore);
+ if (index == -1) {
+ Log.errorAndQuit("Did not find specified 'after' object in offsettable table.");
+ }
+ Offsettable offsettable = new Offsettable(item, true);
+ insertOffsettableAt(index + 1, offsettable);
+ return offsettable;
+ }
+
+ private int getOffsettableIndexForFirstItemType(RawDexObject item) {
+ Class<?> itemClass = item.getClass();
+ for (int i = 0; i < offsettableTable.size(); i++) {
+ if (offsettableTable.get(i).getItem().getClass().equals(itemClass)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private int getOffsettableIndexForItem(RawDexObject item) {
+ for (int i = 0; i < offsettableTable.size(); i++) {
+ if (offsettableTable.get(i).getItem() == item) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Given a RawDexObject, get the Offsettable that contains it.
+ */
+ public Offsettable getOffsettableForItem(RawDexObject item) {
+ for (int i = 0; i < offsettableTable.size(); i++) {
+ if (offsettableTable.get(i).getItem() == item) {
+ return offsettableTable.get(i);
+ }
+ }
+ return null;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/Offsettable.java b/tools/dexfuzz/src/dexfuzz/rawdex/Offsettable.java
new file mode 100644
index 0000000..1b8cb24
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/Offsettable.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+/**
+ * Tracks the original and updated positions of a RawDexObject when it is
+ * parsed in from a DEX file, and written out to a mutated DEX file.
+ */
+public class Offsettable {
+ /**
+ * The position of this Offsettable's item when it was read in.
+ */
+ private int originalPosition;
+
+ /**
+ * Set as we write out any Offsettable, so the Offset knows what its
+ * new value should be.
+ */
+ private int newPosition;
+
+ /**
+ * The actual Item this Offsettable contains.
+ */
+ private RawDexObject item;
+
+ /**
+ * Set either when getOriginalPosition() is called by the OffsetTracker
+ * to put the location in the offsettable map, so when Offsets are being
+ * associated, they know which Offsettable to point at.
+ * Or when an Offsettable is created that is marked as new, so we don't
+ * need to know its original position, because an Offset will be directly
+ * associated with it.
+ */
+ private boolean originalPositionKnown;
+
+ /**
+ * Set when we calculate the new position of this Offsettable as the file is
+ * being output.
+ */
+ private boolean updated;
+
+ /**
+ * Only the OffsetTracker should be able to create a new Offsettable.
+ */
+ public Offsettable(RawDexObject item, boolean isNew) {
+ this.item = item;
+ if (isNew) {
+ // We no longer care about the original position of the Offsettable, because
+ // we are at the stage where we manually point Offsets at Offsettables, and
+ // don't need to use the OffsetTracker's offsettable map.
+ // So just lie and say we know it now.
+ originalPositionKnown = true;
+ }
+ }
+
+ public RawDexObject getItem() {
+ return item;
+ }
+
+ /**
+ * Gets the offset from the beginning of the file to the RawDexObject this Offsettable
+ * contains, when the file was originally read.
+ * Called when we're associating Offsets with Offsettables using the OffsetTracker's
+ * offsettable map.
+ */
+ public int getOriginalPosition() {
+ if (!originalPositionKnown) {
+ throw new Error("Cannot get the original position of an Offsettable when not yet set.");
+ }
+ return originalPosition;
+ }
+
+ public void setOriginalPosition(int pos) {
+ originalPosition = pos;
+ originalPositionKnown = true;
+ }
+
+ /**
+ * Get the new position of this Offsettable, once it's been written out to the output file.
+ */
+ public int getNewPosition() {
+ if (!updated) {
+ throw new Error("Cannot request new position before it has been set!");
+ }
+ return newPosition;
+ }
+
+ /**
+ * Record the new position of this Offsettable, as it is written out to the output file.
+ */
+ public void setNewPosition(int pos) {
+ if (!updated) {
+ newPosition = pos;
+ updated = true;
+ } else {
+ throw new Error("Cannot update an Offsettable twice!");
+ }
+ }
+
+ public boolean readyForFinalOffsetToBeWritten() {
+ return (originalPositionKnown && updated);
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/Opcode.java b/tools/dexfuzz/src/dexfuzz/rawdex/Opcode.java
new file mode 100644
index 0000000..312e855
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/Opcode.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+public enum Opcode {
+ NOP,
+ MOVE,
+ MOVE_FROM16,
+ MOVE_16,
+ MOVE_WIDE,
+ MOVE_WIDE_FROM16,
+ MOVE_WIDE_16,
+ MOVE_OBJECT,
+ MOVE_OBJECT_FROM16,
+ MOVE_OBJECT_16,
+ MOVE_RESULT,
+ MOVE_RESULT_WIDE,
+ MOVE_RESULT_OBJECT,
+ MOVE_EXCEPTION,
+ RETURN_VOID,
+ RETURN,
+ RETURN_WIDE,
+ RETURN_OBJECT,
+ CONST_4,
+ CONST_16,
+ CONST,
+ CONST_HIGH16,
+ CONST_WIDE_16,
+ CONST_WIDE_32,
+ CONST_WIDE,
+ CONST_WIDE_HIGH16,
+ CONST_STRING,
+ CONST_STRING_JUMBO,
+ CONST_CLASS,
+ MONITOR_ENTER,
+ MONITOR_EXIT,
+ CHECK_CAST,
+ INSTANCE_OF,
+ ARRAY_LENGTH,
+ NEW_INSTANCE,
+ NEW_ARRAY,
+ FILLED_NEW_ARRAY,
+ FILLED_NEW_ARRAY_RANGE,
+ FILL_ARRAY_DATA,
+ THROW,
+ GOTO,
+ GOTO_16,
+ GOTO_32,
+ PACKED_SWITCH,
+ SPARSE_SWITCH,
+ CMPL_FLOAT,
+ CMPG_FLOAT,
+ CMPL_DOUBLE,
+ CMPG_DOUBLE,
+ CMP_LONG,
+ IF_EQ,
+ IF_NE,
+ IF_LT,
+ IF_GE,
+ IF_GT,
+ IF_LE,
+ IF_EQZ,
+ IF_NEZ,
+ IF_LTZ,
+ IF_GEZ,
+ IF_GTZ,
+ IF_LEZ,
+ UNUSED_3E,
+ UNUSED_3F,
+ UNUSED_40,
+ UNUSED_41,
+ UNUSED_42,
+ UNUSED_43,
+ AGET,
+ AGET_WIDE,
+ AGET_OBJECT,
+ AGET_BOOLEAN,
+ AGET_BYTE,
+ AGET_CHAR,
+ AGET_SHORT,
+ APUT,
+ APUT_WIDE,
+ APUT_OBJECT,
+ APUT_BOOLEAN,
+ APUT_BYTE,
+ APUT_CHAR,
+ APUT_SHORT,
+ IGET,
+ IGET_WIDE,
+ IGET_OBJECT,
+ IGET_BOOLEAN,
+ IGET_BYTE,
+ IGET_CHAR,
+ IGET_SHORT,
+ IPUT,
+ IPUT_WIDE,
+ IPUT_OBJECT,
+ IPUT_BOOLEAN,
+ IPUT_BYTE,
+ IPUT_CHAR,
+ IPUT_SHORT,
+ SGET,
+ SGET_WIDE,
+ SGET_OBJECT,
+ SGET_BOOLEAN,
+ SGET_BYTE,
+ SGET_CHAR,
+ SGET_SHORT,
+ SPUT,
+ SPUT_WIDE,
+ SPUT_OBJECT,
+ SPUT_BOOLEAN,
+ SPUT_BYTE,
+ SPUT_CHAR,
+ SPUT_SHORT,
+ INVOKE_VIRTUAL,
+ INVOKE_SUPER,
+ INVOKE_DIRECT,
+ INVOKE_STATIC,
+ INVOKE_INTERFACE,
+ RETURN_VOID_BARRIER,
+ INVOKE_VIRTUAL_RANGE,
+ INVOKE_SUPER_RANGE,
+ INVOKE_DIRECT_RANGE,
+ INVOKE_STATIC_RANGE,
+ INVOKE_INTERFACE_RANGE,
+ UNUSED_79,
+ UNUSED_7A,
+ NEG_INT,
+ NOT_INT,
+ NEG_LONG,
+ NOT_LONG,
+ NEG_FLOAT,
+ NEG_DOUBLE,
+ INT_TO_LONG,
+ INT_TO_FLOAT,
+ INT_TO_DOUBLE,
+ LONG_TO_INT,
+ LONG_TO_FLOAT,
+ LONG_TO_DOUBLE,
+ FLOAT_TO_INT,
+ FLOAT_TO_LONG,
+ FLOAT_TO_DOUBLE,
+ DOUBLE_TO_INT,
+ DOUBLE_TO_LONG,
+ DOUBLE_TO_FLOAT,
+ INT_TO_BYTE,
+ INT_TO_CHAR,
+ INT_TO_SHORT,
+ ADD_INT,
+ SUB_INT,
+ MUL_INT,
+ DIV_INT,
+ REM_INT,
+ AND_INT,
+ OR_INT,
+ XOR_INT,
+ SHL_INT,
+ SHR_INT,
+ USHR_INT,
+ ADD_LONG,
+ SUB_LONG,
+ MUL_LONG,
+ DIV_LONG,
+ REM_LONG,
+ AND_LONG,
+ OR_LONG,
+ XOR_LONG,
+ SHL_LONG,
+ SHR_LONG,
+ USHR_LONG,
+ ADD_FLOAT,
+ SUB_FLOAT,
+ MUL_FLOAT,
+ DIV_FLOAT,
+ REM_FLOAT,
+ ADD_DOUBLE,
+ SUB_DOUBLE,
+ MUL_DOUBLE,
+ DIV_DOUBLE,
+ REM_DOUBLE,
+ ADD_INT_2ADDR,
+ SUB_INT_2ADDR,
+ MUL_INT_2ADDR,
+ DIV_INT_2ADDR,
+ REM_INT_2ADDR,
+ AND_INT_2ADDR,
+ OR_INT_2ADDR,
+ XOR_INT_2ADDR,
+ SHL_INT_2ADDR,
+ SHR_INT_2ADDR,
+ USHR_INT_2ADDR,
+ ADD_LONG_2ADDR,
+ SUB_LONG_2ADDR,
+ MUL_LONG_2ADDR,
+ DIV_LONG_2ADDR,
+ REM_LONG_2ADDR,
+ AND_LONG_2ADDR,
+ OR_LONG_2ADDR,
+ XOR_LONG_2ADDR,
+ SHL_LONG_2ADDR,
+ SHR_LONG_2ADDR,
+ USHR_LONG_2ADDR,
+ ADD_FLOAT_2ADDR,
+ SUB_FLOAT_2ADDR,
+ MUL_FLOAT_2ADDR,
+ DIV_FLOAT_2ADDR,
+ REM_FLOAT_2ADDR,
+ ADD_DOUBLE_2ADDR,
+ SUB_DOUBLE_2ADDR,
+ MUL_DOUBLE_2ADDR,
+ DIV_DOUBLE_2ADDR,
+ REM_DOUBLE_2ADDR,
+ ADD_INT_LIT16,
+ RSUB_INT,
+ MUL_INT_LIT16,
+ DIV_INT_LIT16,
+ REM_INT_LIT16,
+ AND_INT_LIT16,
+ OR_INT_LIT16,
+ XOR_INT_LIT16,
+ ADD_INT_LIT8,
+ RSUB_INT_LIT8,
+ MUL_INT_LIT8,
+ DIV_INT_LIT8,
+ REM_INT_LIT8,
+ AND_INT_LIT8,
+ OR_INT_LIT8,
+ XOR_INT_LIT8,
+ SHL_INT_LIT8,
+ SHR_INT_LIT8,
+ USHR_INT_LIT8,
+ IGET_QUICK,
+ IGET_WIDE_QUICK,
+ IGET_OBJECT_QUICK,
+ IPUT_QUICK,
+ IPUT_WIDE_QUICK,
+ IPUT_OBJECT_QUICK,
+ INVOKE_VIRTUAL_QUICK,
+ INVOKE_VIRTUAL_QUICK_RANGE,
+ IPUT_BOOLEAN_QUICK,
+ IPUT_BYTE_QUICK,
+ IPUT_CHAR_QUICK,
+ IPUT_SHORT_QUICK,
+ UNUSED_EF,
+ UNUSED_F0,
+ UNUSED_F1,
+ UNUSED_F2,
+ UNUSED_F3,
+ UNUSED_F4,
+ UNUSED_F5,
+ UNUSED_F6,
+ UNUSED_F7,
+ UNUSED_F8,
+ UNUSED_F9,
+ UNUSED_FA,
+ UNUSED_FB,
+ UNUSED_FC,
+ UNUSED_FD,
+ UNUSED_FE,
+ UNUSED_FF;
+
+ public static boolean isBetween(Opcode opcode, Opcode opcode1, Opcode opcode2) {
+ return (opcode.ordinal() >= opcode1.ordinal() && opcode.ordinal() <= opcode2.ordinal());
+ }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/OpcodeInfo.java b/tools/dexfuzz/src/dexfuzz/rawdex/OpcodeInfo.java
new file mode 100644
index 0000000..fb1d093
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/OpcodeInfo.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.rawdex.formats.AbstractFormat;
+
+/**
+ * Every Instruction points to an OpcodeInfo object that holds useful information
+ * about that kind of instruction, including the Format that allows us to read the
+ * instructions fields correctly.
+ */
+public class OpcodeInfo {
+ public final Opcode opcode;
+ public final String name;
+ public final int value;
+ public final AbstractFormat format;
+
+ /**
+ * Construct an OpcodeInfo. A static list of these is created in Instruction.java.
+ */
+ public OpcodeInfo(Opcode opcode, String name, int opcodeValue, AbstractFormat fmt) {
+ this.opcode = opcode;
+ this.name = name;
+ this.value = opcodeValue;
+ this.format = fmt;
+ }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/ParameterAnnotation.java b/tools/dexfuzz/src/dexfuzz/rawdex/ParameterAnnotation.java
new file mode 100644
index 0000000..0db5efd
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/ParameterAnnotation.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class ParameterAnnotation implements RawDexObject {
+ public int methodIdx;
+ public Offset annotationsOff;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ methodIdx = file.readUInt();
+ annotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.writeUInt(methodIdx);
+ file.getOffsetTracker().tryToWriteOffset(annotationsOff, file, false /* ULEB128 */);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (kind == IndexUpdateKind.METHOD_ID && methodIdx >= insertedIdx) {
+ methodIdx++;
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/ProtoIdItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/ProtoIdItem.java
new file mode 100644
index 0000000..3ccc82f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/ProtoIdItem.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class ProtoIdItem implements RawDexObject {
+ public int shortyIdx;
+ public int returnTypeIdx;
+ public Offset parametersOff;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().getNewOffsettable(file, this);
+ shortyIdx = file.readUInt();
+ returnTypeIdx = file.readUInt();
+ parametersOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ file.writeUInt(shortyIdx);
+ file.writeUInt(returnTypeIdx);
+ file.getOffsetTracker().tryToWriteOffset(parametersOff, file, false /* ULEB128 */);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (kind == IndexUpdateKind.STRING_ID && shortyIdx >= insertedIdx) {
+ shortyIdx++;
+ }
+ if (kind == IndexUpdateKind.TYPE_ID && returnTypeIdx >= insertedIdx) {
+ returnTypeIdx++;
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/RawDexFile.java b/tools/dexfuzz/src/dexfuzz/rawdex/RawDexFile.java
new file mode 100644
index 0000000..483ed6c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/RawDexFile.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class RawDexFile implements RawDexObject {
+ private OffsetTracker offsetTracker;
+
+ public HeaderItem header;
+
+ public MapList mapList;
+
+ // Can be allocated after reading the header.
+ public List<StringIdItem> stringIds;
+ public List<TypeIdItem> typeIds;
+ public List<ProtoIdItem> protoIds;
+ public List<FieldIdItem> fieldIds;
+ public List<MethodIdItem> methodIds;
+ public List<ClassDefItem> classDefs;
+
+ // Need to be allocated later (will be allocated in MapList.java)
+ public List<StringDataItem> stringDatas;
+ public List<ClassDataItem> classDatas;
+ public List<TypeList> typeLists;
+ public List<CodeItem> codeItems;
+ public DebugInfoItem debugInfoItem;
+ public List<AnnotationsDirectoryItem> annotationsDirectoryItems;
+ public List<AnnotationSetRefList> annotationSetRefLists;
+ public List<AnnotationSetItem> annotationSetItems;
+ public List<AnnotationItem> annotationItems;
+ public List<EncodedArrayItem> encodedArrayItems;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ // Get a reference to the OffsetTracker, so that IdCreator can use it.
+ offsetTracker = file.getOffsetTracker();
+
+ file.seek(0);
+
+ // Read header.
+ (header = new HeaderItem()).read(file);
+
+ // We can allocate all of these now.
+ stringIds = new ArrayList<StringIdItem>(header.stringIdsSize);
+ typeIds = new ArrayList<TypeIdItem>(header.typeIdsSize);
+ protoIds = new ArrayList<ProtoIdItem>(header.protoIdsSize);
+ fieldIds = new ArrayList<FieldIdItem>(header.fieldIdsSize);
+ methodIds = new ArrayList<MethodIdItem>(header.methodIdsSize);
+ classDefs = new ArrayList<ClassDefItem>(header.classDefsSize);
+
+ mapList = new MapList(this);
+ mapList.read(file);
+
+ file.getOffsetTracker().associateOffsets();
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.seek(0);
+
+ // We read the header first, and then the map list, and then everything
+ // else. Therefore, when we get to the end of the header, tell OffsetTracker
+ // to skip past the map list offsets, and then when we get to the map list,
+ // tell OffsetTracker to skip back there, and then return to where it was previously.
+
+ // Update the map items' sizes first
+ // - but only update the items that we expect to have changed size.
+ // ALSO update the header's table sizes!
+ for (MapItem mapItem : mapList.mapItems) {
+ switch (mapItem.type) {
+ case MapItem.TYPE_STRING_ID_ITEM:
+ if (mapItem.size != stringIds.size()) {
+ Log.debug("Updating StringIDs List size: " + stringIds.size());
+ mapItem.size = stringIds.size();
+ header.stringIdsSize = stringIds.size();
+ }
+ break;
+ case MapItem.TYPE_STRING_DATA_ITEM:
+ if (mapItem.size != stringDatas.size()) {
+ Log.debug("Updating StringDatas List size: " + stringDatas.size());
+ mapItem.size = stringDatas.size();
+ }
+ break;
+ case MapItem.TYPE_METHOD_ID_ITEM:
+ if (mapItem.size != methodIds.size()) {
+ Log.debug("Updating MethodIDs List size: " + methodIds.size());
+ mapItem.size = methodIds.size();
+ header.methodIdsSize = methodIds.size();
+ }
+ break;
+ case MapItem.TYPE_FIELD_ID_ITEM:
+ if (mapItem.size != fieldIds.size()) {
+ Log.debug("Updating FieldIDs List size: " + fieldIds.size());
+ mapItem.size = fieldIds.size();
+ header.fieldIdsSize = fieldIds.size();
+ }
+ break;
+ case MapItem.TYPE_PROTO_ID_ITEM:
+ if (mapItem.size != protoIds.size()) {
+ Log.debug("Updating ProtoIDs List size: " + protoIds.size());
+ mapItem.size = protoIds.size();
+ header.protoIdsSize = protoIds.size();
+ }
+ break;
+ case MapItem.TYPE_TYPE_ID_ITEM:
+ if (mapItem.size != typeIds.size()) {
+ Log.debug("Updating TypeIDs List size: " + typeIds.size());
+ mapItem.size = typeIds.size();
+ header.typeIdsSize = typeIds.size();
+ }
+ break;
+ case MapItem.TYPE_TYPE_LIST:
+ if (mapItem.size != typeLists.size()) {
+ Log.debug("Updating TypeLists List size: " + typeLists.size());
+ mapItem.size = typeLists.size();
+ }
+ break;
+ default:
+ }
+ }
+
+ // Use the map list to write the file.
+ for (MapItem mapItem : mapList.mapItems) {
+ switch (mapItem.type) {
+ case MapItem.TYPE_HEADER_ITEM:
+ header.write(file);
+ file.getOffsetTracker().skipToAfterMapList();
+ break;
+ case MapItem.TYPE_STRING_ID_ITEM:
+ if (mapItem.size != stringIds.size()) {
+ Log.errorAndQuit("MapItem's size " + mapItem.size
+ + " no longer matches StringIDs table size " + stringIds.size());
+ }
+ for (StringIdItem stringId : stringIds) {
+ stringId.write(file);
+ }
+ break;
+ case MapItem.TYPE_TYPE_ID_ITEM:
+ if (mapItem.size != typeIds.size()) {
+ Log.errorAndQuit("MapItem's size " + mapItem.size
+ + " no longer matches TypeIDs table size " + typeIds.size());
+ }
+ for (TypeIdItem typeId : typeIds) {
+ typeId.write(file);
+ }
+ break;
+ case MapItem.TYPE_PROTO_ID_ITEM:
+ if (mapItem.size != protoIds.size()) {
+ Log.errorAndQuit("MapItem's size " + mapItem.size
+ + " no longer matches ProtoIDs table size " + protoIds.size());
+ }
+ for (ProtoIdItem protoId : protoIds) {
+ protoId.write(file);
+ }
+ break;
+ case MapItem.TYPE_FIELD_ID_ITEM:
+ if (mapItem.size != fieldIds.size()) {
+ Log.errorAndQuit("MapItem's size " + mapItem.size
+ + " no longer matches FieldIDs table size " + fieldIds.size());
+ }
+ for (FieldIdItem fieldId : fieldIds) {
+ fieldId.write(file);
+ }
+ break;
+ case MapItem.TYPE_METHOD_ID_ITEM:
+ if (mapItem.size != methodIds.size()) {
+ Log.errorAndQuit("MapItem's size " + mapItem.size
+ + " no longer matches MethodIDs table size " + methodIds.size());
+ }
+ for (MethodIdItem methodId : methodIds) {
+ methodId.write(file);
+ }
+ break;
+ case MapItem.TYPE_CLASS_DEF_ITEM:
+ if (mapItem.size != classDefs.size()) {
+ Log.errorAndQuit("MapItem's size " + mapItem.size
+ + " no longer matches ClassDefs table size " + classDefs.size());
+ }
+ for (ClassDefItem classDef : classDefs) {
+ classDef.write(file);
+ }
+ break;
+ case MapItem.TYPE_MAP_LIST:
+ file.getOffsetTracker().goBackToMapList();
+ mapList.write(file);
+ file.getOffsetTracker().goBackToPreviousPoint();
+ break;
+ case MapItem.TYPE_TYPE_LIST:
+ if (mapItem.size != typeLists.size()) {
+ Log.errorAndQuit("MapItem's size " + mapItem.size
+ + " no longer matches TypeLists table size " + typeLists.size());
+ }
+ for (TypeList typeList : typeLists) {
+ typeList.write(file);
+ }
+ break;
+ case MapItem.TYPE_ANNOTATION_SET_REF_LIST:
+ if (mapItem.size != annotationSetRefLists.size()) {
+ Log.errorAndQuit("MapItem's size " + mapItem.size
+ + " no longer matches AnnotationSetRefLists table size "
+ + annotationSetRefLists.size());
+ }
+ for (AnnotationSetRefList annotationSetRefList : annotationSetRefLists) {
+ annotationSetRefList.write(file);
+ }
+ break;
+ case MapItem.TYPE_ANNOTATION_SET_ITEM:
+ if (mapItem.size != annotationSetItems.size()) {
+ Log.errorAndQuit("MapItem's size " + mapItem.size
+ + " no longer matches AnnotationSetItems table size "
+ + annotationSetItems.size());
+ }
+ for (AnnotationSetItem annotationSetItem : annotationSetItems) {
+ annotationSetItem.write(file);
+ }
+ break;
+ case MapItem.TYPE_CLASS_DATA_ITEM:
+ if (mapItem.size != classDatas.size()) {
+ Log.errorAndQuit("MapItem's size " + mapItem.size
+ + " no longer matches ClassDataItems table size " + classDatas.size());
+ }
+ for (ClassDataItem classData : classDatas) {
+ classData.write(file);
+ }
+ break;
+ case MapItem.TYPE_CODE_ITEM:
+ if (mapItem.size != codeItems.size()) {
+ Log.errorAndQuit("MapItem's size " + mapItem.size
+ + " no longer matches CodeItems table size " + codeItems.size());
+ }
+ for (CodeItem codeItem : codeItems) {
+ codeItem.write(file);
+ }
+ break;
+ case MapItem.TYPE_STRING_DATA_ITEM:
+ if (mapItem.size != stringDatas.size()) {
+ Log.errorAndQuit("MapItem's size " + mapItem.size
+ + " no longer matches StringDataItems table size "
+ + stringDatas.size());
+ }
+ for (StringDataItem stringDataItem : stringDatas) {
+ stringDataItem.write(file);
+ }
+ break;
+ case MapItem.TYPE_DEBUG_INFO_ITEM:
+ debugInfoItem.write(file);
+ break;
+ case MapItem.TYPE_ANNOTATION_ITEM:
+ if (mapItem.size != annotationItems.size()) {
+ Log.errorAndQuit("MapItem's size " + mapItem.size
+ + " no longer matches AnnotationItems table size "
+ + annotationItems.size());
+ }
+ for (AnnotationItem annotationItem : annotationItems) {
+ annotationItem.write(file);
+ }
+ break;
+ case MapItem.TYPE_ENCODED_ARRAY_ITEM:
+ if (mapItem.size != encodedArrayItems.size()) {
+ Log.errorAndQuit("MapItem's size " + mapItem.size
+ + " no longer matches EncodedArrayItems table size "
+ + encodedArrayItems.size());
+ }
+ for (EncodedArrayItem encodedArrayItem : encodedArrayItems) {
+ encodedArrayItem.write(file);
+ }
+ break;
+ case MapItem.TYPE_ANNOTATIONS_DIRECTORY_ITEM:
+ if (mapItem.size != annotationsDirectoryItems.size()) {
+ Log.errorAndQuit("MapItem's size " + mapItem.size
+ + " no longer matches AnnotationDirectoryItems table size "
+ + annotationsDirectoryItems.size());
+ }
+ for (AnnotationsDirectoryItem annotationsDirectory : annotationsDirectoryItems) {
+ annotationsDirectory.write(file);
+ }
+ break;
+ default:
+ Log.errorAndQuit("Encountered unknown map item in map item list.");
+ }
+ }
+
+ file.getOffsetTracker().updateOffsets(file);
+ }
+
+ /**
+ * Given a DexRandomAccessFile, calculate the correct adler32 checksum for it.
+ */
+ private int calculateAdler32Checksum(DexRandomAccessFile file) throws IOException {
+ // Skip magic + checksum.
+ file.seek(12);
+ int a = 1;
+ int b = 0;
+ while (file.getFilePointer() < file.length()) {
+ a = (a + file.readUnsignedByte()) % 65521;
+ b = (b + a) % 65521;
+ }
+ return (b << 16) | a;
+ }
+
+ /**
+ * Given a DexRandomAccessFile, update the file size, data size, and checksum.
+ */
+ public void updateHeader(DexRandomAccessFile file) throws IOException {
+ // File size must be updated before checksum.
+ int newFileSize = (int) file.length();
+ file.seek(32);
+ file.writeUInt(newFileSize);
+
+ // Data size must be updated before checksum.
+ int newDataSize = newFileSize - header.dataOff.getNewPositionOfItem();
+ file.seek(104);
+ file.writeUInt(newDataSize);
+
+ // Now update the checksum.
+ int newChecksum = calculateAdler32Checksum(file);
+ file.seek(8);
+ file.writeUInt(newChecksum);
+
+ header.fileSize = newFileSize;
+ header.dataSize = newDataSize;
+ header.checksum = newChecksum;
+ }
+
+ /**
+ * This should only be called from NewItemCreator.
+ */
+ public OffsetTracker getOffsetTracker() {
+ return offsetTracker;
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ for (TypeIdItem typeId : typeIds) {
+ typeId.incrementIndex(kind, insertedIdx);
+ }
+ for (ProtoIdItem protoId : protoIds) {
+ protoId.incrementIndex(kind, insertedIdx);
+ }
+ for (FieldIdItem fieldId : fieldIds) {
+ fieldId.incrementIndex(kind, insertedIdx);
+ }
+ for (MethodIdItem methodId : methodIds) {
+ methodId.incrementIndex(kind, insertedIdx);
+ }
+ for (ClassDefItem classDef : classDefs) {
+ classDef.incrementIndex(kind, insertedIdx);
+ }
+ for (ClassDataItem classData : classDatas) {
+ classData.incrementIndex(kind, insertedIdx);
+ }
+ if (typeLists != null) {
+ for (TypeList typeList : typeLists) {
+ typeList.incrementIndex(kind, insertedIdx);
+ }
+ }
+ for (CodeItem codeItem : codeItems) {
+ codeItem.incrementIndex(kind, insertedIdx);
+ }
+ if (annotationsDirectoryItems != null) {
+ for (AnnotationsDirectoryItem annotationsDirectoryItem : annotationsDirectoryItems) {
+ annotationsDirectoryItem.incrementIndex(kind, insertedIdx);
+ }
+ }
+ if (annotationItems != null) {
+ for (AnnotationItem annotationItem : annotationItems) {
+ annotationItem.incrementIndex(kind, insertedIdx);
+ }
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/RawDexObject.java b/tools/dexfuzz/src/dexfuzz/rawdex/RawDexObject.java
new file mode 100644
index 0000000..0b67e9c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/RawDexObject.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+/**
+ * Base class for any data structure that we may read or write from a DEX file.
+ */
+public interface RawDexObject {
+ /**
+ * Populate information for this DEX data from the file.
+ * @param file Input file, should already be "seeked" to the correct position.
+ * @throws IOException If there's a problem writing to the file.
+ */
+ public void read(DexRandomAccessFile file) throws IOException;
+
+ /**
+ * Write information for this DEX data to the file.
+ * @param file Output file, should already be "seeked" to the correct position.
+ * @throws IOException If there's a problem writing to the file.
+ */
+ public void write(DexRandomAccessFile file) throws IOException;
+
+ public static enum IndexUpdateKind {
+ STRING_ID,
+ TYPE_ID,
+ PROTO_ID,
+ FIELD_ID,
+ METHOD_ID
+ }
+
+ /**
+ * When we insert a new string, type, proto, field or method into the DEX file,
+ * this must be called. We may have inserted something into the middle of a table,
+ * so any indices pointing afterwards must be updated.
+ */
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx);
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/StringDataItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/StringDataItem.java
new file mode 100644
index 0000000..87c603e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/StringDataItem.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+public class StringDataItem implements RawDexObject {
+ private int size;
+ private String data;
+ private byte[] dataAsBytes;
+ private boolean writeRawBytes;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().getNewOffsettable(file, this);
+ size = file.readUleb128();
+ if (size != 0) {
+ dataAsBytes = file.readDexUtf(size);
+ data = new String(dataAsBytes, StandardCharsets.US_ASCII);
+ if (size != data.length()) {
+ Log.warn("Don't have full support for decoding MUTF-8 yet, DEX file "
+ + "may be incorrectly mutated. Avoid using this test case for now.");
+ writeRawBytes = true;
+ }
+ } else {
+ // Read past the null byte.
+ file.readByte();
+ }
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ file.writeUleb128(size);
+ if (size > 0) {
+ if (writeRawBytes) {
+ file.writeDexUtf(dataAsBytes);
+ } else {
+ file.writeDexUtf(data.getBytes(StandardCharsets.US_ASCII));
+ }
+ } else {
+ // Write out the null byte.
+ file.writeByte(0);
+ }
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ // Do nothing.
+ }
+
+ public void setSize(int size) {
+ this.size = size;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public void setString(String data) {
+ this.data = data;
+ }
+
+ public String getString() {
+ if (writeRawBytes) {
+ Log.warn("Reading a string that hasn't been properly decoded! Returning empty string.");
+ return "";
+ }
+ return data;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/StringIdItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/StringIdItem.java
new file mode 100644
index 0000000..da8c294
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/StringIdItem.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class StringIdItem implements RawDexObject {
+ public Offset stringDataOff;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().getNewOffsettable(file, this);
+ stringDataOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ file.getOffsetTracker().tryToWriteOffset(stringDataOff, file, false /* ULEB128 */);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ // Do nothing.
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/TryItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/TryItem.java
new file mode 100644
index 0000000..99be6de
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/TryItem.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class TryItem implements RawDexObject {
+ public int startAddr;
+ public short insnCount;
+ public short handlerOff; // Not a global offset; don't need to adjust like an Offset.
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ startAddr = file.readUInt();
+ insnCount = file.readUShort();
+ handlerOff = file.readUShort();
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.writeUInt(startAddr);
+ file.writeUShort(insnCount);
+ file.writeUShort(handlerOff);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ // Do nothing.
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/TypeIdItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/TypeIdItem.java
new file mode 100644
index 0000000..bb3eff1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/TypeIdItem.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class TypeIdItem implements RawDexObject {
+ public int descriptorIdx;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().getNewOffsettable(file, this);
+ descriptorIdx = file.readUInt();
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ file.writeUInt(descriptorIdx);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (kind == IndexUpdateKind.STRING_ID && descriptorIdx >= insertedIdx) {
+ descriptorIdx++;
+ }
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/TypeItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/TypeItem.java
new file mode 100644
index 0000000..a788b47
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/TypeItem.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class TypeItem implements RawDexObject {
+ public short typeIdx;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ typeIdx = file.readUShort();
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.writeUShort(typeIdx);
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ if (kind == IndexUpdateKind.TYPE_ID && typeIdx >= insertedIdx) {
+ typeIdx++;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/TypeList.java b/tools/dexfuzz/src/dexfuzz/rawdex/TypeList.java
new file mode 100644
index 0000000..90a2b57
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/TypeList.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class TypeList implements RawDexObject {
+ public int size;
+ public TypeItem[] list;
+
+ @Override
+ public void read(DexRandomAccessFile file) throws IOException {
+ file.alignForwards(4);
+ file.getOffsetTracker().getNewOffsettable(file, this);
+ size = file.readUInt();
+ list = new TypeItem[size];
+ for (int i = 0; i < size; i++) {
+ (list[i] = new TypeItem()).read(file);
+ }
+ }
+
+ @Override
+ public void write(DexRandomAccessFile file) throws IOException {
+ file.alignForwards(4);
+ file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+ file.writeUInt(size);
+ for (TypeItem typeItem : list) {
+ typeItem.write(file);
+ }
+ }
+
+ @Override
+ public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+ for (TypeItem type : list) {
+ type.incrementIndex(kind, insertedIdx);
+ }
+ }
+
+ /**
+ * Returns if this TypeList comes before the provided TypeList, considering the legal
+ * ordering of TypeLists in DEX files.
+ */
+ public boolean comesBefore(TypeList other) {
+ int checkSize = Math.min(size, other.size);
+ for (int i = 0; i < checkSize; i++) {
+ if (list[i].typeIdx < other.list[i].typeIdx) {
+ return true;
+ } else if (list[i].typeIdx > other.list[i].typeIdx) {
+ return false;
+ }
+ }
+ if (size == other.size) {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/AbstractFormat.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/AbstractFormat.java
new file mode 100644
index 0000000..29b15ae
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/AbstractFormat.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+/**
+ * Every Format subclasses this AbstractFormat. The subclasses then implement these
+ * methods to write out a provided Instruction according to this format, and also methods
+ * to read the vregs from an Instruction's raw bytes.
+ * Hierarchy is as follows:
+ * AbstractFormat
+ * |____________Format1
+ * | |_____Format10t
+ * | |_____Format10x
+ * | |_____Format11n
+ * | |_____Format11x
+ * | |_____Format12x
+ * |____________Format2
+ * | |_____Format20bc
+ * | |_____Format20t
+ * etc...
+ */
+public abstract class AbstractFormat {
+ /**
+ * Get the size of an Instruction that has this format.
+ */
+ public abstract int getSize();
+
+ /**
+ * Given a file handle and an instruction, write that Instruction out to the file
+ * correctly, considering the current format.
+ */
+ public abstract void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException;
+
+ /**
+ * Read the value of vA, considering this format.
+ */
+ public abstract long getA(byte[] raw) throws IOException;
+
+ /**
+ * Read the value of vB, considering this format.
+ */
+ public abstract long getB(byte[] raw) throws IOException;
+
+ /**
+ * Read the value of vC, considering this format.
+ */
+ public abstract long getC(byte[] raw) throws IOException;
+
+ /**
+ * Only Format35c should return true for this.
+ */
+ public abstract boolean needsInvokeFormatInfo();
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsConst.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsConst.java
new file mode 100644
index 0000000..e2b164a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsConst.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.Instruction;
+
+/**
+ * Every Format that contains a value that is a constant (that includes instructions like
+ * const/4, but also add-int/lit8) should implement this interface, to allow the constant
+ * part of a provided Instruction to be read and set correctly.
+ */
+public interface ContainsConst {
+ public long getConst(Instruction insn);
+
+ public void setConst(Instruction insn, long constant);
+
+ public long getConstRange();
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsPoolIndex.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsPoolIndex.java
new file mode 100644
index 0000000..5f66b0e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsPoolIndex.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.OpcodeInfo;
+
+/**
+ * Every Format that contains a value that is an index into the ID tables of this DEX file
+ * should implement this interface, to allow the index part of a provided Instruction
+ * to be read and set correctly.
+ */
+public interface ContainsPoolIndex {
+ public enum PoolIndexKind {
+ Type,
+ Field,
+ String,
+ Method,
+ Invalid
+ }
+
+ public int getPoolIndex(Instruction insn);
+
+ public void setPoolIndex(Instruction insn, int poolIndex);
+
+ public PoolIndexKind getPoolIndexKind(OpcodeInfo info);
+}
diff --git a/runtime/arch/arm64/portable_entrypoints_arm64.S b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsTarget.java
similarity index 62%
copy from runtime/arch/arm64/portable_entrypoints_arm64.S
copy to tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsTarget.java
index 9e2c030..bb24e61 100644
--- a/runtime/arch/arm64/portable_entrypoints_arm64.S
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsTarget.java
@@ -14,17 +14,16 @@
* limitations under the License.
*/
-#include "asm_support_arm64.S"
+package dexfuzz.rawdex.formats;
- /*
- * Portable invocation stub.
- */
-UNIMPLEMENTED art_portable_invoke_stub
+import dexfuzz.rawdex.Instruction;
-UNIMPLEMENTED art_portable_proxy_invoke_handler
+/**
+ * Every Format that contains an offset to a target instruction
+ * should implement this interface, to allow the offset to be read and set correctly.
+ */
+public interface ContainsTarget {
+ public long getTarget(Instruction insn);
-UNIMPLEMENTED art_portable_resolution_trampoline
-
-UNIMPLEMENTED art_portable_to_interpreter_bridge
-
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
+ public void setTarget(Instruction insn, long target);
+}
diff --git a/runtime/arch/arm64/portable_entrypoints_arm64.S b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsVRegs.java
similarity index 65%
copy from runtime/arch/arm64/portable_entrypoints_arm64.S
copy to tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsVRegs.java
index 9e2c030..40ba5ac 100644
--- a/runtime/arch/arm64/portable_entrypoints_arm64.S
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsVRegs.java
@@ -14,17 +14,12 @@
* limitations under the License.
*/
-#include "asm_support_arm64.S"
+package dexfuzz.rawdex.formats;
- /*
- * Portable invocation stub.
- */
-UNIMPLEMENTED art_portable_invoke_stub
-
-UNIMPLEMENTED art_portable_proxy_invoke_handler
-
-UNIMPLEMENTED art_portable_resolution_trampoline
-
-UNIMPLEMENTED art_portable_to_interpreter_bridge
-
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
+/**
+ * Every Format that contains virtual registers should implement this interface,
+ * to allow the number of virtual registers specified by the format to be found.
+ */
+public interface ContainsVRegs {
+ public int getVRegCount();
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format00x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format00x.java
new file mode 100644
index 0000000..aae7469
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format00x.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format00x extends AbstractFormat {
+ @Override
+ public int getSize() {
+ return 0;
+ }
+
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public boolean needsInvokeFormatInfo() {
+ return false;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format1.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format1.java
new file mode 100644
index 0000000..e503513
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format1.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format1 extends AbstractFormat {
+ @Override
+ public int getSize() {
+ return 1;
+ }
+
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public boolean needsInvokeFormatInfo() {
+ return false;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10t.java
new file mode 100644
index 0000000..a9e13f1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10t.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format10t extends Format1 implements ContainsTarget {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) insn.vregA);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getSignedByteFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public long getTarget(Instruction insn) {
+ return insn.vregA;
+ }
+
+ @Override
+ public void setTarget(Instruction insn, long target) {
+ insn.vregA = target;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10x.java
new file mode 100644
index 0000000..aabf725
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10x.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format10x extends Format1 {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) 0); // padding
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11n.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11n.java
new file mode 100644
index 0000000..4b8c35c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11n.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format11n extends Format1 implements ContainsConst, ContainsVRegs {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) (insn.vregA | (insn.vregB << 4)));
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public long getConst(Instruction insn) {
+ return insn.vregB;
+ }
+
+ @Override
+ public void setConst(Instruction insn, long constant) {
+ insn.vregB = constant;
+ }
+
+ @Override
+ public long getConstRange() {
+ return (1 << 4);
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 1;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11x.java
new file mode 100644
index 0000000..e8963f1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11x.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format11x extends Format1 implements ContainsVRegs {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) insn.vregA);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 1;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format12x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format12x.java
new file mode 100644
index 0000000..170ebe1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format12x.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format12x extends Format1 implements ContainsVRegs {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) (insn.vregA | (insn.vregB << 4)));
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 2;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format2.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format2.java
new file mode 100644
index 0000000..5ddc124
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format2.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format2 extends AbstractFormat {
+ @Override
+ public int getSize() {
+ return 2;
+ }
+
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public boolean needsInvokeFormatInfo() {
+ return false;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20bc.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20bc.java
new file mode 100644
index 0000000..4f21489
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20bc.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+// NB: This format is only used for statically determined verification errors,
+// so we shouldn't encounter it in ART. (Or even before, as they are only written in during
+// verification, which comes after our fuzzing...)
+// Therefore, no need to say this implements ContainsPoolIndex, even though it is a *c format
+public class Format20bc extends Format2 {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) insn.vregA);
+ file.writeUShort((short) insn.vregB);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 1);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20t.java
new file mode 100644
index 0000000..1e33c15
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20t.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format20t extends Format2 implements ContainsTarget {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) 0); // padding
+ file.writeUShort((short) insn.vregA);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public long getTarget(Instruction insn) {
+ return insn.vregA;
+ }
+
+ @Override
+ public void setTarget(Instruction insn, long target) {
+ insn.vregA = target;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21c.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21c.java
new file mode 100644
index 0000000..e60b54a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21c.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format21c extends Format2 implements ContainsVRegs, ContainsPoolIndex {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) insn.vregA);
+ file.writeUShort((short) insn.vregB);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 1;
+ }
+
+ @Override
+ public int getPoolIndex(Instruction insn) {
+ return (int) insn.vregB;
+ }
+
+ @Override
+ public void setPoolIndex(Instruction insn, int poolIndex) {
+ insn.vregB = poolIndex;
+ }
+
+ @Override
+ public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+ if (info.opcode == Opcode.CONST_STRING) {
+ return PoolIndexKind.String;
+ }
+ if (info.opcode == Opcode.CONST_CLASS
+ || info.opcode == Opcode.CHECK_CAST
+ || info.opcode == Opcode.NEW_INSTANCE) {
+ return PoolIndexKind.Type;
+ }
+ // everything else is static field accesses
+ return PoolIndexKind.Field;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21h.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21h.java
new file mode 100644
index 0000000..c279f6c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21h.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format21h extends Format2 implements ContainsConst, ContainsVRegs {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) insn.vregA);
+ file.writeUShort((short) insn.vregB);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public long getConst(Instruction insn) {
+ return insn.vregB;
+ }
+
+ @Override
+ public void setConst(Instruction insn, long constant) {
+ insn.vregB = constant;
+ }
+
+ @Override
+ public long getConstRange() {
+ return (1 << 16);
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 1;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21s.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21s.java
new file mode 100644
index 0000000..594d1a7
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21s.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format21s extends Format2 implements ContainsConst, ContainsVRegs {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) insn.vregA);
+ file.writeUShort((short) insn.vregB);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public long getConst(Instruction insn) {
+ return insn.vregB;
+ }
+
+ @Override
+ public void setConst(Instruction insn, long constant) {
+ insn.vregB = constant;
+ }
+
+ @Override
+ public long getConstRange() {
+ return (1 << 16);
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 1;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21t.java
new file mode 100644
index 0000000..dc3d659
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21t.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format21t extends Format2 implements ContainsTarget, ContainsVRegs {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) insn.vregA);
+ file.writeUShort((short) insn.vregB);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public long getTarget(Instruction insn) {
+ return insn.vregB;
+ }
+
+ @Override
+ public void setTarget(Instruction insn, long target) {
+ insn.vregB = target;
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 1;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22b.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22b.java
new file mode 100644
index 0000000..bbdc7e3
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22b.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format22b extends Format2 implements ContainsVRegs, ContainsConst {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) insn.vregA);
+ file.writeByte((byte) insn.vregB);
+ file.writeByte((byte) insn.vregC);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 2);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return RawInsnHelper.getSignedByteFromByte(raw, 3);
+ }
+
+ @Override
+ public long getConst(Instruction insn) {
+ return insn.vregC;
+ }
+
+ @Override
+ public void setConst(Instruction insn, long constant) {
+ insn.vregC = constant;
+ }
+
+ @Override
+ public long getConstRange() {
+ return (1 << 8);
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 2;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22c.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22c.java
new file mode 100644
index 0000000..4dff336
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22c.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format22c extends Format2 implements ContainsVRegs, ContainsPoolIndex {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) (insn.vregA | (insn.vregB << 4)));
+ file.writeUShort((short) insn.vregC);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 2;
+ }
+
+ @Override
+ public int getPoolIndex(Instruction insn) {
+ return (int) insn.vregC;
+ }
+
+ @Override
+ public void setPoolIndex(Instruction insn, int poolIndex) {
+ insn.vregC = poolIndex;
+ }
+
+ @Override
+ public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+ if (info.opcode == Opcode.INSTANCE_OF || info.opcode == Opcode.NEW_ARRAY) {
+ return PoolIndexKind.Type;
+ }
+ return PoolIndexKind.Field;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22cs.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22cs.java
new file mode 100644
index 0000000..b66e14c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22cs.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format22cs extends Format2 implements ContainsVRegs, ContainsPoolIndex {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ throw new Error("Did not expect to have to write a 22cs instruction!");
+ // If for some reason 22cs instructions were in DEX files in the future, uncomment:
+ //file.writeByte((byte) insn.info.value);
+ //file.writeByte((byte) (insn.vreg_a | (insn.vreg_b << 4)));
+ //file.writeUShort((short) insn.vreg_c);
+ //return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 2;
+ }
+
+ @Override
+ public int getPoolIndex(Instruction insn) {
+ return (int) insn.vregC;
+ }
+
+ @Override
+ public void setPoolIndex(Instruction insn, int poolIndex) {
+ insn.vregC = poolIndex;
+ }
+
+ @Override
+ public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+ // This should technically be a byte offset, but we should never encounter
+ // this format during DEX mutation anyway. (see writeToFile())
+ return PoolIndexKind.Field;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22s.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22s.java
new file mode 100644
index 0000000..9497179
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22s.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format22s extends Format2 implements ContainsVRegs, ContainsConst {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) (insn.vregA | (insn.vregB << 4)));
+ file.writeUShort((short) insn.vregC);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+ }
+
+ @Override
+ public long getConst(Instruction insn) {
+ return insn.vregC;
+ }
+
+ @Override
+ public void setConst(Instruction insn, long constant) {
+ insn.vregC = constant;
+ }
+
+ @Override
+ public long getConstRange() {
+ return (1 << 16);
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 2;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22t.java
new file mode 100644
index 0000000..5ea9579
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22t.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format22t extends Format2 implements ContainsTarget, ContainsVRegs {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) (insn.vregA | (insn.vregB << 4)));
+ file.writeUShort((short) insn.vregC);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+ }
+
+ @Override
+ public long getTarget(Instruction insn) {
+ return insn.vregC;
+ }
+
+ @Override
+ public void setTarget(Instruction insn, long target) {
+ insn.vregC = target;
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 2;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22x.java
new file mode 100644
index 0000000..5cd3d73
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22x.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format22x extends Format2 implements ContainsVRegs {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) insn.vregA);
+ file.writeUShort((short) insn.vregB);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 2;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format23x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format23x.java
new file mode 100644
index 0000000..8ce7c7c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format23x.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format23x extends Format2 implements ContainsVRegs {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) insn.vregA);
+ file.writeByte((byte) insn.vregB);
+ file.writeByte((byte) insn.vregC);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 2);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 3);
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 3;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3.java
new file mode 100644
index 0000000..d9d7ce1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format3 extends AbstractFormat {
+ @Override
+ public int getSize() {
+ return 3;
+ }
+
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public boolean needsInvokeFormatInfo() {
+ return false;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format30t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format30t.java
new file mode 100644
index 0000000..0b62646
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format30t.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format30t extends Format3 implements ContainsTarget {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) 0); // padding
+ file.writeUInt((int) insn.vregA);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getSignedIntFromFourBytes(raw, 2);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public long getTarget(Instruction insn) {
+ return insn.vregA;
+ }
+
+ @Override
+ public void setTarget(Instruction insn, long target) {
+ insn.vregA = target;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31c.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31c.java
new file mode 100644
index 0000000..435fa19
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31c.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format31c extends Format3 implements ContainsVRegs, ContainsPoolIndex {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) insn.vregA);
+ file.writeUInt((int) insn.vregB);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedIntFromFourBytes(raw, 2);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 1;
+ }
+
+ @Override
+ public int getPoolIndex(Instruction insn) {
+ return (int) insn.vregB;
+ }
+
+ @Override
+ public void setPoolIndex(Instruction insn, int poolIndex) {
+ insn.vregB = poolIndex;
+ }
+
+ @Override
+ public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+ return PoolIndexKind.String;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31i.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31i.java
new file mode 100644
index 0000000..d54c074
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31i.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format31i extends Format3 implements ContainsConst, ContainsVRegs {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) insn.vregA);
+ file.writeUInt((int) insn.vregB);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getSignedIntFromFourBytes(raw, 2);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public long getConst(Instruction insn) {
+ return insn.vregB;
+ }
+
+ @Override
+ public void setConst(Instruction insn, long constant) {
+ insn.vregB = constant;
+ }
+
+ @Override
+ public long getConstRange() {
+ return (1L << 32);
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 1;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31t.java
new file mode 100644
index 0000000..b74db8f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31t.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format31t extends Format3 implements ContainsTarget, ContainsVRegs {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) insn.vregA);
+ file.writeUInt((int) insn.vregB);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getSignedIntFromFourBytes(raw, 2);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public long getTarget(Instruction insn) {
+ return insn.vregB;
+ }
+
+ @Override
+ public void setTarget(Instruction insn, long target) {
+ insn.vregB = target;
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 1;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format32x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format32x.java
new file mode 100644
index 0000000..2f46105
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format32x.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format32x extends Format3 implements ContainsVRegs {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) 0); // padding
+ file.writeUShort((short) insn.vregA);
+ file.writeUShort((short) insn.vregB);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 4);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 2;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35c.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35c.java
new file mode 100644
index 0000000..e4a50ff
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35c.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format35c extends Format3 implements ContainsPoolIndex {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) (insn.invokeFormatInfo.vregG | (insn.vregA << 4)));
+ file.writeUShort((short) insn.vregB);
+ file.writeByte((byte) ((insn.invokeFormatInfo.vregD << 4) | insn.vregC));
+ file.writeByte((byte) ((insn.invokeFormatInfo.vregF << 4)
+ | insn.invokeFormatInfo.vregE));
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 4);
+ }
+
+ @Override
+ public boolean needsInvokeFormatInfo() {
+ return true;
+ }
+
+ @Override
+ public int getPoolIndex(Instruction insn) {
+ return (int) insn.vregB;
+ }
+
+ @Override
+ public void setPoolIndex(Instruction insn, int poolIndex) {
+ insn.vregB = poolIndex;
+ }
+
+ @Override
+ public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+ if (info.opcode == Opcode.FILLED_NEW_ARRAY) {
+ return PoolIndexKind.Type;
+ }
+ return PoolIndexKind.Method;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35mi.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35mi.java
new file mode 100644
index 0000000..f622385
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35mi.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format35mi extends Format3 {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) (insn.invokeFormatInfo.vregG | (insn.vregA << 4)));
+ file.writeUShort((short) insn.vregB);
+ file.writeByte((byte) ((insn.invokeFormatInfo.vregD << 4) | insn.vregC));
+ file.writeByte((byte) ((insn.invokeFormatInfo.vregF << 4)
+ | insn.invokeFormatInfo.vregE));
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 4);
+ }
+
+ @Override
+ public boolean needsInvokeFormatInfo() {
+ return true;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35ms.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35ms.java
new file mode 100644
index 0000000..3be9707
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35ms.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format35ms extends Format3 {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) (insn.invokeFormatInfo.vregG | (insn.vregA << 4)));
+ file.writeUShort((short) insn.vregB);
+ file.writeByte((byte) ((insn.invokeFormatInfo.vregD << 4) | insn.vregC));
+ file.writeByte((byte) ((insn.invokeFormatInfo.vregF << 4)
+ | insn.invokeFormatInfo.vregE));
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 4);
+ }
+
+ @Override
+ public boolean needsInvokeFormatInfo() {
+ return true;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rc.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rc.java
new file mode 100644
index 0000000..630825d
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rc.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format3rc extends Format3 implements ContainsPoolIndex {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) insn.vregA);
+ file.writeUShort((short) insn.vregB);
+ file.writeUShort((short) insn.vregC);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 4);
+ }
+
+ @Override
+ public int getPoolIndex(Instruction insn) {
+ return (int) insn.vregB;
+ }
+
+ @Override
+ public void setPoolIndex(Instruction insn, int poolIndex) {
+ insn.vregB = poolIndex;
+ }
+
+ @Override
+ public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+ if (info.opcode == Opcode.FILLED_NEW_ARRAY_RANGE) {
+ return PoolIndexKind.Type;
+ }
+ return PoolIndexKind.Method;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rmi.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rmi.java
new file mode 100644
index 0000000..7b6ceea
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rmi.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format3rmi extends Format3 {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) insn.vregA);
+ file.writeUShort((short) insn.vregB);
+ file.writeUShort((short) insn.vregC);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 4);
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rms.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rms.java
new file mode 100644
index 0000000..17535f9
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rms.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format3rms extends Format3 {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) insn.vregA);
+ file.writeUShort((short) insn.vregB);
+ file.writeUShort((short) insn.vregC);
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 4);
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format5.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format5.java
new file mode 100644
index 0000000..bc141be
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format5.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format5 extends AbstractFormat {
+ @Override
+ public int getSize() {
+ return 5;
+ }
+
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public boolean needsInvokeFormatInfo() {
+ return false;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format51l.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format51l.java
new file mode 100644
index 0000000..fc2e0ed
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format51l.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format51l extends Format5 implements ContainsConst, ContainsVRegs {
+ @Override
+ public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+ file.writeByte((byte) insn.info.value);
+ file.writeByte((byte) insn.vregA);
+ file.writeUShort((short) (insn.vregB & 0xffff));
+ file.writeUShort((short) ((insn.vregB & 0xffff0000) >> 16));
+ file.writeUShort((short) ((insn.vregB & 0xffff00000000L) >> 32));
+ file.writeUShort((short) ((insn.vregB & 0xffff000000000000L) >> 48));
+ return;
+ }
+
+ @Override
+ public long getA(byte[] raw) throws IOException {
+ return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+ }
+
+ @Override
+ public long getB(byte[] raw) throws IOException {
+ return RawInsnHelper.getSignedLongFromEightBytes(raw, 2);
+ }
+
+ @Override
+ public long getC(byte[] raw) throws IOException {
+ return (long) 0;
+ }
+
+ @Override
+ public long getConst(Instruction insn) {
+ return insn.vregB;
+ }
+
+ @Override
+ public void setConst(Instruction insn, long constant) {
+ insn.vregB = constant;
+ }
+
+ @Override
+ public long getConstRange() {
+ return (1L << 63);
+ }
+
+ @Override
+ public int getVRegCount() {
+ return 1;
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/RawInsnHelper.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/RawInsnHelper.java
new file mode 100644
index 0000000..b16a1b5
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/RawInsnHelper.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+/**
+ * Class consisting of static methods used for common read/write operations
+ * perfomed in the Format classes.
+ */
+public class RawInsnHelper {
+ /**
+ * Read a signed byte from the idx into the raw array.
+ */
+ public static long getSignedByteFromByte(byte[] raw, int idx) {
+ return (long) raw[idx];
+ }
+
+ /**
+ * Read an unsigned byte from the idx into the raw array.
+ */
+ public static long getUnsignedByteFromByte(byte[] raw, int idx) {
+ return ((long) raw[idx]) & 0xff;
+ }
+
+ /**
+ * Read an unsigned lower 4 bits from the idx into the raw array.
+ */
+ public static long getUnsignedLowNibbleFromByte(byte[] raw, int idx) {
+ return ((long) raw[idx]) & 0xf;
+ }
+
+ /**
+ * Read an unsigned higher 4 bits from the idx into the raw array.
+ */
+ public static long getUnsignedHighNibbleFromByte(byte[] raw, int idx) {
+ return (((long) raw[idx]) >> 4) & 0xf;
+ }
+
+ /**
+ * Read an unsigned 2 bytes as a short from the idx into the raw array.
+ */
+ public static long getUnsignedShortFromTwoBytes(byte[] raw, int idx) {
+ return (long) ( (((long) raw[idx]) & 0xff)
+ | ((((long) raw[idx + 1]) & 0xff) << 8));
+ }
+
+ /**
+ * Read a signed 2 bytes as a short from the idx into the raw array.
+ */
+ public static long getSignedShortFromTwoBytes(byte[] raw, int idx) {
+ return (long) ( (((long) raw[idx]) & 0xff)
+ | (((long) raw[idx + 1]) << 8));
+ }
+
+ /**
+ * Read an unsigned 4 bytes as an int from the idx into the raw array.
+ */
+ public static long getUnsignedIntFromFourBytes(byte[] raw, int idx) {
+ return (long) ( (((long) raw[idx]) & 0xff)
+ | ((((long) raw[idx + 1]) & 0xff) << 8)
+ | ((((long) raw[idx + 2]) & 0xff) << 16)
+ | ((((long) raw[idx + 3]) & 0xff) << 24) );
+ }
+
+ /**
+ * Read a signed 4 bytes as an int from the idx into the raw array.
+ */
+ public static long getSignedIntFromFourBytes(byte[] raw, int idx) {
+ return (long) ( (((long) raw[idx]) & 0xff)
+ | ((((long) raw[idx + 1]) & 0xff) << 8)
+ | ((((long) raw[idx + 2]) & 0xff) << 16)
+ | (((long) raw[idx + 3]) << 24) );
+ }
+
+ /**
+ * Read a signed 8 bytes as a long from the idx into the raw array.
+ */
+ public static long getSignedLongFromEightBytes(byte[] raw, int idx) {
+ return (long) ( (((long) raw[idx]) & 0xff)
+ | ((((long) raw[idx + 1]) & 0xff) << 8)
+ | ((((long) raw[idx + 2]) & 0xff) << 16)
+ | ((((long) raw[idx + 3]) & 0xff) << 24)
+ | ((((long) raw[idx + 4]) & 0xff) << 32)
+ | ((((long) raw[idx + 5]) & 0xff) << 40)
+ | ((((long) raw[idx + 6]) & 0xff) << 48)
+ | (((long) raw[idx + 7]) << 56) );
+ }
+
+ /**
+ * Given an idx into a raw array, and an int, write that int into the array at that position.
+ */
+ public static void writeUnsignedIntToFourBytes(byte[] raw, int idx, int value) {
+ raw[idx] = (byte) (value & 0xFF);
+ raw[idx + 1] = (byte) ((value & 0xFF00) >>> 8);
+ raw[idx + 2] = (byte) ((value & 0xFF0000) >>> 16);
+ raw[idx + 3] = (byte) ((value & 0xFF000000) >>> 24);
+ }
+
+ /**
+ * Given an idx into a raw array, and a short, write that int into the array at that position.
+ */
+ public static void writeUnsignedShortToTwoBytes(byte[] raw, int idx, int value) {
+ raw[idx] = (byte) (value & 0xFF);
+ raw[idx + 1] = (byte) ((value & 0xFF00) >>> 8);
+ }
+}
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 8eac1d3..92d2202 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -11,11 +11,44 @@
{
description: "Assert.java differences between vogar and junit.",
result: EXEC_FAILED,
+ modes: [host],
name: "libcore.java.math.RunCSVTests#test_csv"
},
{
- description: "Test is currently being updated.",
+ description: "Differences between vogar and cts in user directory",
result: EXEC_FAILED,
- name: "libcore.java.util.OldTimeZoneTest#test_getDisplayNameZILjava_util_Locale"
+ modes: [device],
+ name: "libcore.java.lang.SystemTest#testSystemProperties_mutable"
+},
+{
+ description: "Differences between vogar and cts",
+ result: EXEC_FAILED,
+ modes: [device],
+ names: ["libcore.java.lang.OldSystemTest#test_getProperties",
+ "org.apache.harmony.tests.java.lang.Process2Test#test_getErrorStream",
+ "org.apache.harmony.tests.java.lang.ProcessTest#test_exitValue"]
+},
+{
+ description: "Failures needing investigation",
+ result: EXEC_FAILED,
+ modes: [device],
+ names: ["libcore.java.util.TimeZoneTest#testDisplayNames",
+ "libcore.java.util.TimeZoneTest#test_useDaylightTime_Taiwan",
+ "org.apache.harmony.tests.java.util.DateTest#test_Constructor",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_Constructor_LReadableByteChannel",
+ "org.apache.harmony.tests.java.util.TimeZoneTest#test_hasSameRules_Ljava_util_TimeZone",
+ "libcore.java.util.TimeZoneTest#testAllDisplayNames"]
+},
+{
+ description: "Test timeouts",
+ result: EXEC_TIMEOUT,
+ modes: [device],
+ names: ["org.apache.harmony.tests.java.util.ScannerTest#testPerformance"]
+},
+{
+ description: "Needs the newest cat version on the device",
+ result: EXEC_FAILED,
+ modes: [device],
+ names: ["org.apache.harmony.tests.java.lang.ProcessTest#test_getErrorStream"]
}
]
diff --git a/tools/symbolize.sh b/tools/symbolize.sh
index b66191f..0168e7d 100755
--- a/tools/symbolize.sh
+++ b/tools/symbolize.sh
@@ -52,7 +52,7 @@
DIRS=$(adbls /data/dalvik-cache/)
for DIR in $DIRS ; do
case $DIR in
- arm|arm64|mips|x86|x86_64)
+ arm|arm64|mips|mips64|x86|x86_64)
FILES=$(adbls /data/dalvik-cache/$DIR/*.oat /data/dalvik-cache/$DIR/*.dex)
for FILE in $FILES ; do
# Cannot use basename as the file doesn't exist.