diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..1cdfed923171c2f7c51dfdcae11d872e268995ca --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +USE_LLVM_COMPILER +USE_PORTABLE_COMPILER +SMALL_ART +SEA_IR_ART diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000000000000000000000000000000000000..46a7c1ec3e2410a55a1127af11e0ed65abd65c90 --- /dev/null +++ b/Android.mk @@ -0,0 +1,369 @@ +# +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + +art_path := $(LOCAL_PATH) +art_build_path := $(art_path)/build + +######################################################################## +# clean-oat targets +# + +# following the example of build's dont_bother for clean targets +ifneq (,$(filter clean-oat,$(MAKECMDGOALS))) +art_dont_bother := true +endif +ifneq (,$(filter clean-oat-host,$(MAKECMDGOALS))) +art_dont_bother := true +endif +ifneq (,$(filter clean-oat-target,$(MAKECMDGOALS))) +art_dont_bother := true +endif + +.PHONY: clean-oat +clean-oat: clean-oat-host clean-oat-target + +.PHONY: clean-oat-host +clean-oat-host: + rm -f $(ART_NATIVETEST_OUT)/*.odex + rm -f $(ART_NATIVETEST_OUT)/*.oat + rm -f $(ART_NATIVETEST_OUT)/*.art + rm -f $(ART_TEST_OUT)/*.odex + rm -f $(ART_TEST_OUT)/*.oat + rm -f $(ART_TEST_OUT)/*.art + rm -f $(DALVIK_CACHE_OUT)/*@classes.dex + rm -f $(DALVIK_CACHE_OUT)/*.oat + rm -f $(DALVIK_CACHE_OUT)/*.art + rm -f $(HOST_OUT_JAVA_LIBRARIES)/*.odex + rm -f $(HOST_OUT_JAVA_LIBRARIES)/*.oat + rm -f $(HOST_OUT_JAVA_LIBRARIES)/*.art + rm -f $(TARGET_OUT_JAVA_LIBRARIES)/*.odex + rm -f $(TARGET_OUT_JAVA_LIBRARIES)/*.oat + rm -f $(TARGET_OUT_JAVA_LIBRARIES)/*.art + rm -f $(TARGET_OUT_UNSTRIPPED)/system/framework/*.odex + rm -f $(TARGET_OUT_UNSTRIPPED)/system/framework/*.oat + rm -f $(TARGET_OUT_APPS)/*.odex + rm -f $(TARGET_OUT_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/javalib.odex + rm -f $(TARGET_OUT_INTERMEDIATES)/APPS/*_intermediates/*.odex + rm -rf /tmp/test-*/dalvik-cache/*@classes.dex + +.PHONY: clean-oat-target +clean-oat-target: + adb remount + adb shell rm $(ART_NATIVETEST_DIR)/*.odex + adb shell rm $(ART_NATIVETEST_DIR)/*.oat + adb shell rm $(ART_NATIVETEST_DIR)/*.art + adb shell rm $(ART_TEST_DIR)/*.odex + adb shell rm $(ART_TEST_DIR)/*.oat + adb shell rm $(ART_TEST_DIR)/*.art + adb shell rm $(DALVIK_CACHE_DIR)/*.dex + adb shell rm $(DALVIK_CACHE_DIR)/*.oat + adb shell rm $(DALVIK_CACHE_DIR)/*.art + adb shell rm $(DEXPREOPT_BOOT_JAR_DIR)/*.oat + adb shell rm $(DEXPREOPT_BOOT_JAR_DIR)/*.art + adb shell rm system/app/*.odex + adb shell rm data/run-test/test-*/dalvik-cache/*@classes.dex + +ifneq ($(art_dont_bother),true) + +######################################################################## +# product targets +include $(art_path)/runtime/Android.mk +include $(art_path)/compiler/Android.mk +include $(art_path)/dex2oat/Android.mk +include $(art_path)/oatdump/Android.mk +include $(art_path)/dalvikvm/Android.mk +include $(art_path)/jdwpspy/Android.mk +include $(art_build_path)/Android.oat.mk + +# ART_HOST_DEPENDENCIES depends on Android.executable.mk above for ART_HOST_EXECUTABLES +ART_HOST_DEPENDENCIES := $(ART_HOST_EXECUTABLES) $(HOST_OUT_JAVA_LIBRARIES)/core-hostdex.jar +ART_HOST_DEPENDENCIES += $(HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION) +ART_TARGET_DEPENDENCIES := $(ART_TARGET_EXECUTABLES) $(TARGET_OUT_JAVA_LIBRARIES)/core.jar $(TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so + +######################################################################## +# test targets + +include $(art_path)/test/Android.mk +include $(art_build_path)/Android.gtest.mk + +# The ART_*_TEST_DEPENDENCIES definitions: +# - depend on Android.oattest.mk above for ART_TEST_*_DEX_FILES +# - depend on Android.gtest.mk above for ART_*_TEST_EXECUTABLES +ART_HOST_TEST_DEPENDENCIES := $(ART_HOST_DEPENDENCIES) $(ART_HOST_TEST_EXECUTABLES) $(ART_TEST_HOST_DEX_FILES) $(HOST_CORE_IMG_OUT) +ART_TARGET_TEST_DEPENDENCIES := $(ART_TARGET_DEPENDENCIES) $(ART_TARGET_TEST_EXECUTABLES) $(ART_TEST_TARGET_DEX_FILES) $(TARGET_CORE_IMG_OUT) + +include $(art_build_path)/Android.libarttest.mk + +# "mm test-art" to build and run all tests on host and device +.PHONY: test-art +test-art: test-art-host test-art-target + @echo test-art PASSED + +.PHONY: test-art-gtest +test-art-gtest: test-art-host-gtest test-art-target-gtest + @echo test-art-gtest PASSED + +.PHONY: test-art-oat +test-art-oat: test-art-host-oat test-art-target-oat + @echo test-art-oat PASSED + +.PHONY: test-art-run-test +test-art-run-test: test-art-host-run-test test-art-target-run-test + @echo test-art-run-test PASSED + +######################################################################## +# host test targets + +# "mm test-art-host" to build and run all host tests +.PHONY: test-art-host +test-art-host: test-art-host-gtest test-art-host-oat test-art-host-run-test + @echo test-art-host PASSED + +.PHONY: test-art-host-interpreter +test-art-host-interpreter: test-art-host-oat-interpreter test-art-host-run-test-interpreter + @echo test-art-host-interpreter PASSED + +.PHONY: test-art-host-dependencies +test-art-host-dependencies: $(ART_HOST_TEST_DEPENDENCIES) $(HOST_OUT_SHARED_LIBRARIES)/libarttest$(ART_HOST_SHLIB_EXTENSION) $(HOST_CORE_DEX_LOCATIONS) + +.PHONY: test-art-host-gtest +test-art-host-gtest: $(ART_HOST_TEST_TARGETS) + @echo test-art-host-gtest PASSED + +define run-host-gtests-with + $(foreach file,$(sort $(ART_HOST_TEST_EXECUTABLES)),$(1) $(file) &&) true +endef + +# "mm valgrind-test-art-host-gtest" to build and run the host gtests under valgrind. +.PHONY: valgrind-test-art-host-gtest +valgrind-test-art-host-gtest: test-art-host-dependencies + $(call run-host-gtests-with,valgrind --leak-check=full) + @echo valgrind-test-art-host-gtest PASSED + +.PHONY: test-art-host-oat-default +test-art-host-oat-default: $(ART_TEST_HOST_OAT_DEFAULT_TARGETS) + @echo test-art-host-oat-default PASSED + +.PHONY: test-art-host-oat-interpreter +test-art-host-oat-interpreter: $(ART_TEST_HOST_OAT_INTERPRETER_TARGETS) + @echo test-art-host-oat-interpreter PASSED + +.PHONY: test-art-host-oat +test-art-host-oat: test-art-host-oat-default test-art-host-oat-interpreter + @echo test-art-host-oat PASSED + +define declare-test-art-host-run-test +.PHONY: test-art-host-run-test-default-$(1) +test-art-host-run-test-default-$(1): test-art-host-dependencies + art/test/run-test --host $(1) + @echo test-art-host-run-test-default-$(1) PASSED + +TEST_ART_HOST_RUN_TEST_DEFAULT_TARGETS += test-art-host-run-test-default-$(1) + +.PHONY: test-art-host-run-test-interpreter-$(1) +test-art-host-run-test-interpreter-$(1): test-art-host-dependencies + art/test/run-test --host --interpreter $(1) + @echo test-art-host-run-test-interpreter-$(1) PASSED + +TEST_ART_HOST_RUN_TEST_INTERPRETER_TARGETS += test-art-host-run-test-interpreter-$(1) + +.PHONY: test-art-host-run-test-$(1) +test-art-host-run-test-$(1): test-art-host-run-test-default-$(1) test-art-host-run-test-interpreter-$(1) + +endef + +$(foreach test, $(wildcard art/test/[0-9]*), $(eval $(call declare-test-art-host-run-test,$(notdir $(test))))) + +.PHONY: test-art-host-run-test-default +test-art-host-run-test-default: $(TEST_ART_HOST_RUN_TEST_DEFAULT_TARGETS) + @echo test-art-host-run-test-default PASSED + +.PHONY: test-art-host-run-test-interpreter +test-art-host-run-test-interpreter: $(TEST_ART_HOST_RUN_TEST_INTERPRETER_TARGETS) + @echo test-art-host-run-test-interpreter PASSED + +.PHONY: test-art-host-run-test +test-art-host-run-test: test-art-host-run-test-default test-art-host-run-test-interpreter + @echo test-art-host-run-test PASSED + +######################################################################## +# target test targets + +# "mm test-art-target" to build and run all target tests +.PHONY: test-art-target +test-art-target: test-art-target-gtest test-art-target-oat test-art-target-run-test + @echo test-art-target PASSED + +.PHONY: test-art-target-dependencies +test-art-target-dependencies: $(ART_TARGET_TEST_DEPENDENCIES) $(ART_TEST_OUT)/libarttest.so + +.PHONY: test-art-target-sync +test-art-target-sync: test-art-target-dependencies + adb remount + adb sync + adb shell mkdir -p $(ART_TEST_DIR) + +.PHONY: test-art-target-gtest +test-art-target-gtest: $(ART_TARGET_TEST_TARGETS) + +.PHONY: test-art-target-oat +test-art-target-oat: $(ART_TEST_TARGET_OAT_TARGETS) + @echo test-art-target-oat PASSED + +define declare-test-art-target-run-test +.PHONY: test-art-target-run-test-$(1) +test-art-target-run-test-$(1): test-art-target-sync + art/test/run-test $(1) + @echo test-art-target-run-test-$(1) PASSED + +TEST_ART_TARGET_RUN_TEST_TARGETS += test-art-target-run-test-$(1) + +test-art-run-test-$(1): test-art-host-run-test-$(1) test-art-target-run-test-$(1) + +endef + +$(foreach test, $(wildcard art/test/[0-9]*), $(eval $(call declare-test-art-target-run-test,$(notdir $(test))))) + +.PHONY: test-art-target-run-test +test-art-target-run-test: $(TEST_ART_TARGET_RUN_TEST_TARGETS) + @echo test-art-target-run-test PASSED + +######################################################################## +# oat-target and oat-target-sync targets + +OAT_TARGET_TARGETS := + +# $(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 +# to define the oat-target-* rules there because they will conflict +# with the build/core/dex_preopt.mk defined rules. +.PHONY: oat-target-$(1) +oat-target-$(1): + +else +.PHONY: oat-target-$(1) +oat-target-$(1): $$(OUT_OAT_FILE) + +$$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(TARGET_BOOT_IMG_OUT) $(DEX2OAT_DEPENDENCY) + @mkdir -p $$(dir $$@) + $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms64m --runtime-arg -Xmx64m --boot-image=$(TARGET_BOOT_IMG_OUT) --dex-file=$(PRODUCT_OUT)/$(1) --dex-location=/$(1) --oat-file=$$@ --host-prefix=$(PRODUCT_OUT) --instruction-set=$(TARGET_ARCH) --android-root=$(PRODUCT_OUT)/system + +endif + +OAT_TARGET_TARGETS += oat-target-$(1) +endef + +$(foreach file,\ + $(filter-out\ + $(addprefix $(TARGET_OUT_JAVA_LIBRARIES)/,$(addsuffix .jar,$(TARGET_BOOT_JARS))),\ + $(wildcard $(TARGET_OUT_APPS)/*.apk) $(wildcard $(TARGET_OUT_JAVA_LIBRARIES)/*.jar)),\ + $(eval $(call declare-oat-target-target,$(subst $(PRODUCT_OUT)/,,$(file))))) + +.PHONY: oat-target +oat-target: $(ART_TARGET_DEPENDENCIES) $(TARGET_BOOT_OAT_OUT) $(OAT_TARGET_TARGETS) + +.PHONY: oat-target-sync +oat-target-sync: oat-target + adb remount + adb sync + +######################################################################## +# "m build-art" for quick minimal build +.PHONY: build-art +build-art: build-art-host build-art-target + +.PHONY: build-art-host +build-art-host: $(ART_HOST_EXECUTABLES) $(ART_HOST_TEST_EXECUTABLES) $(HOST_CORE_IMG_OUT) $(HOST_OUT)/lib/libjavacore.so + +.PHONY: build-art-target +build-art-target: $(ART_TARGET_EXECUTABLES) $(ART_TARGET_TEST_EXECUTABLES) $(TARGET_CORE_IMG_OUT) $(TARGET_OUT)/lib/libjavacore.so + +######################################################################## +# oatdump targets + +.PHONY: dump-oat +dump-oat: dump-oat-core dump-oat-boot + +.PHONY: dump-oat-core +dump-oat-core: dump-oat-core-host dump-oat-core-target + +.PHONY: dump-oat-core-host +ifeq ($(ART_BUILD_HOST),true) +dump-oat-core-host: $(HOST_CORE_IMG_OUT) $(OATDUMP) + $(OATDUMP) --image=$(HOST_CORE_IMG_OUT) --output=/tmp/core.host.oatdump.txt --host-prefix="" + @echo Output in /tmp/core.host.oatdump.txt +endif + +.PHONY: dump-oat-core-target +ifeq ($(ART_BUILD_TARGET),true) +dump-oat-core-target: $(TARGET_CORE_IMG_OUT) $(OATDUMP) + $(OATDUMP) --image=$(TARGET_CORE_IMG_OUT) --output=/tmp/core.target.oatdump.txt + @echo Output in /tmp/core.target.oatdump.txt +endif + +.PHONY: dump-oat-boot +ifeq ($(ART_BUILD_TARGET_NDEBUG),true) +dump-oat-boot: $(TARGET_BOOT_IMG_OUT) $(OATDUMP) + $(OATDUMP) --image=$(TARGET_BOOT_IMG_OUT) --output=/tmp/boot.oatdump.txt + @echo Output in /tmp/boot.oatdump.txt +endif + +.PHONY: dump-oat-Calculator +ifeq ($(ART_BUILD_TARGET_NDEBUG),true) +dump-oat-Calculator: $(TARGET_OUT_APPS)/Calculator.odex $(TARGET_BOOT_IMG_OUT) $(OATDUMP) + $(OATDUMP) --oat-file=$< --output=/tmp/Calculator.oatdump.txt + @echo Output in /tmp/Calculator.oatdump.txt +endif + +######################################################################## +# cpplint targets to style check art source files + +include $(art_build_path)/Android.cpplint.mk + +######################################################################## +# targets to switch back and forth from libdvm to libart + +.PHONY: use-art +use-art: + adb root && sleep 3 + adb shell setprop persist.sys.dalvik.vm.lib libart.so + adb reboot + +.PHONY: use-artd +use-artd: + adb root && sleep 3 + adb shell setprop persist.sys.dalvik.vm.lib libartd.so + adb reboot + +.PHONY: use-dalvik +use-dalvik: + adb root && sleep 3 + adb shell setprop persist.sys.dalvik.vm.lib libdvm.so + adb reboot + +######################################################################## + +endif # !art_dont_bother diff --git a/build/Android.common.mk b/build/Android.common.mk new file mode 100644 index 0000000000000000000000000000000000000000..ac1be1e28ad52d372d7497187830c6a9e89b43e2 --- /dev/null +++ b/build/Android.common.mk @@ -0,0 +1,223 @@ +# +# 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 ANDROID_COMMON_MK +ANDROID_COMMON_MK = true + +# These can be overridden via the environment or by editing to +# enable/disable certain build configuration. +# +# For example, to disable everything but the host debug build you use: +# +# (export ART_BUILD_TARGET_NDEBUG=false && export ART_BUILD_TARGET_DEBUG=false && export ART_BUILD_HOST_NDEBUG=false && ...) +# +# Beware that tests may use the non-debug build for performance, notable 055-enum-performance +# +ART_BUILD_TARGET_NDEBUG ?= true +ART_BUILD_TARGET_DEBUG ?= true +ART_BUILD_HOST_NDEBUG ?= $(WITH_HOST_DALVIK) +ART_BUILD_HOST_DEBUG ?= $(WITH_HOST_DALVIK) + +ifeq ($(ART_BUILD_TARGET_NDEBUG),false) +$(info Disabling ART_BUILD_TARGET_NDEBUG) +endif +ifeq ($(ART_BUILD_TARGET_DEBUG),false) +$(info Disabling ART_BUILD_TARGET_DEBUG) +endif +ifeq ($(ART_BUILD_HOST_NDEBUG),false) +$(info Disabling ART_BUILD_HOST_NDEBUG) +endif +ifeq ($(ART_BUILD_HOST_DEBUG),false) +$(info Disabling ART_BUILD_HOST_DEBUG) +endif + +# +# Used to enable smart mode +# +ART_SMALL_MODE := false +ifneq ($(wildcard art/SMALL_ART),) +$(info Enabling ART_SMALL_MODE because of existence of art/SMALL_ART) +ART_SMALL_MODE := true +endif +ifeq ($(WITH_ART_SMALL_MODE), true) +ART_SMALL_MODE := true +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 + +LLVM_ROOT_PATH := external/llvm +include $(LLVM_ROOT_PATH)/llvm.mk + +# Clang build. +# ART_TARGET_CLANG := true +# ART_HOST_CLANG := true + +# directory used for gtests on device +ART_NATIVETEST_DIR := /data/nativetest/art +ART_NATIVETEST_OUT := $(TARGET_OUT_DATA_NATIVE_TESTS)/art + +# directory used for tests on device +ART_TEST_DIR := /data/art-test +ART_TEST_OUT := $(TARGET_OUT_DATA)/art-test + +ART_CPP_EXTENSION := .cc + +ART_HOST_SHLIB_EXTENSION := $(HOST_SHLIB_SUFFIX) +ART_HOST_SHLIB_EXTENSION ?= .so + +ART_C_INCLUDES := \ + external/gtest/include \ + external/valgrind/main/include \ + external/zlib \ + frameworks/compile/mclinker/include + +art_cflags := \ + -fno-rtti \ + -std=gnu++11 \ + -ggdb3 \ + -Wall \ + -Werror \ + -Wextra \ + -Wstrict-aliasing=3 \ + -fstrict-aliasing + +ifeq ($(ART_SMALL_MODE),true) + art_cflags += -DART_SMALL_MODE=1 +endif + +ifeq ($(ART_SEA_IR_MODE),true) + art_cflags += -DART_SEA_IR_MODE=1 +endif + +ifeq ($(HOST_OS),linux) + art_non_debug_cflags := \ + -Wframe-larger-than=1728 +endif + +art_non_debug_cflags := \ + -O3 + +art_debug_cflags := \ + -O1 \ + -DDYNAMIC_ANNOTATIONS_ENABLED=1 \ + -UNDEBUG + +# start of image reserved address space +IMG_HOST_BASE_ADDRESS := 0x60000000 + +ifeq ($(TARGET_ARCH),mips) +IMG_TARGET_BASE_ADDRESS := 0x30000000 +else +IMG_TARGET_BASE_ADDRESS := 0x60000000 +endif + +ART_HOST_CFLAGS := $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(IMG_HOST_BASE_ADDRESS) + +ifeq ($(TARGET_ARCH),x86) +ART_TARGET_CFLAGS += -msse2 +endif + +ART_TARGET_CFLAGS := $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(IMG_TARGET_BASE_ADDRESS) +ifeq ($(TARGET_CPU_SMP),true) + ART_TARGET_CFLAGS += -DANDROID_SMP=1 +else + ART_TARGET_CFLAGS += -DANDROID_SMP=0 +endif + +# Enable thread-safety for GCC 4.6 on the target but not for GCC 4.7 where this feature was removed. +ifneq ($(filter 4.6 4.6.%, $(TARGET_GCC_VERSION)),) + ART_TARGET_CFLAGS += -Wthread-safety +else + # Warn if not using GCC 4.6 for target builds when not doing a top-level or 'mma' build. + ifneq ($(ONE_SHOT_MAKEFILE),) + # Enable target GCC 4.6 with: export TARGET_GCC_VERSION_EXP=4.6 + $(info Using target GCC $(TARGET_GCC_VERSION) disables thread-safety checks.) + endif +endif +# We build with GCC 4.6 on the host. +ART_HOST_CFLAGS += -Wthread-safety + +# Make host builds easier to debug and profile by not omitting the frame pointer. +ART_HOST_CFLAGS += -fno-omit-frame-pointer + +# To use oprofile_android --callgraph, uncomment this and recompile with "mmm art -B -j16" +# ART_TARGET_CFLAGS += -fno-omit-frame-pointer -marm -mapcs + +ART_HOST_NON_DEBUG_CFLAGS := $(art_non_debug_cflags) +ART_TARGET_NON_DEBUG_CFLAGS := $(art_non_debug_cflags) + +# TODO: move -fkeep-inline-functions to art_debug_cflags when target gcc > 4.4 (and -lsupc++) +ART_HOST_DEBUG_CFLAGS := $(art_debug_cflags) -fkeep-inline-functions +ART_HOST_DEBUG_LDLIBS := -lsupc++ + +ifneq ($(HOST_OS),linux) + # Some Mac OS pthread header files are broken with -fkeep-inline-functions. + ART_HOST_DEBUG_CFLAGS := $(filter-out -fkeep-inline-functions,$(ART_HOST_DEBUG_CFLAGS)) + # Mac OS doesn't have libsupc++. + ART_HOST_DEBUG_LDLIBS := $(filter-out -lsupc++,$(ART_HOST_DEBUG_LDLIBS)) +endif + +ART_TARGET_DEBUG_CFLAGS := $(art_debug_cflags) + +ifeq ($(ART_USE_PORTABLE_COMPILER),true) +PARALLEL_ART_COMPILE_JOBS := -j8 +endif + +ART_BUILD_TARGET := false +ART_BUILD_HOST := false +ART_BUILD_NDEBUG := false +ART_BUILD_DEBUG := false +ifeq ($(ART_BUILD_TARGET_NDEBUG),true) + ART_BUILD_TARGET := true + ART_BUILD_NDEBUG := true +endif +ifeq ($(ART_BUILD_TARGET_DEBUG),true) + ART_BUILD_TARGET := true + ART_BUILD_DEBUG := true +endif +ifeq ($(ART_BUILD_HOST_NDEBUG),true) + ART_BUILD_HOST := true + ART_BUILD_NDEBUG := true +endif +ifeq ($(ART_BUILD_HOST_DEBUG),true) + ART_BUILD_HOST := true + ART_BUILD_DEBUG := true +endif + +endif # ANDROID_COMMON_MK diff --git a/build/Android.cpplint.mk b/build/Android.cpplint.mk new file mode 100644 index 0000000000000000000000000000000000000000..adb87cb4e96c28ee3a125eb54bfd68932fac0f2d --- /dev/null +++ b/build/Android.cpplint.mk @@ -0,0 +1,54 @@ +# +# 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. +# + +ART_CPPLINT := art/tools/cpplint.py +ART_CPPLINT_FILTER := --filter=-whitespace/line_length,-build/include,-readability/function,-readability/streams,-readability/todo,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn,-runtime/printf +ART_CPPLINT_SRC := $(shell find art -name *.h -o -name *$(ART_CPP_EXTENSION) | grep -v art/compiler/llvm/generated/) + +# "mm cpplint-art" to verify we aren't regressing +.PHONY: cpplint-art +cpplint-art: + $(ART_CPPLINT) $(ART_CPPLINT_FILTER) $(ART_CPPLINT_SRC) + +# "mm cpplint-art-all" to see all warnings +.PHONY: cpplint-art-all +cpplint-art-all: + $(ART_CPPLINT) $(ART_CPPLINT_SRC) + +OUT_CPPLINT := $(TARGET_COMMON_OUT_ROOT)/cpplint + +ART_CPPLINT_TARGETS := + +define declare-art-cpplint-target +art_cpplint_file := $(1) +art_cpplint_touch := $$(OUT_CPPLINT)/$$(subst /,__,$$(art_cpplint_file)) + +$$(art_cpplint_touch): $$(art_cpplint_file) $(ART_CPPLINT) art/build/Android.cpplint.mk + $(hide) $(ART_CPPLINT) $(ART_CPPLINT_FILTER) $$< + @mkdir -p $$(dir $$@) + $(hide) touch $$@ + +ART_CPPLINT_TARGETS += $$(art_cpplint_touch) +endef + +$(foreach file, $(ART_CPPLINT_SRC), $(eval $(call declare-art-cpplint-target,$(file)))) +#$(info $(call declare-art-cpplint-target,$(firstword $(ART_CPPLINT_SRC)))) + +include $(CLEAR_VARS) +LOCAL_MODULE := cpplint-art-phony +LOCAL_MODULE_TAGS := optional +LOCAL_ADDITIONAL_DEPENDENCIES := $(ART_CPPLINT_TARGETS) +include $(BUILD_PHONY_PACKAGE) diff --git a/build/Android.executable.mk b/build/Android.executable.mk new file mode 100644 index 0000000000000000000000000000000000000000..5cf15be1c17d2736bc08ea482670738bba9aad51 --- /dev/null +++ b/build/Android.executable.mk @@ -0,0 +1,105 @@ +# +# 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 art/build/Android.common.mk + +ART_HOST_EXECUTABLES ?= +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 +# $(3): extra shared libraries +# $(4): extra include directories +# $(5): target or host +# $(6): ndebug or debug +define build-art-executable + ifneq ($(5),target) + ifneq ($(5),host) + $$(error expected target or host for argument 5, received $(5)) + endif + endif + ifneq ($(6),ndebug) + ifneq ($(6),debug) + $$(error expected ndebug or debug for argument 6, received $(6)) + endif + endif + + art_executable := $(1) + art_source := $(2) + art_shared_libraries := $(3) + art_c_includes := $(4) + art_target_or_host := $(5) + art_ndebug_or_debug := $(6) + + include $(CLEAR_VARS) + ifeq ($$(art_target_or_host),target) + include external/stlport/libstlport.mk + endif + + 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_SHARED_LIBRARIES += $$(art_shared_libraries) # libnativehelper + + ifeq ($$(art_ndebug_or_debug),ndebug) + LOCAL_MODULE := $$(art_executable) + else #debug + LOCAL_MODULE := $$(art_executable)d + endif + + LOCAL_CFLAGS := $(ART_EXECUTABLES_CFLAGS) + ifeq ($$(art_target_or_host),target) + LOCAL_CLANG := $(ART_TARGET_CLANG) + LOCAL_CFLAGS += $(ART_TARGET_CFLAGS) + ifeq ($$(art_ndebug_or_debug),debug) + LOCAL_CFLAGS += $(ART_TARGET_DEBUG_CFLAGS) + else + LOCAL_CFLAGS += $(ART_TARGET_NON_DEBUG_CFLAGS) + endif + else # host + LOCAL_CLANG := $(ART_HOST_CLANG) + LOCAL_CFLAGS += $(ART_HOST_CFLAGS) + ifeq ($$(art_ndebug_or_debug),debug) + LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS) + else + LOCAL_CFLAGS += $(ART_HOST_NON_DEBUG_CFLAGS) + endif + endif + + ifeq ($$(art_ndebug_or_debug),ndebug) + LOCAL_SHARED_LIBRARIES += libart + else # debug + LOCAL_SHARED_LIBRARIES += libartd + endif + + LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk + LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.executable.mk + + ifeq ($$(art_target_or_host),target) + include $(BUILD_EXECUTABLE) + ART_TARGET_EXECUTABLES := $(ART_TARGET_EXECUTABLES) $(TARGET_OUT_EXECUTABLES)/$$(LOCAL_MODULE) + else # host + include $(BUILD_HOST_EXECUTABLE) + ART_HOST_EXECUTABLES := $(ART_HOST_EXECUTABLES) $(HOST_OUT_EXECUTABLES)/$$(LOCAL_MODULE) + endif + +endef diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk new file mode 100644 index 0000000000000000000000000000000000000000..4c658a2bb7992d9b3ab84e4befd160b8ed81912b --- /dev/null +++ b/build/Android.gtest.mk @@ -0,0 +1,181 @@ +# +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := art + +TEST_COMMON_SRC_FILES := \ + compiler/driver/compiler_driver_test.cc \ + compiler/elf_writer_test.cc \ + compiler/image_test.cc \ + compiler/jni/jni_compiler_test.cc \ + compiler/oat_test.cc \ + compiler/output_stream_test.cc \ + compiler/utils/dedupe_set_test.cc \ + compiler/utils/arm/managed_register_arm_test.cc \ + compiler/utils/x86/managed_register_x86_test.cc \ + runtime/barrier_test.cc \ + runtime/base/histogram_test.cc \ + runtime/base/mutex_test.cc \ + runtime/base/timing_logger_test.cc \ + runtime/base/unix_file/fd_file_test.cc \ + runtime/base/unix_file/mapped_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_instruction_visitor_test.cc \ + runtime/dex_method_iterator_test.cc \ + runtime/entrypoints/math_entrypoints_test.cc \ + runtime/exception_test.cc \ + runtime/gc/accounting/space_bitmap_test.cc \ + runtime/gc/heap_test.cc \ + runtime/gc/space/space_test.cc \ + runtime/gtest_test.cc \ + runtime/indenter_test.cc \ + runtime/indirect_reference_table_test.cc \ + runtime/intern_table_test.cc \ + runtime/jni_internal_test.cc \ + runtime/mem_map_test.cc \ + runtime/mirror/dex_cache_test.cc \ + runtime/mirror/object_test.cc \ + runtime/reference_table_test.cc \ + runtime/runtime_test.cc \ + runtime/thread_pool_test.cc \ + runtime/utils_test.cc \ + runtime/verifier/method_verifier_test.cc \ + runtime/verifier/reg_type_test.cc \ + runtime/zip_archive_test.cc + +ifeq ($(ART_SEA_IR_MODE),true) +TEST_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 + +TEST_TARGET_SRC_FILES := \ + $(TEST_COMMON_SRC_FILES) + +TEST_HOST_SRC_FILES := \ + $(TEST_COMMON_SRC_FILES) \ + compiler/utils/x86/assembler_x86_test.cc + +ART_HOST_TEST_EXECUTABLES := +ART_TARGET_TEST_EXECUTABLES := +ART_HOST_TEST_TARGETS := +ART_TARGET_TEST_TARGETS := + +ART_TEST_CFLAGS := +ifeq ($(ART_USE_PORTABLE_COMPILER),true) + ART_TEST_CFLAGS += -DART_USE_PORTABLE_COMPILER=1 +endif + +# $(1): target or host +# $(2): file name +define build-art-test + ifneq ($(1),target) + ifneq ($(1),host) + $$(error expected target or host for argument 1, received $(1)) + endif + endif + + art_target_or_host := $(1) + art_gtest_filename := $(2) + + art_gtest_name := $$(notdir $$(basename $$(art_gtest_filename))) + + include $(CLEAR_VARS) + ifeq ($$(art_target_or_host),target) + include external/stlport/libstlport.mk + endif + + LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) + LOCAL_MODULE := $$(art_gtest_name) + ifeq ($$(art_target_or_host),target) + LOCAL_MODULE_TAGS := tests + endif + LOCAL_SRC_FILES := $$(art_gtest_filename) runtime/common_test.cc + LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime art/compiler + LOCAL_SHARED_LIBRARIES += libartd-compiler libartd + # dex2oatd is needed to go libartd-compiler and libartd + LOCAL_REQUIRED_MODULES := dex2oatd + + LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk + LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.gtest.mk + + # Mac OS linker doesn't understand --export-dynamic. + ifneq ($(HOST_OS)-$$(art_target_or_host),darwin-host) + # Allow jni_compiler_test to find Java_MyClassNatives_bar within itself using dlopen(NULL, ...). + LOCAL_LDFLAGS := -Wl,--export-dynamic -Wl,-u,Java_MyClassNatives_bar -Wl,-u,Java_MyClassNatives_sbar + endif + + LOCAL_CFLAGS := $(ART_TEST_CFLAGS) + ifeq ($$(art_target_or_host),target) + LOCAL_CLANG := $(ART_TARGET_CLANG) + LOCAL_CFLAGS += $(ART_TARGET_CFLAGS) $(ART_TARGET_DEBUG_CFLAGS) + LOCAL_SHARED_LIBRARIES += libdl libicuuc libicui18n libnativehelper libz libcutils + LOCAL_STATIC_LIBRARIES += libgtest + LOCAL_MODULE_PATH := $(ART_NATIVETEST_OUT) + include $(LLVM_DEVICE_BUILD_MK) + include $(BUILD_EXECUTABLE) + art_gtest_exe := $$(LOCAL_MODULE_PATH)/$$(LOCAL_MODULE) + ART_TARGET_TEST_EXECUTABLES += $$(art_gtest_exe) + else # host + LOCAL_CLANG := $(ART_HOST_CLANG) + LOCAL_CFLAGS += $(ART_HOST_CFLAGS) $(ART_HOST_DEBUG_CFLAGS) + LOCAL_SHARED_LIBRARIES += libicuuc-host libicui18n-host libnativehelper libz-host + LOCAL_STATIC_LIBRARIES += libcutils + ifeq ($(HOST_OS),darwin) + # Mac OS complains about unresolved symbols if you don't include this. + LOCAL_WHOLE_STATIC_LIBRARIES := libgtest_host + endif + include $(LLVM_HOST_BUILD_MK) + include $(BUILD_HOST_EXECUTABLE) + art_gtest_exe := $(HOST_OUT_EXECUTABLES)/$$(LOCAL_MODULE) + ART_HOST_TEST_EXECUTABLES += $$(art_gtest_exe) + endif +art_gtest_target := test-art-$$(art_target_or_host)-gtest-$$(art_gtest_name) +ifeq ($$(art_target_or_host),target) +.PHONY: $$(art_gtest_target) +$$(art_gtest_target): $$(art_gtest_exe) test-art-target-sync + adb shell touch $(ART_TEST_DIR)/$$@ + adb shell rm $(ART_TEST_DIR)/$$@ + adb shell chmod 755 $(ART_NATIVETEST_DIR)/$$(notdir $$<) + adb shell sh -c "$(ART_NATIVETEST_DIR)/$$(notdir $$<) && touch $(ART_TEST_DIR)/$$@" + $(hide) (adb pull $(ART_TEST_DIR)/$$@ /tmp/ && echo $$@ PASSED) || (echo $$@ FAILED && exit 1) + $(hide) rm /tmp/$$@ + +ART_TARGET_TEST_TARGETS += $$(art_gtest_target) +else +.PHONY: $$(art_gtest_target) +$$(art_gtest_target): $$(art_gtest_exe) test-art-host-dependencies + $$< + @echo $$@ PASSED + +ART_HOST_TEST_TARGETS += $$(art_gtest_target) +endif +endef + +ifeq ($(ART_BUILD_TARGET),true) + $(foreach file,$(TEST_TARGET_SRC_FILES), $(eval $(call build-art-test,target,$(file)))) +endif +ifeq ($(WITH_HOST_DALVIK),true) + ifeq ($(ART_BUILD_HOST),true) + $(foreach file,$(TEST_HOST_SRC_FILES), $(eval $(call build-art-test,host,$(file)))) + endif +endif diff --git a/build/Android.libarttest.mk b/build/Android.libarttest.mk new file mode 100644 index 0000000000000000000000000000000000000000..f946d919cff2c83a4a0c4b215dcf82ad4fb98edd --- /dev/null +++ b/build/Android.libarttest.mk @@ -0,0 +1,73 @@ +# +# 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. +# + +LIBARTTEST_COMMON_SRC_FILES := \ + test/JniTest/jni_test.cc \ + test/ReferenceMap/stack_walk_refmap_jni.cc \ + test/StackWalk/stack_walk_jni.cc + +# $(1): target or host +define build-libarttest + ifneq ($(1),target) + ifneq ($(1),host) + $$(error expected target or host for argument 1, received $(1)) + endif + endif + + art_target_or_host := $(1) + + include $(CLEAR_VARS) + ifeq ($$(art_target_or_host),target) + include external/stlport/libstlport.mk + endif + + LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) + LOCAL_MODULE := libarttest + ifeq ($$(art_target_or_host),target) + LOCAL_MODULE_TAGS := tests + endif + LOCAL_SRC_FILES := $(LIBARTTEST_COMMON_SRC_FILES) + LOCAL_SHARED_LIBRARIES += libartd + LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime + LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/build/Android.common.mk + LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/build/Android.libarttest.mk + ifeq ($$(art_target_or_host),target) + LOCAL_CLANG := $(ART_TARGET_CLANG) + LOCAL_CFLAGS := $(ART_TARGET_CFLAGS) $(ART_TARGET_DEBUG_CFLAGS) + LOCAL_SHARED_LIBRARIES += libdl libcutils + LOCAL_STATIC_LIBRARIES := libgtest + LOCAL_MODULE_PATH := $(ART_TEST_OUT) + include $(BUILD_SHARED_LIBRARY) + else # host + LOCAL_CLANG := $(ART_HOST_CLANG) + LOCAL_CFLAGS := $(ART_HOST_CFLAGS) $(ART_HOST_DEBUG_CFLAGS) + LOCAL_STATIC_LIBRARIES := libcutils + LOCAL_LDLIBS := -ldl -lpthread + ifeq ($(HOST_OS),linux) + LOCAL_LDLIBS += -lrt + endif + include $(BUILD_HOST_SHARED_LIBRARY) + endif +endef + +ifeq ($(ART_BUILD_TARGET),true) + $(eval $(call build-libarttest,target)) +endif +ifeq ($(WITH_HOST_DALVIK),true) + ifeq ($(ART_BUILD_HOST),true) + $(eval $(call build-libarttest,host)) + endif +endif diff --git a/build/Android.oat.mk b/build/Android.oat.mk new file mode 100644 index 0000000000000000000000000000000000000000..ea7b0b08ca9ff333eec4ef3a805a6bd923e0fd7e --- /dev/null +++ b/build/Android.oat.mk @@ -0,0 +1,114 @@ +# +# 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. +# + +# DEX2OAT defined in build/core/config.mk +DEX2OATD := $(HOST_OUT_EXECUTABLES)/dex2oatd$(HOST_EXECUTABLE_SUFFIX) + +LIBART_COMPILER := $(HOST_OUT_SHARED_LIBRARIES)/libart-compiler$(HOST_SHLIB_SUFFIX) +LIBARTD_COMPILER := $(HOST_OUT_SHARED_LIBRARIES)/libartd-compiler$(HOST_SHLIB_SUFFIX) + +# TODO: for now, override with debug version for better error reporting +DEX2OAT := $(DEX2OATD) +LIBART_COMPILER := $(LIBARTD_COMPILER) + +# By default, do not run rerun dex2oat if the tool changes. +# Comment out the | to force dex2oat to rerun on after all changes. +DEX2OAT_DEPENDENCY := | +DEX2OAT_DEPENDENCY += $(DEX2OAT) +DEX2OAT_DEPENDENCY += $(LIBART_COMPILER) + +OATDUMP := $(HOST_OUT_EXECUTABLES)/oatdump$(HOST_EXECUTABLE_SUFFIX) +OATDUMPD := $(HOST_OUT_EXECUTABLES)/oatdumpd$(HOST_EXECUTABLE_SUFFIX) +# TODO: for now, override with debug version for better error reporting +OATDUMP := $(OATDUMPD) + +PRELOADED_CLASSES := frameworks/base/preloaded-classes + +######################################################################## +# A smaller libcore only oat file +TARGET_CORE_JARS := core-libart conscrypt okhttp core-junit bouncycastle +HOST_CORE_JARS := $(addsuffix -hostdex,$(TARGET_CORE_JARS)) + +HOST_CORE_DEX_LOCATIONS := $(foreach jar,$(HOST_CORE_JARS), $(HOST_OUT_JAVA_LIBRARIES)/$(jar).jar) +TARGET_CORE_DEX_LOCATIONS := $(foreach jar,$(TARGET_CORE_JARS),/$(DEXPREOPT_BOOT_JAR_DIR)/$(jar).jar) + +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) + +HOST_CORE_OAT := $(HOST_OUT_JAVA_LIBRARIES)/core.oat +TARGET_CORE_OAT := $(ART_TEST_DIR)/core.oat + +HOST_CORE_OAT_OUT := $(HOST_OUT_JAVA_LIBRARIES)/core.oat +TARGET_CORE_OAT_OUT := $(ART_TEST_OUT)/core.oat + +HOST_CORE_IMG_OUT := $(HOST_OUT_JAVA_LIBRARIES)/core.art +TARGET_CORE_IMG_OUT := $(ART_TEST_OUT)/core.art + +$(HOST_CORE_IMG_OUT): $(HOST_CORE_DEX_FILES) $(DEX2OAT_DEPENDENCY) + @echo "host dex2oat: $@ ($?)" + @mkdir -p $(dir $@) + $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix --dex-file=,$(HOST_CORE_DEX_FILES)) $(addprefix --dex-location=,$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$(HOST_CORE_OAT_OUT) --oat-location=$(HOST_CORE_OAT) --image=$(HOST_CORE_IMG_OUT) --base=$(IMG_HOST_BASE_ADDRESS) --instruction-set=$(HOST_ARCH) --host --android-root=$(HOST_OUT) + +$(TARGET_CORE_IMG_OUT): $(TARGET_CORE_DEX_FILES) $(DEX2OAT_DEPENDENCY) + @echo "target dex2oat: $@ ($?)" + @mkdir -p $(dir $@) + $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix --dex-file=,$(TARGET_CORE_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$(TARGET_CORE_OAT_OUT) --oat-location=$(TARGET_CORE_OAT) --image=$(TARGET_CORE_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) --instruction-set=$(TARGET_ARCH) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system + +$(HOST_CORE_OAT_OUT): $(HOST_CORE_IMG_OUT) + +$(TARGET_CORE_OAT_OUT): $(TARGET_CORE_IMG_OUT) + +ifeq ($(ART_BUILD_HOST),true) +include $(CLEAR_VARS) +LOCAL_MODULE := core.art-host +LOCAL_MODULE_TAGS := optional +LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk +LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.oat.mk +LOCAL_ADDITIONAL_DEPENDENCIES += $(HOST_CORE_IMG_OUT) +include $(BUILD_PHONY_PACKAGE) +endif + +######################################################################## +# The full system boot classpath +TARGET_BOOT_JARS := $(subst :, ,$(DEXPREOPT_BOOT_JARS)) +TARGET_BOOT_JARS := $(foreach jar,$(TARGET_BOOT_JARS),$(patsubst core, core-libart,$(jar))) +TARGET_BOOT_DEX_LOCATIONS := $(foreach jar,$(TARGET_BOOT_JARS),/$(DEXPREOPT_BOOT_JAR_DIR)/$(jar).jar) +TARGET_BOOT_DEX_FILES := $(foreach jar,$(TARGET_BOOT_JARS),$(call intermediates-dir-for,JAVA_LIBRARIES,$(jar),,COMMON)/javalib.jar) + +TARGET_BOOT_IMG_OUT := $(DEFAULT_DEX_PREOPT_IMAGE) +TARGET_BOOT_OAT_OUT := $(patsubst %.art,%.oat,$(TARGET_BOOT_IMG_OUT)) +TARGET_BOOT_OAT := $(subst $(PRODUCT_OUT),,$(TARGET_BOOT_OAT_OUT)) +TARGET_BOOT_OAT_UNSTRIPPED_OUT := $(TARGET_OUT_UNSTRIPPED)$(TARGET_BOOT_OAT) + +$(TARGET_BOOT_IMG_OUT): $(TARGET_BOOT_DEX_FILES) $(DEX2OAT_DEPENDENCY) + @echo "target dex2oat: $@ ($?)" + @mkdir -p $(dir $@) + @mkdir -p $(dir $(TARGET_BOOT_OAT_UNSTRIPPED_OUT)) + $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms256m --runtime-arg -Xmx256m --image-classes=$(PRELOADED_CLASSES) $(addprefix --dex-file=,$(TARGET_BOOT_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_BOOT_DEX_LOCATIONS)) --oat-symbols=$(TARGET_BOOT_OAT_UNSTRIPPED_OUT) --oat-file=$(TARGET_BOOT_OAT_OUT) --oat-location=$(TARGET_BOOT_OAT) --image=$(TARGET_BOOT_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) --instruction-set=$(TARGET_ARCH) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system + +$(TARGET_BOOT_OAT_UNSTRIPPED_OUT): $(TARGET_BOOT_IMG_OUT) + +$(TARGET_BOOT_OAT_OUT): $(TARGET_BOOT_OAT_UNSTRIPPED_OUT) + +ifeq ($(ART_BUILD_TARGET_NDEBUG),true) +include $(CLEAR_VARS) +LOCAL_MODULE := boot.art +LOCAL_MODULE_TAGS := optional +LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk +LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.oat.mk +LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_BOOT_IMG_OUT) $(TARGET_BOOT_OAT_OUT) +include $(BUILD_PHONY_PACKAGE) +endif diff --git a/compiler/Android.mk b/compiler/Android.mk new file mode 100644 index 0000000000000000000000000000000000000000..66ff46163baa4f6238bfa05f858717325b972883 --- /dev/null +++ b/compiler/Android.mk @@ -0,0 +1,258 @@ +# +# 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. +# + +LOCAL_PATH := $(call my-dir) + +include art/build/Android.common.mk + +LIBART_COMPILER_SRC_FILES := \ + compiled_method.cc \ + dex/local_value_numbering.cc \ + dex/arena_allocator.cc \ + dex/arena_bit_vector.cc \ + dex/quick/arm/assemble_arm.cc \ + dex/quick/arm/call_arm.cc \ + dex/quick/arm/fp_arm.cc \ + dex/quick/arm/int_arm.cc \ + dex/quick/arm/target_arm.cc \ + dex/quick/arm/utility_arm.cc \ + dex/quick/codegen_util.cc \ + dex/quick/gen_common.cc \ + dex/quick/gen_invoke.cc \ + dex/quick/gen_loadstore.cc \ + dex/quick/local_optimizations.cc \ + dex/quick/mips/assemble_mips.cc \ + dex/quick/mips/call_mips.cc \ + dex/quick/mips/fp_mips.cc \ + dex/quick/mips/int_mips.cc \ + dex/quick/mips/target_mips.cc \ + dex/quick/mips/utility_mips.cc \ + dex/quick/mir_to_lir.cc \ + dex/quick/ralloc_util.cc \ + dex/quick/x86/assemble_x86.cc \ + dex/quick/x86/call_x86.cc \ + dex/quick/x86/fp_x86.cc \ + dex/quick/x86/int_x86.cc \ + dex/quick/x86/target_x86.cc \ + dex/quick/x86/utility_x86.cc \ + dex/portable/mir_to_gbc.cc \ + dex/dex_to_dex_compiler.cc \ + dex/mir_dataflow.cc \ + dex/mir_optimization.cc \ + dex/frontend.cc \ + dex/mir_graph.cc \ + dex/mir_analysis.cc \ + dex/vreg_analysis.cc \ + dex/ssa_transformation.cc \ + driver/compiler_driver.cc \ + driver/dex_compilation_unit.cc \ + jni/portable/jni_compiler.cc \ + jni/quick/arm/calling_convention_arm.cc \ + jni/quick/mips/calling_convention_mips.cc \ + jni/quick/x86/calling_convention_x86.cc \ + jni/quick/calling_convention.cc \ + jni/quick/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_thumb2.cc \ + llvm/runtime_support_builder_x86.cc \ + trampolines/trampoline_compiler.cc \ + utils/arm/assembler_arm.cc \ + utils/arm/managed_register_arm.cc \ + utils/assembler.cc \ + utils/mips/assembler_mips.cc \ + utils/mips/managed_register_mips.cc \ + utils/x86/assembler_x86.cc \ + utils/x86/managed_register_x86.cc \ + buffered_output_stream.cc \ + elf_fixup.cc \ + elf_stripper.cc \ + elf_writer.cc \ + elf_writer_quick.cc \ + file_output_stream.cc \ + image_writer.cc \ + oat_writer.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 += elf_writer_mclinker.cc + LIBART_COMPILER_CFLAGS += -DART_USE_PORTABLE_COMPILER=1 +endif + +LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES := \ + dex/compiler_enums.h + +# $(1): target or host +# $(2): ndebug or debug +define build-libart-compiler + ifneq ($(1),target) + ifneq ($(1),host) + $$(error expected target or host for argument 1, received $(1)) + endif + endif + ifneq ($(2),ndebug) + ifneq ($(2),debug) + $$(error expected ndebug or debug for argument 2, received $(2)) + endif + endif + + art_target_or_host := $(1) + art_ndebug_or_debug := $(2) + + include $(CLEAR_VARS) + ifeq ($$(art_target_or_host),target) + include external/stlport/libstlport.mk + else + LOCAL_IS_HOST_MODULE := true + endif + LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) + ifeq ($$(art_ndebug_or_debug),ndebug) + LOCAL_MODULE := libart-compiler + else # debug + LOCAL_MODULE := libartd-compiler + endif + + LOCAL_MODULE_TAGS := optional + LOCAL_MODULE_CLASS := SHARED_LIBRARIES + + LOCAL_SRC_FILES := $$(LIBART_COMPILER_SRC_FILES) + + GENERATED_SRC_DIR := $$(call intermediates-dir-for,$$(LOCAL_MODULE_CLASS),$$(LOCAL_MODULE),$$(LOCAL_IS_HOST_MODULE),) + ENUM_OPERATOR_OUT_CC_FILES := $$(patsubst %.h,%_operator_out.cc,$$(LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES)) + ENUM_OPERATOR_OUT_GEN := $$(addprefix $$(GENERATED_SRC_DIR)/,$$(ENUM_OPERATOR_OUT_CC_FILES)) + +$$(ENUM_OPERATOR_OUT_GEN): art/tools/generate-operator-out.py +$$(ENUM_OPERATOR_OUT_GEN): PRIVATE_CUSTOM_TOOL = art/tools/generate-operator-out.py $(LOCAL_PATH) $$< > $$@ +$$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PATH)/%.h + $$(transform-generated-source) + + LOCAL_GENERATED_SOURCES += $$(ENUM_OPERATOR_OUT_GEN) + + LOCAL_CFLAGS := $$(LIBART_COMPILER_CFLAGS) + ifeq ($$(art_target_or_host),target) + LOCAL_CLANG := $(ART_TARGET_CLANG) + LOCAL_CFLAGS += $(ART_TARGET_CFLAGS) + else # host + LOCAL_CLANG := $(ART_HOST_CLANG) + LOCAL_CFLAGS += $(ART_HOST_CFLAGS) + endif + + # TODO: clean up the compilers and remove this. + LOCAL_CFLAGS += -Wno-unused-parameter + + LOCAL_SHARED_LIBRARIES += liblog + ifeq ($$(art_ndebug_or_debug),debug) + ifeq ($$(art_target_or_host),target) + LOCAL_CFLAGS += $(ART_TARGET_DEBUG_CFLAGS) + else # host + LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS) + endif + LOCAL_SHARED_LIBRARIES += libartd + else + ifeq ($$(art_target_or_host),target) + LOCAL_CFLAGS += $(ART_TARGET_NON_DEBUG_CFLAGS) + else # host + LOCAL_CFLAGS += $(ART_HOST_NON_DEBUG_CFLAGS) + endif + LOCAL_SHARED_LIBRARIES += libart + endif + LOCAL_SHARED_LIBRARIES += libbcc libbcinfo libLLVM + ifeq ($(ART_USE_PORTABLE_COMPILER),true) + LOCAL_CFLAGS += -DART_USE_PORTABLE_COMPILER=1 + endif + + LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime + + ifeq ($$(art_target_or_host),host) + LOCAL_LDLIBS := -ldl -lpthread + endif + LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk + LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk + ifeq ($$(art_target_or_host),target) + LOCAL_SHARED_LIBRARIES += libcutils + include $(LLVM_GEN_INTRINSICS_MK) + include $(LLVM_DEVICE_BUILD_MK) + include $(BUILD_SHARED_LIBRARY) + else # host + LOCAL_STATIC_LIBRARIES += libcutils + include $(LLVM_GEN_INTRINSICS_MK) + include $(LLVM_HOST_BUILD_MK) + include $(BUILD_HOST_SHARED_LIBRARY) + endif + + ifeq ($$(art_target_or_host),target) + ifeq ($$(art_ndebug_or_debug),debug) + $(TARGET_OUT_EXECUTABLES)/dex2oatd: $$(LOCAL_INSTALLED_MODULE) + else + $(TARGET_OUT_EXECUTABLES)/dex2oat: $$(LOCAL_INSTALLED_MODULE) + endif + else # host + ifeq ($$(art_ndebug_or_debug),debug) + $(HOST_OUT_EXECUTABLES)/dex2oatd: $$(LOCAL_INSTALLED_MODULE) + else + $(HOST_OUT_EXECUTABLES)/dex2oat: $$(LOCAL_INSTALLED_MODULE) + endif + endif + +endef + +ifeq ($(ART_BUILD_TARGET_NDEBUG),true) + $(eval $(call build-libart-compiler,target,ndebug)) +endif +ifeq ($(ART_BUILD_TARGET_DEBUG),true) + $(eval $(call build-libart-compiler,target,debug)) +endif +ifeq ($(WITH_HOST_DALVIK),true) + # 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_NDEBUG),true) + $(eval $(call build-libart-compiler,host,ndebug)) + endif + ifeq ($(ART_BUILD_DEBUG),true) + $(eval $(call build-libart-compiler,host,debug)) + endif +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)) + +$(DEX2OAT): $(TARGET_OUT_SHARED_LIBRARIES)/libcompiler_rt.a + +endif +endif diff --git a/compiler/buffered_output_stream.cc b/compiler/buffered_output_stream.cc new file mode 100644 index 0000000000000000000000000000000000000000..81a58f62841c348039ebd9f71fcca905bb141aee --- /dev/null +++ b/compiler/buffered_output_stream.cc @@ -0,0 +1,59 @@ +/* + * 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 "buffered_output_stream.h" + +#include + +namespace art { + +BufferedOutputStream::BufferedOutputStream(OutputStream* out) + : OutputStream(out->GetLocation()), out_(out), used_(0) {} + +bool BufferedOutputStream::WriteFully(const void* buffer, int64_t byte_count) { + if (byte_count > kBufferSize) { + Flush(); + return out_->WriteFully(buffer, byte_count); + } + if (used_ + byte_count > kBufferSize) { + bool success = Flush(); + if (!success) { + return false; + } + } + const uint8_t* src = reinterpret_cast(buffer); + memcpy(&buffer_[used_], src, byte_count); + used_ += byte_count; + return true; +} + +bool BufferedOutputStream::Flush() { + bool success = true; + if (used_ > 0) { + success = out_->WriteFully(&buffer_[0], used_); + used_ = 0; + } + return success; +} + +off_t BufferedOutputStream::Seek(off_t offset, Whence whence) { + if (!Flush()) { + return -1; + } + return out_->Seek(offset, whence); +} + +} // namespace art diff --git a/compiler/buffered_output_stream.h b/compiler/buffered_output_stream.h new file mode 100644 index 0000000000000000000000000000000000000000..7d874fbc5c32c662f05dbcb8eba81f962b24aaf3 --- /dev/null +++ b/compiler/buffered_output_stream.h @@ -0,0 +1,54 @@ +/* + * 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_BUFFERED_OUTPUT_STREAM_H_ +#define ART_COMPILER_BUFFERED_OUTPUT_STREAM_H_ + +#include "output_stream.h" + +#include "globals.h" + +namespace art { + +class BufferedOutputStream : public OutputStream { + public: + explicit BufferedOutputStream(OutputStream* out); + + virtual ~BufferedOutputStream() { + delete out_; + } + + virtual bool WriteFully(const void* buffer, int64_t byte_count); + + virtual off_t Seek(off_t offset, Whence whence); + + private: + static const size_t kBufferSize = 8 * KB; + + bool Flush(); + + OutputStream* const out_; + + uint8_t buffer_[kBufferSize]; + + size_t used_; + + DISALLOW_COPY_AND_ASSIGN(BufferedOutputStream); +}; + +} // namespace art + +#endif // ART_COMPILER_BUFFERED_OUTPUT_STREAM_H_ diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc new file mode 100644 index 0000000000000000000000000000000000000000..29ff390678c03c7ab8b41688dd21d12bea85e03d --- /dev/null +++ b/compiler/compiled_method.cc @@ -0,0 +1,170 @@ +/* + * 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 "compiled_method.h" +#include "driver/compiler_driver.h" + +namespace art { + +CompiledCode::CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set, + const std::vector& code) + : compiler_driver_(compiler_driver), instruction_set_(instruction_set), code_(nullptr) { + SetCode(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), symbol_(symbol) { + CHECK_NE(elf_object.size(), 0U); + CHECK_NE(symbol.size(), 0U); + std::vector 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(temp_code); +} + +void CompiledCode::SetCode(const std::vector& code) { + CHECK(!code.empty()); + code_ = compiler_driver_->DeduplicateCode(code); +} + +uint32_t CompiledCode::AlignCode(uint32_t offset) const { + return AlignCode(offset, instruction_set_); +} + +uint32_t CompiledCode::AlignCode(uint32_t offset, InstructionSet instruction_set) { + switch (instruction_set) { + case kArm: + case kThumb2: + return RoundUp(offset, kArmAlignment); + case kMips: + return RoundUp(offset, kMipsAlignment); + case kX86: + return RoundUp(offset, kX86Alignment); + default: + LOG(FATAL) << "Unknown InstructionSet: " << instruction_set; + return 0; + } +} + +size_t CompiledCode::CodeDelta() const { + switch (instruction_set_) { + case kArm: + case kMips: + case kX86: + return 0; + case kThumb2: { + // +1 to set the low-order bit so a BLX will switch to Thumb mode + return 1; + } + default: + LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_; + return 0; + } +} + +const void* CompiledCode::CodePointer(const void* code_pointer, + InstructionSet instruction_set) { + switch (instruction_set) { + case kArm: + case kMips: + case kX86: + return code_pointer; + case kThumb2: { + uintptr_t address = reinterpret_cast(code_pointer); + // Set the low-order bit so a BLX will switch to Thumb mode + address |= 0x1; + return reinterpret_cast(address); + } + default: + LOG(FATAL) << "Unknown InstructionSet: " << instruction_set; + return NULL; + } +} + +#if defined(ART_USE_PORTABLE_COMPILER) +const std::string& CompiledCode::GetSymbol() const { + CHECK_NE(0U, symbol_.size()); + return symbol_; +} + +const std::vector& CompiledCode::GetOatdataOffsetsToCompliledCodeOffset() const { + CHECK_NE(0U, oatdata_offsets_to_compiled_code_offset_.size()) << symbol_; + return oatdata_offsets_to_compiled_code_offset_; +} + +void CompiledCode::AddOatdataOffsetToCompliledCodeOffset(uint32_t offset) { + oatdata_offsets_to_compiled_code_offset_.push_back(offset); +} +#endif + +CompiledMethod::CompiledMethod(CompilerDriver& driver, + InstructionSet instruction_set, + const std::vector& code, + const size_t frame_size_in_bytes, + const uint32_t core_spill_mask, + const uint32_t fp_spill_mask, + const std::vector& mapping_table, + const std::vector& vmap_table, + const std::vector& native_gc_map) + : 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), + mapping_table_(driver.DeduplicateMappingTable(mapping_table)), + vmap_table_(driver.DeduplicateVMapTable(vmap_table)), + gc_map_(driver.DeduplicateGCMap(native_gc_map)) { +} + +CompiledMethod::CompiledMethod(CompilerDriver& driver, + InstructionSet instruction_set, + const std::vector& code, + const size_t frame_size_in_bytes, + const uint32_t core_spill_mask, + const uint32_t fp_spill_mask) + : 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) { + mapping_table_ = driver.DeduplicateMappingTable(std::vector()); + vmap_table_ = driver.DeduplicateVMapTable(std::vector()); + gc_map_ = driver.DeduplicateGCMap(std::vector()); +} + +// Constructs a CompiledMethod for the Portable compiler. +CompiledMethod::CompiledMethod(CompilerDriver& driver, InstructionSet instruction_set, + const std::string& code, const std::vector& 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), gc_map_(driver.DeduplicateGCMap(gc_map)) { + mapping_table_ = driver.DeduplicateMappingTable(std::vector()); + vmap_table_ = driver.DeduplicateVMapTable(std::vector()); +} + +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) { + mapping_table_ = driver.DeduplicateMappingTable(std::vector()); + vmap_table_ = driver.DeduplicateVMapTable(std::vector()); + gc_map_ = driver.DeduplicateGCMap(std::vector()); +} + +} // namespace art diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h new file mode 100644 index 0000000000000000000000000000000000000000..e4fedf1ab47c0be9e3713c9f9208728c016505c9 --- /dev/null +++ b/compiler/compiled_method.h @@ -0,0 +1,176 @@ +/* + * 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_COMPILED_METHOD_H_ +#define ART_COMPILER_COMPILED_METHOD_H_ + +#include +#include + +#include "instruction_set.h" +#include "utils.h" +#include "UniquePtr.h" + +namespace llvm { + class Function; +} // namespace llvm + +namespace art { + +class CompilerDriver; + +class CompiledCode { + public: + // For Quick to supply an code blob + CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set, + const std::vector& code); + + // For Portable to supply an ELF object + CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set, + const std::string& elf_object, const std::string &symbol); + + InstructionSet GetInstructionSet() const { + return instruction_set_; + } + + const std::vector& GetCode() const { + return *code_; + } + + void SetCode(const std::vector& code); + + bool operator==(const CompiledCode& rhs) const { + return (code_ == rhs.code_); + } + + // To align an offset from a page-aligned value to make it suitable + // for code storage. For example on ARM, to ensure that PC relative + // valu computations work out as expected. + uint32_t AlignCode(uint32_t offset) const; + static uint32_t AlignCode(uint32_t offset, InstructionSet instruction_set); + + // returns the difference between the code address and a usable PC. + // mainly to cope with kThumb2 where the lower bit must be set. + size_t CodeDelta() const; + + // Returns a pointer suitable for invoking the code at the argument + // code_pointer address. Mainly to cope with kThumb2 where the + // lower bit must be set to indicate Thumb mode. + static const void* CodePointer(const void* code_pointer, + InstructionSet instruction_set); + +#if defined(ART_USE_PORTABLE_COMPILER) + const std::string& GetSymbol() const; + const std::vector& GetOatdataOffsetsToCompliledCodeOffset() const; + void AddOatdataOffsetToCompliledCodeOffset(uint32_t offset); +#endif + + private: + CompilerDriver* compiler_driver_; + + const InstructionSet instruction_set_; + + // Used to store the PIC code for Quick and an ELF image for portable. + std::vector* code_; + + // Used for the Portable ELF symbol name. + const std::string symbol_; + + // There are offsets from the oatdata symbol to where the offset to + // the compiled method will be found. These are computed by the + // OatWriter and then used by the ElfWriter to add relocations so + // that MCLinker can update the values to the location in the linked .so. + std::vector oatdata_offsets_to_compiled_code_offset_; +}; + +class CompiledMethod : public CompiledCode { + public: + // Constructs a CompiledMethod for the non-LLVM compilers. + CompiledMethod(CompilerDriver& driver, + InstructionSet instruction_set, + const std::vector& code, + const size_t frame_size_in_bytes, + const uint32_t core_spill_mask, + const uint32_t fp_spill_mask, + const std::vector& mapping_table, + const std::vector& vmap_table, + const std::vector& native_gc_map); + + // Constructs a CompiledMethod for the JniCompiler. + CompiledMethod(CompilerDriver& driver, + InstructionSet instruction_set, + const std::vector& code, + const size_t frame_size_in_bytes, + const uint32_t core_spill_mask, + const uint32_t fp_spill_mask); + + // Constructs a CompiledMethod for the Portable compiler. + CompiledMethod(CompilerDriver& driver, InstructionSet instruction_set, const std::string& code, + const std::vector& 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() {} + + size_t GetFrameSizeInBytes() const { + return frame_size_in_bytes_; + } + + uint32_t GetCoreSpillMask() const { + return core_spill_mask_; + } + + uint32_t GetFpSpillMask() const { + return fp_spill_mask_; + } + + const std::vector& GetMappingTable() const { + DCHECK(mapping_table_ != nullptr); + return *mapping_table_; + } + + const std::vector& GetVmapTable() const { + DCHECK(vmap_table_ != nullptr); + return *vmap_table_; + } + + const std::vector& GetGcMap() const { + DCHECK(gc_map_ != nullptr); + return *gc_map_; + } + + private: + // For quick code, the size of the activation used by the code. + const size_t frame_size_in_bytes_; + // For quick code, a bit mask describing spilled GPR callee-save registers. + const uint32_t core_spill_mask_; + // For quick code, a bit mask describing spilled FPR callee-save registers. + const uint32_t fp_spill_mask_; + // 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* mapping_table_; + // For quick code, a uleb128 encoded map from GPR/FPR register to dex register. Size prefixed. + std::vector* 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* gc_map_; +}; + +} // namespace art + +#endif // ART_COMPILER_COMPILED_METHOD_H_ diff --git a/compiler/dex/arena_allocator.cc b/compiler/dex/arena_allocator.cc new file mode 100644 index 0000000000000000000000000000000000000000..36393e73877ee79c60036a1ff0ae637e5fb53270 --- /dev/null +++ b/compiler/dex/arena_allocator.cc @@ -0,0 +1,189 @@ +/* + * 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 "compiler_internals.h" +#include "dex_file-inl.h" +#include "arena_allocator.h" +#include "base/logging.h" +#include "base/mutex.h" + +namespace art { + +// Memmap is a bit slower than malloc according to my measurements. +static constexpr bool kUseMemMap = false; +static constexpr bool kUseMemSet = true && kUseMemMap; + +static const char* alloc_names[ArenaAllocator::kNumAllocKinds] = { + "Misc ", + "BasicBlock ", + "LIR ", + "MIR ", + "DataFlow ", + "GrowList ", + "GrowBitMap ", + "Dalvik2SSA ", + "DebugInfo ", + "Successor ", + "RegAlloc ", + "Data ", + "Preds ", +}; + +Arena::Arena(size_t size) + : bytes_allocated_(0), + map_(nullptr), + next_(nullptr) { + if (kUseMemMap) { + map_ = MemMap::MapAnonymous("dalvik-arena", NULL, size, PROT_READ | PROT_WRITE); + memory_ = map_->Begin(); + size_ = map_->Size(); + } else { + memory_ = reinterpret_cast(calloc(1, size)); + size_ = size; + } +} + +Arena::~Arena() { + if (kUseMemMap) { + delete map_; + } else { + free(reinterpret_cast(memory_)); + } +} + +void Arena::Reset() { + if (bytes_allocated_) { + if (kUseMemSet || !kUseMemMap) { + memset(Begin(), 0, bytes_allocated_); + } else { + madvise(Begin(), bytes_allocated_, MADV_DONTNEED); + } + bytes_allocated_ = 0; + } +} + +ArenaPool::ArenaPool() + : lock_("Arena pool lock"), + free_arenas_(nullptr) { +} + +ArenaPool::~ArenaPool() { + while (free_arenas_ != nullptr) { + auto* arena = free_arenas_; + free_arenas_ = free_arenas_->next_; + delete arena; + } +} + +Arena* ArenaPool::AllocArena(size_t size) { + Thread* self = Thread::Current(); + Arena* ret = nullptr; + { + MutexLock lock(self, lock_); + if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) { + ret = free_arenas_; + free_arenas_ = free_arenas_->next_; + } + } + if (ret == nullptr) { + ret = new Arena(size); + } + ret->Reset(); + return ret; +} + +void ArenaPool::FreeArena(Arena* arena) { + Thread* self = Thread::Current(); + { + MutexLock lock(self, lock_); + arena->next_ = free_arenas_; + free_arenas_ = arena; + } +} + +size_t ArenaAllocator::BytesAllocated() const { + size_t total = 0; + for (int i = 0; i < kNumAllocKinds; i++) { + total += alloc_stats_[i]; + } + return total; +} + +ArenaAllocator::ArenaAllocator(ArenaPool* pool) + : pool_(pool), + begin_(nullptr), + end_(nullptr), + ptr_(nullptr), + arena_head_(nullptr), + num_allocations_(0) { + memset(&alloc_stats_[0], 0, sizeof(alloc_stats_)); +} + +void ArenaAllocator::UpdateBytesAllocated() { + if (arena_head_ != nullptr) { + // Update how many bytes we have allocated into the arena so that the arena pool knows how + // much memory to zero out. + arena_head_->bytes_allocated_ = ptr_ - begin_; + } +} + +ArenaAllocator::~ArenaAllocator() { + // Reclaim all the arenas by giving them back to the thread pool. + UpdateBytesAllocated(); + while (arena_head_ != nullptr) { + Arena* arena = arena_head_; + arena_head_ = arena_head_->next_; + pool_->FreeArena(arena); + } +} + +void ArenaAllocator::ObtainNewArenaForAllocation(size_t allocation_size) { + UpdateBytesAllocated(); + Arena* new_arena = pool_->AllocArena(std::max(Arena::kDefaultSize, allocation_size)); + new_arena->next_ = arena_head_; + arena_head_ = new_arena; + // Update our internal data structures. + ptr_ = begin_ = new_arena->Begin(); + end_ = new_arena->End(); +} + +// Dump memory usage stats. +void ArenaAllocator::DumpMemStats(std::ostream& os) const { + size_t malloc_bytes = 0; + // Start out with how many lost bytes we have in the arena we are currently allocating into. + size_t lost_bytes(end_ - ptr_); + size_t num_arenas = 0; + for (Arena* arena = arena_head_; arena != nullptr; arena = arena->next_) { + malloc_bytes += arena->Size(); + if (arena != arena_head_) { + lost_bytes += arena->RemainingSpace(); + } + ++num_arenas; + } + const size_t bytes_allocated = BytesAllocated(); + os << " MEM: used: " << bytes_allocated << ", allocated: " << malloc_bytes + << ", lost: " << lost_bytes << "\n"; + if (num_allocations_ != 0) { + os << "Number of arenas allocated: " << num_arenas << ", Number of allocations: " + << num_allocations_ << ", avg size: " << bytes_allocated / num_allocations_ << "\n"; + } + os << "===== Allocation by kind\n"; + for (int i = 0; i < kNumAllocKinds; i++) { + os << alloc_names[i] << std::setw(10) << alloc_stats_[i] << "\n"; + } +} + +} // namespace art diff --git a/compiler/dex/arena_allocator.h b/compiler/dex/arena_allocator.h new file mode 100644 index 0000000000000000000000000000000000000000..dda52a2ed0a8bd1c76dfcaac203bab8e66a1939b --- /dev/null +++ b/compiler/dex/arena_allocator.h @@ -0,0 +1,155 @@ +/* + * 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_ARENA_ALLOCATOR_H_ +#define ART_COMPILER_DEX_ARENA_ALLOCATOR_H_ + +#include +#include + +#include "base/mutex.h" +#include "compiler_enums.h" +#include "mem_map.h" + +namespace art { + +class Arena; +class ArenaPool; +class ArenaAllocator; + +class Arena { + public: + static constexpr size_t kDefaultSize = 128 * KB; + explicit Arena(size_t size = kDefaultSize); + ~Arena(); + void Reset(); + uint8_t* Begin() { + return memory_; + } + + uint8_t* End() { + return memory_ + size_; + } + + size_t Size() const { + return size_; + } + + size_t RemainingSpace() const { + return Size() - bytes_allocated_; + } + + private: + size_t bytes_allocated_; + uint8_t* memory_; + size_t size_; + MemMap* map_; + Arena* next_; + friend class ArenaPool; + friend class ArenaAllocator; + DISALLOW_COPY_AND_ASSIGN(Arena); +}; + +class ArenaPool { + public: + ArenaPool(); + ~ArenaPool(); + Arena* AllocArena(size_t size); + void FreeArena(Arena* arena); + + private: + Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + Arena* free_arenas_ GUARDED_BY(lock_); + DISALLOW_COPY_AND_ASSIGN(ArenaPool); +}; + +class ArenaAllocator { + public: + // Type of allocation for memory tuning. + enum ArenaAllocKind { + kAllocMisc, + kAllocBB, + kAllocLIR, + kAllocMIR, + kAllocDFInfo, + kAllocGrowableArray, + kAllocGrowableBitMap, + kAllocDalvikToSSAMap, + kAllocDebugInfo, + kAllocSuccessor, + kAllocRegAlloc, + kAllocData, + kAllocPredecessors, + kNumAllocKinds + }; + + static constexpr bool kCountAllocations = false; + + explicit ArenaAllocator(ArenaPool* pool); + ~ArenaAllocator(); + + // Returns zeroed memory. + void* Alloc(size_t bytes, ArenaAllocKind kind) ALWAYS_INLINE { + bytes = (bytes + 3) & ~3; + if (UNLIKELY(ptr_ + bytes > end_)) { + // Obtain a new block. + ObtainNewArenaForAllocation(bytes); + if (UNLIKELY(ptr_ == nullptr)) { + return nullptr; + } + } + if (kCountAllocations) { + alloc_stats_[kind] += bytes; + ++num_allocations_; + } + uint8_t* ret = ptr_; + ptr_ += bytes; + return ret; + } + + void ObtainNewArenaForAllocation(size_t allocation_size); + size_t BytesAllocated() const; + void DumpMemStats(std::ostream& os) const; + + private: + void UpdateBytesAllocated(); + + ArenaPool* pool_; + uint8_t* begin_; + uint8_t* end_; + uint8_t* ptr_; + Arena* arena_head_; + + // Statistics. + size_t num_allocations_; + size_t alloc_stats_[kNumAllocKinds]; // Bytes used by various allocation kinds. + + DISALLOW_COPY_AND_ASSIGN(ArenaAllocator); +}; // ArenaAllocator + +struct MemStats { + public: + void Dump(std::ostream& os) const { + arena_.DumpMemStats(os); + } + explicit MemStats(const ArenaAllocator &arena) : arena_(arena) {} + private: + const ArenaAllocator &arena_; +}; // MemStats + +} // namespace art + +#endif // ART_COMPILER_DEX_ARENA_ALLOCATOR_H_ diff --git a/compiler/dex/arena_bit_vector.cc b/compiler/dex/arena_bit_vector.cc new file mode 100644 index 0000000000000000000000000000000000000000..3fa92952767b9e37bc9aa0cc63023ccbabf13f08 --- /dev/null +++ b/compiler/dex/arena_bit_vector.cc @@ -0,0 +1,143 @@ +/* + * 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 "compiler_internals.h" +#include "dex_file-inl.h" + +namespace art { + +// TODO: profile to make sure this is still a win relative to just using shifted masks. +static uint32_t check_masks[32] = { + 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, + 0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200, + 0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000, + 0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000, + 0x00100000, 0x00200000, 0x00400000, 0x00800000, 0x01000000, + 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, + 0x40000000, 0x80000000 }; + +ArenaBitVector::ArenaBitVector(ArenaAllocator* arena, unsigned int start_bits, + bool expandable, OatBitMapKind kind) + : arena_(arena), + expandable_(expandable), + kind_(kind), + storage_size_((start_bits + 31) >> 5), + storage_(static_cast(arena_->Alloc(storage_size_ * sizeof(uint32_t), + ArenaAllocator::kAllocGrowableBitMap))) { + DCHECK_EQ(sizeof(storage_[0]), 4U); // Assuming 32-bit units. +} + +/* + * Determine whether or not the specified bit is set. + */ +bool ArenaBitVector::IsBitSet(unsigned int num) { + DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8); + + unsigned int val = storage_[num >> 5] & check_masks[num & 0x1f]; + return (val != 0); +} + +// Mark all bits bit as "clear". +void ArenaBitVector::ClearAllBits() { + memset(storage_, 0, storage_size_ * sizeof(uint32_t)); +} + +// Mark the specified bit as "set". +/* + * TUNING: this could have pathologically bad growth/expand behavior. Make sure we're + * not using it badly or change resize mechanism. + */ +void ArenaBitVector::SetBit(unsigned int num) { + if (num >= storage_size_ * sizeof(uint32_t) * 8) { + DCHECK(expandable_) << "Attempted to expand a non-expandable bitmap to position " << num; + + /* Round up to word boundaries for "num+1" bits */ + unsigned int new_size = (num + 1 + 31) >> 5; + DCHECK_GT(new_size, storage_size_); + uint32_t *new_storage = + static_cast(arena_->Alloc(new_size * sizeof(uint32_t), + ArenaAllocator::kAllocGrowableBitMap)); + memcpy(new_storage, storage_, storage_size_ * sizeof(uint32_t)); + // Zero out the new storage words. + memset(&new_storage[storage_size_], 0, (new_size - storage_size_) * sizeof(uint32_t)); + // TOTO: collect stats on space wasted because of resize. + storage_ = new_storage; + storage_size_ = new_size; + } + + storage_[num >> 5] |= check_masks[num & 0x1f]; +} + +// Mark the specified bit as "unset". +void ArenaBitVector::ClearBit(unsigned int num) { + DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8); + storage_[num >> 5] &= ~check_masks[num & 0x1f]; +} + +// Copy a whole vector to the other. Sizes must match. +void ArenaBitVector::Copy(ArenaBitVector* src) { + DCHECK_EQ(storage_size_, src->GetStorageSize()); + memcpy(storage_, src->GetRawStorage(), sizeof(uint32_t) * storage_size_); +} + +// Intersect with another bit vector. Sizes and expandability must be the same. +void ArenaBitVector::Intersect(const ArenaBitVector* src) { + DCHECK_EQ(storage_size_, src->GetStorageSize()); + DCHECK_EQ(expandable_, src->IsExpandable()); + for (unsigned int idx = 0; idx < storage_size_; idx++) { + storage_[idx] &= src->GetRawStorageWord(idx); + } +} + +/* + * Union with another bit vector. Sizes and expandability must be the same. + */ +void ArenaBitVector::Union(const ArenaBitVector* src) { + DCHECK_EQ(storage_size_, src->GetStorageSize()); + DCHECK_EQ(expandable_, src->IsExpandable()); + for (unsigned int idx = 0; idx < storage_size_; idx++) { + storage_[idx] |= src->GetRawStorageWord(idx); + } +} + +// Count the number of bits that are set. +int ArenaBitVector::NumSetBits() { + unsigned int count = 0; + + for (unsigned int word = 0; word < storage_size_; word++) { + count += __builtin_popcount(storage_[word]); + } + return count; +} + +/* + * Mark specified number of bits as "set". Cannot set all bits like ClearAll + * since there might be unused bits - setting those to one will confuse the + * iterator. + */ +void ArenaBitVector::SetInitialBits(unsigned int num_bits) { + DCHECK_LE(((num_bits + 31) >> 5), storage_size_); + unsigned int idx; + for (idx = 0; idx < (num_bits >> 5); idx++) { + storage_[idx] = -1; + } + unsigned int rem_num_bits = num_bits & 0x1f; + if (rem_num_bits) { + storage_[idx] = (1 << rem_num_bits) - 1; + } +} + +} // namespace art diff --git a/compiler/dex/arena_bit_vector.h b/compiler/dex/arena_bit_vector.h new file mode 100644 index 0000000000000000000000000000000000000000..8bcd628dc0a2af8a1bfd7c2c541e9ff9ac3b9436 --- /dev/null +++ b/compiler/dex/arena_bit_vector.h @@ -0,0 +1,126 @@ +/* + * 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_ARENA_BIT_VECTOR_H_ +#define ART_COMPILER_DEX_ARENA_BIT_VECTOR_H_ + +#include +#include +#include "compiler_enums.h" +#include "arena_allocator.h" + +namespace art { + +/* + * Expanding bitmap, used for tracking resources. Bits are numbered starting + * from zero. All operations on a BitVector are unsynchronized. + */ +class ArenaBitVector { + public: + class Iterator { + public: + explicit Iterator(ArenaBitVector* bit_vector) + : p_bits_(bit_vector), + bit_storage_(bit_vector->GetRawStorage()), + bit_index_(0), + bit_size_(p_bits_->storage_size_ * sizeof(uint32_t) * 8) {} + + // Return the position of the next set bit. -1 means end-of-element reached. + int Next() { + // Did anything obviously change since we started? + DCHECK_EQ(bit_size_, p_bits_->GetStorageSize() * sizeof(uint32_t) * 8); + DCHECK_EQ(bit_storage_, p_bits_->GetRawStorage()); + + if (bit_index_ >= bit_size_) return -1; + + uint32_t word_index = bit_index_ / 32; + uint32_t word = bit_storage_[word_index]; + // Mask out any bits in the first word we've already considered. + word >>= bit_index_ & 0x1f; + if (word == 0) { + bit_index_ &= ~0x1f; + do { + word_index++; + if ((word_index * 32) >= bit_size_) { + bit_index_ = bit_size_; + return -1; + } + word = bit_storage_[word_index]; + bit_index_ += 32; + } while (word == 0); + } + bit_index_ += CTZ(word) + 1; + return bit_index_ - 1; + } + + static void* operator new(size_t size, ArenaAllocator* arena) { + return arena->Alloc(sizeof(ArenaBitVector::Iterator), + ArenaAllocator::kAllocGrowableBitMap); + }; + static void operator delete(void* p) {} // Nop. + + private: + ArenaBitVector* const p_bits_; + uint32_t* const bit_storage_; + uint32_t bit_index_; // Current index (size in bits). + const uint32_t bit_size_; // Size of vector in bits. + }; + + ArenaBitVector(ArenaAllocator* arena, unsigned int start_bits, bool expandable, + OatBitMapKind kind = kBitMapMisc); + ~ArenaBitVector() {} + + static void* operator new(size_t size, ArenaAllocator* arena) { + return arena->Alloc(sizeof(ArenaBitVector), ArenaAllocator::kAllocGrowableBitMap); + } + static void operator delete(void* p) {} // Nop. + + void SetBit(unsigned int num); + void ClearBit(unsigned int num); + void MarkAllBits(bool set); + void DebugBitVector(char* msg, int length); + bool IsBitSet(unsigned int num); + void ClearAllBits(); + void SetInitialBits(unsigned int num_bits); + void Copy(ArenaBitVector* src); + void Intersect(const ArenaBitVector* src2); + void Union(const ArenaBitVector* src); + // Are we equal to another bit vector? Note: expandability attributes must also match. + bool Equal(const ArenaBitVector* src) { + return (storage_size_ == src->GetStorageSize()) && + (expandable_ == src->IsExpandable()) && + (memcmp(storage_, src->GetRawStorage(), storage_size_ * 4) == 0); + } + int NumSetBits(); + + uint32_t GetStorageSize() const { return storage_size_; } + bool IsExpandable() const { return expandable_; } + uint32_t GetRawStorageWord(size_t idx) const { return storage_[idx]; } + uint32_t* GetRawStorage() { return storage_; } + const uint32_t* GetRawStorage() const { return storage_; } + + private: + ArenaAllocator* const arena_; + const bool expandable_; // expand bitmap if we run out? + const OatBitMapKind kind_; // for memory use tuning. + uint32_t storage_size_; // current size, in 32-bit words. + uint32_t* storage_; +}; + + +} // namespace art + +#endif // ART_COMPILER_DEX_ARENA_BIT_VECTOR_H_ diff --git a/compiler/dex/backend.h b/compiler/dex/backend.h new file mode 100644 index 0000000000000000000000000000000000000000..01959b77f5810de991ee1f79b2f9f8387a2c05df --- /dev/null +++ b/compiler/dex/backend.h @@ -0,0 +1,38 @@ +/* + * 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_ + +#include "compiled_method.h" +#include "arena_allocator.h" + +namespace art { + +class Backend { + public: + virtual ~Backend() {} + virtual void Materialize() = 0; + virtual CompiledMethod* GetCompiledMethod() = 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/compiler_enums.h b/compiler/dex/compiler_enums.h new file mode 100644 index 0000000000000000000000000000000000000000..97a682f2aa9c098c64b98950e3c12e5a0ee1db34 --- /dev/null +++ b/compiler/dex/compiler_enums.h @@ -0,0 +1,417 @@ +/* + * 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_DEX_COMPILER_ENUMS_H_ +#define ART_COMPILER_DEX_COMPILER_ENUMS_H_ + +#include "dex_instruction.h" + +namespace art { + +enum RegisterClass { + kCoreReg, + kFPReg, + kAnyReg, +}; + +enum SpecialTargetRegister { + kSelf, // Thread pointer. + kSuspend, // Used to reduce suspend checks for some targets. + kLr, + kPc, + kSp, + kArg0, + kArg1, + kArg2, + kArg3, + kFArg0, + kFArg1, + kFArg2, + kFArg3, + kRet0, + kRet1, + kInvokeTgt, + kCount +}; + +enum RegLocationType { + kLocDalvikFrame = 0, // Normal Dalvik register + kLocPhysReg, + kLocCompilerTemp, + kLocInvalid +}; + +enum BBType { + kEntryBlock, + kDalvikByteCode, + kExitBlock, + kExceptionHandling, + kDead, +}; + +/* + * Def/Use encoding in 64-bit use_mask/def_mask. Low positions used for target-specific + * registers (and typically use the register number as the position). High positions + * reserved for common and abstract resources. + */ + +enum ResourceEncodingPos { + kMustNotAlias = 63, + kHeapRef = 62, // Default memory reference type. + kLiteral = 61, // Literal pool memory reference. + kDalvikReg = 60, // Dalvik v_reg memory reference. + kFPStatus = 59, + kCCode = 58, + kLowestCommonResource = kCCode +}; + +// Shared pseudo opcodes - must be < 0. +enum LIRPseudoOpcode { + kPseudoExportedPC = -16, + kPseudoSafepointPC = -15, + kPseudoIntrinsicRetry = -14, + kPseudoSuspendTarget = -13, + kPseudoThrowTarget = -12, + kPseudoCaseLabel = -11, + kPseudoMethodEntry = -10, + kPseudoMethodExit = -9, + kPseudoBarrier = -8, + kPseudoEntryBlock = -7, + kPseudoExitBlock = -6, + kPseudoTargetLabel = -5, + kPseudoDalvikByteCodeBoundary = -4, + kPseudoPseudoAlign4 = -3, + kPseudoEHBlockLabel = -2, + kPseudoNormalBlockLabel = -1, +}; + +enum ExtendedMIROpcode { + kMirOpFirst = kNumPackedOpcodes, + kMirOpPhi = kMirOpFirst, + kMirOpCopy, + kMirOpFusedCmplFloat, + kMirOpFusedCmpgFloat, + kMirOpFusedCmplDouble, + kMirOpFusedCmpgDouble, + kMirOpFusedCmpLong, + kMirOpNop, + kMirOpNullCheck, + kMirOpRangeCheck, + kMirOpDivZeroCheck, + kMirOpCheck, + kMirOpCheckPart2, + kMirOpSelect, + kMirOpLast, +}; + +enum MIROptimizationFlagPositons { + kMIRIgnoreNullCheck = 0, + kMIRNullCheckOnly, + kMIRIgnoreRangeCheck, + kMIRRangeCheckOnly, + kMIRInlined, // Invoke is inlined (ie dead). + kMIRInlinedPred, // Invoke is inlined via prediction. + kMIRCallee, // Instruction is inlined from callee. + kMIRIgnoreSuspendCheck, + kMIRDup, + kMIRMark, // Temporary node mark. +}; + +// For successor_block_list. +enum BlockListType { + kNotUsed = 0, + kCatch, + kPackedSwitch, + kSparseSwitch, +}; + +enum AssemblerStatus { + kSuccess, + kRetryAll, +}; + +enum OpSize { + kWord, + kLong, + kSingle, + kDouble, + kUnsignedHalf, + kSignedHalf, + kUnsignedByte, + kSignedByte, +}; + +std::ostream& operator<<(std::ostream& os, const OpSize& kind); + +enum OpKind { + kOpMov, + kOpMvn, + kOpCmp, + kOpLsl, + kOpLsr, + kOpAsr, + kOpRor, + kOpNot, + kOpAnd, + kOpOr, + kOpXor, + kOpNeg, + kOpAdd, + kOpAdc, + kOpSub, + kOpSbc, + kOpRsub, + kOpMul, + kOpDiv, + kOpRem, + kOpBic, + kOpCmn, + kOpTst, + kOpBkpt, + kOpBlx, + kOpPush, + kOpPop, + kOp2Char, + kOp2Short, + kOp2Byte, + kOpCondBr, + kOpUncondBr, + kOpBx, + kOpInvalid, +}; + +std::ostream& operator<<(std::ostream& os, const OpKind& kind); + +enum ConditionCode { + kCondEq, // equal + kCondNe, // not equal + kCondCs, // carry set (unsigned less than) + kCondUlt = kCondCs, + kCondCc, // carry clear (unsigned greater than or same) + kCondUge = kCondCc, + kCondMi, // minus + kCondPl, // plus, positive or zero + kCondVs, // overflow + kCondVc, // no overflow + kCondHi, // unsigned greater than + kCondLs, // unsigned lower or same + kCondGe, // signed greater than or equal + kCondLt, // signed less than + kCondGt, // signed greater than + kCondLe, // signed less than or equal + kCondAl, // always + kCondNv, // never +}; + +std::ostream& operator<<(std::ostream& os, const ConditionCode& kind); + +// Target specific condition encodings +enum ArmConditionCode { + kArmCondEq = 0x0, // 0000 + kArmCondNe = 0x1, // 0001 + kArmCondCs = 0x2, // 0010 + kArmCondCc = 0x3, // 0011 + kArmCondMi = 0x4, // 0100 + kArmCondPl = 0x5, // 0101 + kArmCondVs = 0x6, // 0110 + kArmCondVc = 0x7, // 0111 + kArmCondHi = 0x8, // 1000 + kArmCondLs = 0x9, // 1001 + kArmCondGe = 0xa, // 1010 + kArmCondLt = 0xb, // 1011 + kArmCondGt = 0xc, // 1100 + kArmCondLe = 0xd, // 1101 + kArmCondAl = 0xe, // 1110 + kArmCondNv = 0xf, // 1111 +}; + +std::ostream& operator<<(std::ostream& os, const ArmConditionCode& kind); + +enum X86ConditionCode { + kX86CondO = 0x0, // overflow + kX86CondNo = 0x1, // not overflow + + kX86CondB = 0x2, // below + kX86CondNae = kX86CondB, // not-above-equal + kX86CondC = kX86CondB, // carry + + kX86CondNb = 0x3, // not-below + kX86CondAe = kX86CondNb, // above-equal + kX86CondNc = kX86CondNb, // not-carry + + kX86CondZ = 0x4, // zero + kX86CondEq = kX86CondZ, // equal + + kX86CondNz = 0x5, // not-zero + kX86CondNe = kX86CondNz, // not-equal + + kX86CondBe = 0x6, // below-equal + kX86CondNa = kX86CondBe, // not-above + + kX86CondNbe = 0x7, // not-below-equal + kX86CondA = kX86CondNbe, // above + + kX86CondS = 0x8, // sign + kX86CondNs = 0x9, // not-sign + + kX86CondP = 0xa, // 8-bit parity even + kX86CondPE = kX86CondP, + + kX86CondNp = 0xb, // 8-bit parity odd + kX86CondPo = kX86CondNp, + + kX86CondL = 0xc, // less-than + kX86CondNge = kX86CondL, // not-greater-equal + + kX86CondNl = 0xd, // not-less-than + kX86CondGe = kX86CondNl, // not-greater-equal + + kX86CondLe = 0xe, // less-than-equal + kX86CondNg = kX86CondLe, // not-greater + + kX86CondNle = 0xf, // not-less-than + kX86CondG = kX86CondNle, // greater +}; + +std::ostream& operator<<(std::ostream& os, const X86ConditionCode& kind); + +enum ThrowKind { + kThrowNullPointer, + kThrowDivZero, + kThrowArrayBounds, + kThrowConstantArrayBounds, + kThrowNoSuchMethod, + kThrowStackOverflow, +}; + +enum SpecialCaseHandler { + kNoHandler, + kNullMethod, + kConstFunction, + kIGet, + kIGetBoolean, + kIGetObject, + kIGetByte, + kIGetChar, + kIGetShort, + kIGetWide, + kIPut, + kIPutBoolean, + kIPutObject, + kIPutByte, + kIPutChar, + kIPutShort, + kIPutWide, + kIdentity, +}; + +enum DividePattern { + DivideNone, + Divide3, + Divide5, + Divide7, +}; + +std::ostream& operator<<(std::ostream& os, const DividePattern& pattern); + +// Memory barrier types (see "The JSR-133 Cookbook for Compiler Writers"). +enum MemBarrierKind { + kLoadStore, + kLoadLoad, + kStoreStore, + kStoreLoad +}; + +std::ostream& operator<<(std::ostream& os, const MemBarrierKind& kind); + +enum OpFeatureFlags { + kIsBranch = 0, + kNoOperand, + kIsUnaryOp, + kIsBinaryOp, + kIsTertiaryOp, + kIsQuadOp, + kIsQuinOp, + kIsSextupleOp, + kIsIT, + kMemLoad, + kMemStore, + kPCRelFixup, // x86 FIXME: add NEEDS_FIXUP to instruction attributes. + kRegDef0, + kRegDef1, + kRegDefA, + kRegDefD, + kRegDefFPCSList0, + kRegDefFPCSList2, + kRegDefList0, + kRegDefList1, + kRegDefList2, + kRegDefLR, + kRegDefSP, + kRegUse0, + kRegUse1, + kRegUse2, + kRegUse3, + kRegUse4, + kRegUseA, + kRegUseC, + kRegUseD, + kRegUseFPCSList0, + kRegUseFPCSList2, + kRegUseList0, + kRegUseList1, + kRegUseLR, + kRegUsePC, + kRegUseSP, + kSetsCCodes, + kUsesCCodes +}; + +enum SelectInstructionKind { + kSelectNone, + kSelectConst, + kSelectMove, + kSelectGoto +}; + +std::ostream& operator<<(std::ostream& os, const SelectInstructionKind& kind); + +// Type of growable bitmap for memory tuning. +enum OatBitMapKind { + kBitMapMisc = 0, + kBitMapUse, + kBitMapDef, + kBitMapLiveIn, + kBitMapBMatrix, + kBitMapDominators, + kBitMapIDominated, + kBitMapDomFrontier, + kBitMapPhi, + kBitMapTmpBlocks, + kBitMapInputBlocks, + kBitMapRegisterV, + kBitMapTempSSARegisterV, + kBitMapNullCheck, + kBitMapTmpBlockV, + kBitMapPredecessors, + kNumBitMapKinds +}; + +std::ostream& operator<<(std::ostream& os, const OatBitMapKind& kind); + +} // namespace art + +#endif // ART_COMPILER_DEX_COMPILER_ENUMS_H_ diff --git a/compiler/dex/compiler_internals.h b/compiler/dex/compiler_internals.h new file mode 100644 index 0000000000000000000000000000000000000000..9dd0272e50eac6426d82badebfdf4c48d48c58aa --- /dev/null +++ b/compiler/dex/compiler_internals.h @@ -0,0 +1,36 @@ +/* + * 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 +#include +#include +#include + +#include "base/logging.h" +#include "class_linker.h" +#include "driver/compiler_driver.h" +#include "quick/mir_to_lir.h" +#include "mir_graph.h" +#include "compiler_ir.h" +#include "frontend.h" +#include "monitor.h" +#include "thread.h" +#include "utils.h" + +#endif // ART_COMPILER_DEX_COMPILER_INTERNALS_H_ diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h new file mode 100644 index 0000000000000000000000000000000000000000..6607562b1342c59f94d3a59945a69bd3b92c1ba4 --- /dev/null +++ b/compiler/dex/compiler_ir.h @@ -0,0 +1,116 @@ +/* + * 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_IR_H_ +#define ART_COMPILER_DEX_COMPILER_IR_H_ + +#include +#include +#include "arena_allocator.h" +#include "backend.h" +#include "compiler_enums.h" +#include "dex/quick/mir_to_lir.h" +#include "dex_instruction.h" +#include "driver/compiler_driver.h" +#include "driver/dex_compilation_unit.h" +#include "llvm/intrinsic_helper.h" +#include "llvm/ir_builder.h" +#include "safe_map.h" + +namespace art { + +class LLVMInfo; +namespace llvm { +class LlvmCompilationUnit; +} // namespace llvm + +struct ArenaMemBlock; +struct Memstats; +class MIRGraph; +class Mir2Lir; + +struct CompilationUnit { + explicit CompilationUnit(ArenaPool* pool) + : compiler_driver(NULL), + class_linker(NULL), + dex_file(NULL), + class_loader(NULL), + class_def_idx(0), + method_idx(0), + code_item(NULL), + access_flags(0), + invoke_type(kDirect), + shorty(NULL), + disable_opt(0), + enable_debug(0), + verbose(false), + compiler_backend(kNoBackend), + instruction_set(kNone), + num_dalvik_registers(0), + insns(NULL), + num_ins(0), + num_outs(0), + num_regs(0), + num_compiler_temps(0), + compiler_flip_match(false), + arena(pool), + mir_graph(NULL), + cg(NULL) {} + /* + * 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. + const DexFile::CodeItem* code_item; // compiling method's DexFile code_item. + 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; + CompilerBackend compiler_backend; + InstructionSet instruction_set; + + // TODO: much of this info available elsewhere. Go to the original source? + int num_dalvik_registers; // method->registers_size. + const uint16_t* insns; + int num_ins; + int num_outs; + int num_regs; // Unlike num_dalvik_registers, does not include ins. + + // TODO: may want to move this to MIRGraph. + int num_compiler_temps; + + // 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; + + // TODO: move memory management to mir_graph, or just switch to using standard containers. + ArenaAllocator arena; + + UniquePtr mir_graph; // MIR container. + UniquePtr cg; // Target-specific codegen. +}; + +} // namespace art + +#endif // ART_COMPILER_DEX_COMPILER_IR_H_ diff --git a/compiler/dex/dataflow_iterator-inl.h b/compiler/dex/dataflow_iterator-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..06cc505a9ae121b51e4527b5a8a0394b1636a1cc --- /dev/null +++ b/compiler/dex/dataflow_iterator-inl.h @@ -0,0 +1,68 @@ +/* + * 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_DATAFLOW_ITERATOR_INL_H_ +#define ART_COMPILER_DEX_DATAFLOW_ITERATOR_INL_H_ + +#include "dataflow_iterator.h" + +namespace art { + +inline BasicBlock* DataflowIterator::NextBody(bool had_change) { + changed_ |= had_change; + BasicBlock* res = NULL; + if (reverse_) { + if (is_iterative_ && changed_ && (idx_ < 0)) { + idx_ = start_idx_; + changed_ = false; + } + if (idx_ >= 0) { + int bb_id = block_id_list_->Get(idx_--); + res = mir_graph_->GetBasicBlock(bb_id); + } + } else { + if (is_iterative_ && changed_ && (idx_ >= end_idx_)) { + idx_ = start_idx_; + changed_ = false; + } + if (idx_ < end_idx_) { + int bb_id = block_id_list_->Get(idx_++); + res = mir_graph_->GetBasicBlock(bb_id); + } + } + return res; +} + +// AllNodes uses the existing GrowableArray iterator, so use different NextBody(). +inline BasicBlock* AllNodesIterator::NextBody(bool had_change) { + changed_ |= had_change; + BasicBlock* res = NULL; + bool keep_looking = true; + while (keep_looking) { + res = all_nodes_iterator_->Next(); + if (is_iterative_ && changed_ && (res == NULL)) { + all_nodes_iterator_->Reset(); + changed_ = false; + } else if ((res == NULL) || (!res->hidden)) { + keep_looking = false; + } + } + return res; +} + +} // namespace art + +#endif // ART_COMPILER_DEX_DATAFLOW_ITERATOR_INL_H_ diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h new file mode 100644 index 0000000000000000000000000000000000000000..da44ffd99c5a2ea18e29d39f6db03a49d83d424c --- /dev/null +++ b/compiler/dex/dataflow_iterator.h @@ -0,0 +1,155 @@ +/* + * 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_DATAFLOW_ITERATOR_H_ +#define ART_COMPILER_DEX_DATAFLOW_ITERATOR_H_ + +#include "compiler_ir.h" +#include "mir_graph.h" + +namespace art { + + /* + * This class supports iterating over lists of basic blocks in various + * interesting orders. Note that for efficiency, the visit orders have been pre-computed. + * The order itself will not change during the iteration. However, for some uses, + * auxiliary data associated with the basic blocks may be changed during the iteration, + * necessitating another pass over the list. + * + * To support this usage, we have is_iterative_. If false, the iteration is a one-shot + * pass through the pre-computed list using Next(). If true, the caller must tell the + * iterator whether a change has been made that necessitates another pass. Use + * Next(had_change) for this. The general idea is that the iterative_ use case means + * that the iterator will keep repeating the full basic block list until a complete pass + * is made through it with no changes. Note that calling Next(true) does not affect + * the iteration order or short-curcuit the current pass - it simply tells the iterator + * that once it has finished walking through the block list it should reset and do another + * full pass through the list. + */ + class DataflowIterator { + public: + virtual ~DataflowIterator() {} + + // Return the next BasicBlock* to visit. + BasicBlock* Next() { + DCHECK(!is_iterative_); + return NextBody(false); + } + + /* + * Return the next BasicBlock* to visit, and tell the iterator whether any change + * has occurred that requires another full pass over the block list. + */ + BasicBlock* Next(bool had_change) { + DCHECK(is_iterative_); + return NextBody(had_change); + } + + protected: + DataflowIterator(MIRGraph* mir_graph, bool is_iterative, int start_idx, int end_idx, + bool reverse) + : mir_graph_(mir_graph), + is_iterative_(is_iterative), + start_idx_(start_idx), + end_idx_(end_idx), + reverse_(reverse), + block_id_list_(NULL), + idx_(0), + changed_(false) {} + + virtual BasicBlock* NextBody(bool had_change) ALWAYS_INLINE; + + MIRGraph* const mir_graph_; + const bool is_iterative_; + const int start_idx_; + const int end_idx_; + const bool reverse_; + GrowableArray* block_id_list_; + int idx_; + bool changed_; + }; // DataflowIterator + + class ReachableNodesIterator : public DataflowIterator { + public: + ReachableNodesIterator(MIRGraph* mir_graph, bool is_iterative) + : DataflowIterator(mir_graph, is_iterative, 0, + mir_graph->GetNumReachableBlocks(), false) { + idx_ = start_idx_; + block_id_list_ = mir_graph->GetDfsOrder(); + } + }; + + class PreOrderDfsIterator : public DataflowIterator { + public: + PreOrderDfsIterator(MIRGraph* mir_graph, bool is_iterative) + : DataflowIterator(mir_graph, is_iterative, 0, + mir_graph->GetNumReachableBlocks(), false) { + idx_ = start_idx_; + block_id_list_ = mir_graph->GetDfsOrder(); + } + }; + + class PostOrderDfsIterator : public DataflowIterator { + public: + PostOrderDfsIterator(MIRGraph* mir_graph, bool is_iterative) + : DataflowIterator(mir_graph, is_iterative, 0, + mir_graph->GetNumReachableBlocks(), false) { + idx_ = start_idx_; + block_id_list_ = mir_graph->GetDfsPostOrder(); + } + }; + + class ReversePostOrderDfsIterator : public DataflowIterator { + public: + ReversePostOrderDfsIterator(MIRGraph* mir_graph, bool is_iterative) + : DataflowIterator(mir_graph, is_iterative, + mir_graph->GetNumReachableBlocks() -1, 0, true) { + idx_ = start_idx_; + block_id_list_ = mir_graph->GetDfsPostOrder(); + } + }; + + class PostOrderDOMIterator : public DataflowIterator { + public: + PostOrderDOMIterator(MIRGraph* mir_graph, bool is_iterative) + : DataflowIterator(mir_graph, is_iterative, 0, + mir_graph->GetNumReachableBlocks(), false) { + idx_ = start_idx_; + block_id_list_ = mir_graph->GetDomPostOrder(); + } + }; + + class AllNodesIterator : public DataflowIterator { + public: + AllNodesIterator(MIRGraph* mir_graph, bool is_iterative) + : DataflowIterator(mir_graph, is_iterative, 0, 0, false) { + all_nodes_iterator_ = + new (mir_graph->GetArena()) GrowableArray::Iterator(mir_graph->GetBlockList()); + } + + void Reset() { + all_nodes_iterator_->Reset(); + } + + BasicBlock* NextBody(bool had_change) ALWAYS_INLINE; + + private: + GrowableArray::Iterator* all_nodes_iterator_; + }; + +} // namespace art + +#endif // ART_COMPILER_DEX_DATAFLOW_ITERATOR_H_ diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc new file mode 100644 index 0000000000000000000000000000000000000000..63d8aa04f8496e6ad95ce4781c4570edd1e3aa25 --- /dev/null +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -0,0 +1,289 @@ +/* + * 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 "base/logging.h" +#include "base/mutex.h" +#include "dex_file-inl.h" +#include "dex_instruction-inl.h" +#include "driver/compiler_driver.h" +#include "driver/dex_compilation_unit.h" +#include "mirror/art_field-inl.h" +#include "mirror/art_method-inl.h" +#include "mirror/class-inl.h" +#include "mirror/dex_cache.h" + +namespace art { +namespace optimizer { + +// Controls quickening activation. +const bool kEnableQuickening = true; +// Control check-cast elision. +const bool kEnableCheckCastEllision = true; + +class DexCompiler { + public: + DexCompiler(art::CompilerDriver& compiler, + const DexCompilationUnit& unit, + DexToDexCompilationLevel dex_to_dex_compilation_level) + : driver_(compiler), + unit_(unit), + dex_to_dex_compilation_level_(dex_to_dex_compilation_level) {} + + ~DexCompiler() {} + + void Compile(); + + private: + const DexFile& GetDexFile() const { + return *unit_.GetDexFile(); + } + + // TODO: since the whole compilation pipeline uses a "const DexFile", we need + // to "unconst" here. The DEX-to-DEX compiler should work on a non-const DexFile. + DexFile& GetModifiableDexFile() { + return *const_cast(unit_.GetDexFile()); + } + + bool PerformOptimizations() const { + return dex_to_dex_compilation_level_ >= kOptimize; + } + + // Compiles a RETURN-VOID into a RETURN-VOID-BARRIER within a constructor where + // a barrier is required. + void CompileReturnVoid(Instruction* inst, uint32_t dex_pc); + + // Compiles a CHECK-CAST into 2 NOP instructions if it is known to be safe. In + // this case, returns the second NOP instruction pointer. Otherwise, returns + // the given "inst". + Instruction* CompileCheckCast(Instruction* inst, uint32_t dex_pc); + + // Compiles a field access into a quick field access. + // The field index is replaced by an offset within an Object where we can read + // from / write to this field. Therefore, this does not involve any resolution + // at runtime. + // Since the field index is encoded with 16 bits, we can replace it only if the + // field offset can be encoded with 16 bits too. + void CompileInstanceFieldAccess(Instruction* inst, uint32_t dex_pc, + Instruction::Code new_opcode, bool is_put); + + // Compiles a virtual method invocation into a quick virtual method invocation. + // The method index is replaced by the vtable index where the corresponding + // AbstractMethod can be found. Therefore, this does not involve any resolution + // at runtime. + // Since the method index is encoded with 16 bits, we can replace it only if the + // vtable index can be encoded with 16 bits too. + void CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc, + Instruction::Code new_opcode, bool is_range); + + CompilerDriver& driver_; + const DexCompilationUnit& unit_; + const DexToDexCompilationLevel dex_to_dex_compilation_level_; + + DISALLOW_COPY_AND_ASSIGN(DexCompiler); +}; + +void DexCompiler::Compile() { + DCHECK_GE(dex_to_dex_compilation_level_, kRequired); + const DexFile::CodeItem* code_item = unit_.GetCodeItem(); + const uint16_t* insns = code_item->insns_; + const uint32_t insns_size = code_item->insns_size_in_code_units_; + Instruction* inst = const_cast(Instruction::At(insns)); + + for (uint32_t dex_pc = 0; dex_pc < insns_size; + inst = const_cast(inst->Next()), dex_pc = inst->GetDexPc(insns)) { + switch (inst->Opcode()) { + case Instruction::RETURN_VOID: + CompileReturnVoid(inst, dex_pc); + break; + + case Instruction::CHECK_CAST: + inst = CompileCheckCast(inst, dex_pc); + break; + + case Instruction::IGET: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_QUICK, false); + break; + + case Instruction::IGET_WIDE: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE_QUICK, false); + break; + + case Instruction::IGET_OBJECT: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT_QUICK, false); + break; + + case Instruction::IPUT: + case Instruction::IPUT_BOOLEAN: + case Instruction::IPUT_BYTE: + case Instruction::IPUT_CHAR: + case Instruction::IPUT_SHORT: + // These opcodes have the same implementation in interpreter so group + // them under IPUT_QUICK. + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_QUICK, true); + break; + + case Instruction::IPUT_WIDE: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE_QUICK, true); + break; + + case Instruction::IPUT_OBJECT: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_OBJECT_QUICK, true); + break; + + case Instruction::INVOKE_VIRTUAL: + CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_QUICK, false); + break; + + case Instruction::INVOKE_VIRTUAL_RANGE: + CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE_QUICK, true); + break; + + default: + // Nothing to do. + break; + } + } +} + +void DexCompiler::CompileReturnVoid(Instruction* inst, uint32_t dex_pc) { + DCHECK(inst->Opcode() == Instruction::RETURN_VOID); + // Are we compiling a non-clinit constructor? + if (!unit_.IsConstructor() || unit_.IsStatic()) { + return; + } + // Do we need a constructor barrier ? + if (!driver_.RequiresConstructorBarrier(Thread::Current(), unit_.GetDexFile(), + unit_.GetClassDefIndex())) { + return; + } + // Replace RETURN_VOID by RETURN_VOID_BARRIER. + VLOG(compiler) << "Replacing " << Instruction::Name(inst->Opcode()) + << " by " << Instruction::Name(Instruction::RETURN_VOID_BARRIER) + << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method " + << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true); + inst->SetOpcode(Instruction::RETURN_VOID_BARRIER); +} + +Instruction* DexCompiler::CompileCheckCast(Instruction* inst, uint32_t dex_pc) { + if (!kEnableCheckCastEllision || !PerformOptimizations()) { + return inst; + } + MethodReference referrer(&GetDexFile(), unit_.GetDexMethodIndex()); + if (!driver_.IsSafeCast(referrer, dex_pc)) { + return inst; + } + // Ok, this is a safe cast. Since the "check-cast" instruction size is 2 code + // units and a "nop" instruction size is 1 code unit, we need to replace it by + // 2 consecutive NOP instructions. + // Because the caller loops over instructions by calling Instruction::Next onto + // the current instruction, we need to return the 2nd NOP instruction. Indeed, + // its next instruction is the former check-cast's next instruction. + VLOG(compiler) << "Removing " << Instruction::Name(inst->Opcode()) + << " by replacing it with 2 NOPs at dex pc " + << StringPrintf("0x%x", dex_pc) << " in method " + << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true); + // We are modifying 4 consecutive bytes. + inst->SetOpcode(Instruction::NOP); + inst->SetVRegA_10x(0u); // keep compliant with verifier. + // Get to next instruction which is the second half of check-cast and replace + // it by a NOP. + inst = const_cast(inst->Next()); + inst->SetOpcode(Instruction::NOP); + inst->SetVRegA_10x(0u); // keep compliant with verifier. + return inst; +} + +void DexCompiler::CompileInstanceFieldAccess(Instruction* inst, + uint32_t dex_pc, + Instruction::Code new_opcode, + bool is_put) { + if (!kEnableQuickening || !PerformOptimizations()) { + return; + } + uint32_t field_idx = inst->VRegC_22c(); + int field_offset; + bool is_volatile; + bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, field_offset, + is_volatile, is_put); + if (fast_path && !is_volatile && IsUint(16, field_offset)) { + VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode()) + << " to " << Instruction::Name(new_opcode) + << " by replacing field index " << field_idx + << " by field offset " << field_offset + << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method " + << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true); + // We are modifying 4 consecutive bytes. + inst->SetOpcode(new_opcode); + // Replace field index by field offset. + inst->SetVRegC_22c(static_cast(field_offset)); + } +} + +void DexCompiler::CompileInvokeVirtual(Instruction* inst, + uint32_t dex_pc, + Instruction::Code new_opcode, + bool is_range) { + if (!kEnableQuickening || !PerformOptimizations()) { + return; + } + uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); + MethodReference target_method(&GetDexFile(), method_idx); + InvokeType invoke_type = kVirtual; + InvokeType original_invoke_type = invoke_type; + int vtable_idx; + uintptr_t direct_code; + uintptr_t direct_method; + bool fast_path = driver_.ComputeInvokeInfo(&unit_, dex_pc, invoke_type, + target_method, vtable_idx, + direct_code, direct_method, + false); + // TODO: support devirtualization. + if (fast_path && original_invoke_type == invoke_type) { + if (vtable_idx >= 0 && IsUint(16, vtable_idx)) { + VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode()) + << "(" << PrettyMethod(method_idx, GetDexFile(), true) << ")" + << " to " << Instruction::Name(new_opcode) + << " by replacing method index " << method_idx + << " by vtable index " << vtable_idx + << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method " + << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true); + // We are modifying 4 consecutive bytes. + inst->SetOpcode(new_opcode); + // Replace method index by vtable index. + if (is_range) { + inst->SetVRegB_3rc(static_cast(vtable_idx)); + } else { + inst->SetVRegB_35c(static_cast(vtable_idx)); + } + } + } +} + +} // namespace optimizer +} // namespace art + +extern "C" void ArtCompileDEX(art::CompilerDriver& compiler, 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, + art::DexToDexCompilationLevel dex_to_dex_compilation_level) { + if (dex_to_dex_compilation_level != art::kDontDexToDexCompile) { + art::DexCompilationUnit unit(NULL, class_loader, art::Runtime::Current()->GetClassLinker(), + dex_file, code_item, class_def_idx, method_idx, access_flags); + art::optimizer::DexCompiler dex_compiler(compiler, unit, dex_to_dex_compilation_level); + dex_compiler.Compile(); + } +} diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc new file mode 100644 index 0000000000000000000000000000000000000000..fefcab9e87c78a0d2339e6f276002241dfce76e1 --- /dev/null +++ b/compiler/dex/frontend.cc @@ -0,0 +1,302 @@ +/* + * 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 + +#include "compiler_internals.h" +#include "driver/compiler_driver.h" +#include "dataflow_iterator-inl.h" +#include "leb128.h" +#include "mirror/object.h" +#include "runtime.h" +#include "backend.h" +#include "base/logging.h" + +#if defined(ART_USE_PORTABLE_COMPILER) +#include "dex/portable/mir_to_gbc.h" +#include "llvm/llvm_compilation_unit.h" +#endif + +namespace { +#if !defined(ART_USE_PORTABLE_COMPILER) + pthread_once_t llvm_multi_init = PTHREAD_ONCE_INIT; +#endif + void InitializeLLVMForQuick() { + ::llvm::llvm_start_multithreaded(); + } +} + +namespace art { +namespace llvm { +::llvm::Module* makeLLVMModuleContents(::llvm::Module* module); +} + +LLVMInfo::LLVMInfo() { +#if !defined(ART_USE_PORTABLE_COMPILER) + pthread_once(&llvm_multi_init, InitializeLLVMForQuick); +#endif + // 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() { +} + +extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver& compiler) { + CHECK(compiler.GetCompilerContext() == NULL); + LLVMInfo* llvm_info = new LLVMInfo(); + compiler.SetCompilerContext(llvm_info); +} + +extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver& compiler) { + delete reinterpret_cast(compiler.GetCompilerContext()); + compiler.SetCompilerContext(NULL); +} + +/* Default optimizer/debug setting for the compiler. */ +static uint32_t kCompilerOptimizerDisableFlags = 0 | // Disable specific optimizations + (1 << kLoadStoreElimination) | + // (1 << kLoadHoisting) | + // (1 << kSuppressLoads) | + // (1 << kNullCheckElimination) | + // (1 << kPromoteRegs) | + // (1 << kTrackLiveTemps) | + // (1 << kSafeOptimizations) | + // (1 << kBBOpt) | + // (1 << kMatch) | + // (1 << kPromoteCompilerTemps) | + 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) | + 0; + +static CompiledMethod* CompileMethod(CompilerDriver& compiler, + const CompilerBackend compiler_backend, + 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 +#if defined(ART_USE_PORTABLE_COMPILER) + , llvm::LlvmCompilationUnit* llvm_compilation_unit +#endif +) { + VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "..."; + + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + CompilationUnit cu(&compiler.GetArenaPool()); + + cu.compiler_driver = &compiler; + cu.class_linker = class_linker; + cu.instruction_set = compiler.GetInstructionSet(); + cu.compiler_backend = compiler_backend; + DCHECK((cu.instruction_set == kThumb2) || + (cu.instruction_set == kX86) || + (cu.instruction_set == kMips)); + + + /* Adjust this value accordingly once inlining is performed */ + cu.num_dalvik_registers = code_item->registers_size_; + // 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)); + } + + /* + * TODO: rework handling of optimization and debug flags. Should we split out + * MIR and backend flags? Need command-line setting as well. + */ + + if (compiler_backend == kPortable) { + // Fused long branches not currently usseful in bitcode. + cu.disable_opt |= (1 << kBranchFusing); + } + + if (cu.instruction_set == kMips) { + // Disable some optimizations for mips for now + cu.disable_opt |= ( + (1 << kLoadStoreElimination) | + (1 << kLoadHoisting) | + (1 << kSuppressLoads) | + (1 << kNullCheckElimination) | + (1 << kPromoteRegs) | + (1 << kTrackLiveTemps) | + (1 << kSafeOptimizations) | + (1 << kBBOpt) | + (1 << kMatch) | + (1 << kPromoteCompilerTemps)); + } + + cu.mir_graph.reset(new MIRGraph(&cu, &cu.arena)); + + /* 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 !defined(ART_USE_PORTABLE_COMPILER) + if (cu.mir_graph->SkipCompilation(Runtime::Current()->GetCompilerFilter())) { + return NULL; + } +#endif + + /* Do a code layout pass */ + cu.mir_graph->CodeLayout(); + + /* Perform SSA transformation for the whole method */ + cu.mir_graph->SSATransformation(); + + /* Do constant propagation */ + cu.mir_graph->PropagateConstants(); + + /* Count uses */ + cu.mir_graph->MethodUseCount(); + + /* Perform null check elimination */ + cu.mir_graph->NullCheckElimination(); + + /* Combine basic blocks where possible */ + cu.mir_graph->BasicBlockCombine(); + + /* Do some basic block optimizations */ + cu.mir_graph->BasicBlockOptimization(); + + if (cu.enable_debug & (1 << kDebugDumpCheckStats)) { + cu.mir_graph->DumpCheckStats(); + } + + if (kCompilerDebugFlags & (1 << kDebugCountOpcodes)) { + cu.mir_graph->ShowOpcodeStats(); + } + + /* Set up regLocation[] array to describe values - one for each ssa_name. */ + cu.mir_graph->BuildRegLocations(); + + CompiledMethod* result = NULL; + +#if defined(ART_USE_PORTABLE_COMPILER) + if (compiler_backend == kPortable) { + cu.cg.reset(PortableCodeGenerator(&cu, cu.mir_graph.get(), &cu.arena, llvm_compilation_unit)); + } else { +#endif + switch (compiler.GetInstructionSet()) { + case kThumb2: + cu.cg.reset(ArmCodeGenerator(&cu, cu.mir_graph.get(), &cu.arena)); + break; + case kMips: + cu.cg.reset(MipsCodeGenerator(&cu, cu.mir_graph.get(), &cu.arena)); + break; + case kX86: + cu.cg.reset(X86CodeGenerator(&cu, cu.mir_graph.get(), &cu.arena)); + break; + default: + LOG(FATAL) << "Unexpected instruction set: " << compiler.GetInstructionSet(); + } +#if defined(ART_USE_PORTABLE_COMPILER) + } +#endif + + cu.cg->Materialize(); + + result = cu.cg->GetCompiledMethod(); + + if (result) { + VLOG(compiler) << "Compiled " << PrettyMethod(method_idx, dex_file); + } else { + VLOG(compiler) << "Deferred " << PrettyMethod(method_idx, dex_file); + } + + if (cu.enable_debug & (1 << kDebugShowMemoryUsage)) { + if (cu.arena.BytesAllocated() > (5 * 1024 *1024)) { + MemStats mem_stats(cu.arena); + LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable(mem_stats); + } + } + + if (cu.enable_debug & (1 << kDebugShowSummaryMemoryUsage)) { + LOG(INFO) << "MEMINFO " << cu.arena.BytesAllocated() << " " << cu.mir_graph->GetNumBlocks() + << " " << PrettyMethod(method_idx, dex_file); + } + + return result; +} + +CompiledMethod* CompileOneMethod(CompilerDriver& compiler, + const CompilerBackend backend, + 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, + llvm::LlvmCompilationUnit* llvm_compilation_unit) { + return CompileMethod(compiler, backend, code_item, access_flags, invoke_type, class_def_idx, + method_idx, class_loader, dex_file +#if defined(ART_USE_PORTABLE_COMPILER) + , llvm_compilation_unit +#endif + ); // NOLINT(whitespace/parens) +} + +} // namespace art + +extern "C" art::CompiledMethod* + ArtQuickCompileMethod(art::CompilerDriver& compiler, + 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) { + // TODO: check method fingerprint here to determine appropriate backend type. Until then, use build default + art::CompilerBackend backend = compiler.GetCompilerBackend(); + return art::CompileOneMethod(compiler, backend, code_item, access_flags, invoke_type, + class_def_idx, method_idx, class_loader, dex_file, + NULL /* use thread llvm_info */); +} diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h new file mode 100644 index 0000000000000000000000000000000000000000..6c33d109e31271809f3177df467cf6b8aa89c3cb --- /dev/null +++ b/compiler/dex/frontend.h @@ -0,0 +1,127 @@ +/* + * 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 "dex_instruction.h" + + + + + + +namespace llvm { + class Module; + class LLVMContext; +} + +namespace art { +namespace llvm { + class IntrinsicHelper; + class IRBuilder; +} + +/* + * 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, + kPromoteRegs, + kTrackLiveTemps, + kSafeOptimizations, + kBBOpt, + kMatch, + kPromoteCompilerTemps, + kBranchFusing, +}; + +// 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, +}; + +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: + UniquePtr< ::llvm::LLVMContext> llvm_context_; + ::llvm::Module* llvm_module_; // Managed by context_. + UniquePtr intrinsic_helper_; + UniquePtr ir_builder_; +}; + +struct CompilationUnit; +struct BasicBlock; + +} // namespace art + +extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver& driver, + 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); + + + +#endif // ART_COMPILER_DEX_FRONTEND_H_ diff --git a/compiler/dex/growable_array.h b/compiler/dex/growable_array.h new file mode 100644 index 0000000000000000000000000000000000000000..8e2abfbaf18b0944644a77bd448f6125ddaf6365 --- /dev/null +++ b/compiler/dex/growable_array.h @@ -0,0 +1,170 @@ +/* + * 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_GROWABLE_ARRAY_H_ +#define ART_COMPILER_DEX_GROWABLE_ARRAY_H_ + +#include +#include +#include "compiler_enums.h" +#include "arena_allocator.h" + +namespace art { + +struct CompilationUnit; + +// Type of growable list for memory tuning. +enum OatListKind { + kGrowableArrayMisc = 0, + kGrowableArrayBlockList, + kGrowableArraySSAtoDalvikMap, + kGrowableArrayDfsOrder, + kGrowableArrayDfsPostOrder, + kGrowableArrayDomPostOrderTraversal, + kGrowableArrayThrowLaunchPads, + kGrowableArraySuspendLaunchPads, + kGrowableArraySwitchTables, + kGrowableArrayFillArrayData, + kGrowableArraySuccessorBlocks, + kGrowableArrayPredecessors, + kGNumListKinds +}; + +template +class GrowableArray { + public: + class Iterator { + public: + explicit Iterator(GrowableArray* g_list) + : idx_(0), + g_list_(g_list) {} + + // NOTE: returns 0/NULL when no next. + // TODO: redo to make usage consistent with other iterators. + T Next() { + if (idx_ >= g_list_->Size()) { + return 0; + } else { + return g_list_->Get(idx_++); + } + } + + void Reset() { + idx_ = 0; + } + + static void* operator new(size_t size, ArenaAllocator* arena) { + return arena->Alloc(sizeof(GrowableArray::Iterator), ArenaAllocator::kAllocGrowableArray); + }; + static void operator delete(void* p) {} // Nop. + + private: + size_t idx_; + GrowableArray* const g_list_; + }; + + GrowableArray(ArenaAllocator* arena, size_t init_length, OatListKind kind = kGrowableArrayMisc) + : arena_(arena), + num_allocated_(init_length), + num_used_(0), + kind_(kind) { + elem_list_ = static_cast(arena_->Alloc(sizeof(T) * init_length, + ArenaAllocator::kAllocGrowableArray)); + }; + + + // Expand the list size to at least new length. + void Resize(size_t new_length) { + if (new_length <= num_allocated_) return; + // If it's a small list double the size, else grow 1.5x. + size_t target_length = + (num_allocated_ < 128) ? num_allocated_ << 1 : num_allocated_ + (num_allocated_ >> 1); + if (new_length > target_length) { + target_length = new_length; + } + T* new_array = static_cast(arena_->Alloc(sizeof(T) * target_length, + ArenaAllocator::kAllocGrowableArray)); + memcpy(new_array, elem_list_, sizeof(T) * num_allocated_); + num_allocated_ = target_length; + elem_list_ = new_array; + }; + + // NOTE: does not return storage, just resets use count. + void Reset() { + num_used_ = 0; + } + + // Insert an element to the end of a list, resizing if necessary. + void Insert(T elem) { + if (num_used_ == num_allocated_) { + Resize(num_used_ + 1); + } + elem_list_[num_used_++] = elem; + }; + + T Get(size_t index) const { + DCHECK_LT(index, num_used_); + return elem_list_[index]; + }; + + // Overwrite existing element at position index. List must be large enough. + void Put(size_t index, T elem) { + DCHECK_LT(index, num_used_); + elem_list_[index] = elem; + } + + void Increment(size_t index) { + DCHECK_LT(index, num_used_); + elem_list_[index]++; + } + + void Delete(T element) { + bool found = false; + for (size_t i = 0; i < num_used_ - 1; i++) { + if (!found && elem_list_[i] == element) { + found = true; + } + if (found) { + elem_list_[i] = elem_list_[i+1]; + } + } + // We should either have found the element, or it was the last (unscanned) element. + DCHECK(found || (element == elem_list_[num_used_ - 1])); + num_used_--; + }; + + size_t GetNumAllocated() const { return num_allocated_; } + + size_t Size() const { return num_used_; } + + T* GetRawStorage() const { return elem_list_; } + + static void* operator new(size_t size, ArenaAllocator* arena) { + return arena->Alloc(sizeof(GrowableArray), ArenaAllocator::kAllocGrowableArray); + }; + static void operator delete(void* p) {} // Nop. + + private: + ArenaAllocator* const arena_; + size_t num_allocated_; + size_t num_used_; + OatListKind kind_; + T* elem_list_; +}; + +} // namespace art + +#endif // ART_COMPILER_DEX_GROWABLE_ARRAY_H_ diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc new file mode 100644 index 0000000000000000000000000000000000000000..35d29235f2c4680a43a877b5d632fba03d5d8443 --- /dev/null +++ b/compiler/dex/local_value_numbering.cc @@ -0,0 +1,516 @@ +/* + * 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 "local_value_numbering.h" + +namespace art { + + +uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { + uint16_t res = NO_VALUE; + uint16_t opcode = mir->dalvikInsn.opcode; + switch (opcode) { + case Instruction::NOP: + case Instruction::RETURN_VOID: + case Instruction::RETURN: + case Instruction::RETURN_OBJECT: + case Instruction::RETURN_WIDE: + case Instruction::MONITOR_ENTER: + case Instruction::MONITOR_EXIT: + case Instruction::GOTO: + case Instruction::GOTO_16: + case Instruction::GOTO_32: + case Instruction::CHECK_CAST: + case Instruction::THROW: + case Instruction::FILL_ARRAY_DATA: + case Instruction::FILLED_NEW_ARRAY: + case Instruction::FILLED_NEW_ARRAY_RANGE: + case Instruction::PACKED_SWITCH: + case Instruction::SPARSE_SWITCH: + case Instruction::IF_EQ: + case Instruction::IF_NE: + case Instruction::IF_LT: + case Instruction::IF_GE: + case Instruction::IF_GT: + case Instruction::IF_LE: + case Instruction::IF_EQZ: + case Instruction::IF_NEZ: + case Instruction::IF_LTZ: + case Instruction::IF_GEZ: + case Instruction::IF_GTZ: + case Instruction::IF_LEZ: + case Instruction::INVOKE_STATIC_RANGE: + case Instruction::INVOKE_STATIC: + case Instruction::INVOKE_DIRECT: + case Instruction::INVOKE_DIRECT_RANGE: + case Instruction::INVOKE_VIRTUAL: + case Instruction::INVOKE_VIRTUAL_RANGE: + case Instruction::INVOKE_SUPER: + case Instruction::INVOKE_SUPER_RANGE: + case Instruction::INVOKE_INTERFACE: + case Instruction::INVOKE_INTERFACE_RANGE: + case kMirOpFusedCmplFloat: + case kMirOpFusedCmpgFloat: + case kMirOpFusedCmplDouble: + case kMirOpFusedCmpgDouble: + case kMirOpFusedCmpLong: + // Nothing defined - take no action. + break; + + case Instruction::MOVE_EXCEPTION: + case Instruction::MOVE_RESULT: + case Instruction::MOVE_RESULT_OBJECT: + case Instruction::INSTANCE_OF: + case Instruction::NEW_INSTANCE: + case Instruction::CONST_STRING: + case Instruction::CONST_STRING_JUMBO: + case Instruction::CONST_CLASS: + case Instruction::NEW_ARRAY: { + // 1 result, treat as unique each time, use result s_reg - will be unique. + uint16_t res = GetOperandValue(mir->ssa_rep->defs[0]); + SetOperandValue(mir->ssa_rep->defs[0], res); + } + break; + case Instruction::MOVE_RESULT_WIDE: { + // 1 wide result, treat as unique each time, use result s_reg - will be unique. + uint16_t res = GetOperandValueWide(mir->ssa_rep->defs[0]); + SetOperandValueWide(mir->ssa_rep->defs[0], res); + } + break; + + case kMirOpPhi: + /* + * Because we'll only see phi nodes at the beginning of an extended basic block, + * we can ignore them. Revisit if we shift to global value numbering. + */ + break; + + case Instruction::MOVE: + case Instruction::MOVE_OBJECT: + case Instruction::MOVE_16: + case Instruction::MOVE_OBJECT_16: + case Instruction::MOVE_FROM16: + case Instruction::MOVE_OBJECT_FROM16: + case kMirOpCopy: { + // Just copy value number of source to value number of resulit. + uint16_t res = GetOperandValue(mir->ssa_rep->uses[0]); + SetOperandValue(mir->ssa_rep->defs[0], res); + } + break; + + case Instruction::MOVE_WIDE: + case Instruction::MOVE_WIDE_16: + case Instruction::MOVE_WIDE_FROM16: { + // Just copy value number of source to value number of result. + uint16_t res = GetOperandValueWide(mir->ssa_rep->uses[0]); + SetOperandValueWide(mir->ssa_rep->defs[0], res); + } + break; + + case Instruction::CONST: + case Instruction::CONST_4: + case Instruction::CONST_16: { + uint16_t res = LookupValue(Instruction::CONST, Low16Bits(mir->dalvikInsn.vB), + High16Bits(mir->dalvikInsn.vB >> 16), 0); + SetOperandValue(mir->ssa_rep->defs[0], res); + } + break; + + case Instruction::CONST_HIGH16: { + uint16_t res = LookupValue(Instruction::CONST, 0, mir->dalvikInsn.vB, 0); + SetOperandValue(mir->ssa_rep->defs[0], res); + } + break; + + case Instruction::CONST_WIDE_16: + case Instruction::CONST_WIDE_32: { + uint16_t low_res = LookupValue(Instruction::CONST, Low16Bits(mir->dalvikInsn.vB), + High16Bits(mir->dalvikInsn.vB >> 16), 1); + uint16_t high_res; + if (mir->dalvikInsn.vB & 0x80000000) { + high_res = LookupValue(Instruction::CONST, 0xffff, 0xffff, 2); + } else { + high_res = LookupValue(Instruction::CONST, 0, 0, 2); + } + uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3); + SetOperandValue(mir->ssa_rep->defs[0], res); + } + break; + + case Instruction::CONST_WIDE: { + uint32_t low_word = Low32Bits(mir->dalvikInsn.vB_wide); + uint32_t high_word = High32Bits(mir->dalvikInsn.vB_wide); + uint16_t low_res = LookupValue(Instruction::CONST, Low16Bits(low_word), + High16Bits(low_word), 1); + uint16_t high_res = LookupValue(Instruction::CONST, Low16Bits(high_word), + High16Bits(high_word), 2); + uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3); + SetOperandValueWide(mir->ssa_rep->defs[0], res); + } + break; + + case Instruction::CONST_WIDE_HIGH16: { + uint16_t low_res = LookupValue(Instruction::CONST, 0, 0, 1); + uint16_t high_res = LookupValue(Instruction::CONST, 0, Low16Bits(mir->dalvikInsn.vB), 2); + uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3); + SetOperandValueWide(mir->ssa_rep->defs[0], res); + } + break; + + case Instruction::ARRAY_LENGTH: + case Instruction::NEG_INT: + case Instruction::NOT_INT: + case Instruction::NEG_FLOAT: + case Instruction::INT_TO_BYTE: + case Instruction::INT_TO_SHORT: + case Instruction::INT_TO_CHAR: + case Instruction::INT_TO_FLOAT: + case Instruction::FLOAT_TO_INT: { + // res = op + 1 operand + uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); + uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE); + SetOperandValue(mir->ssa_rep->defs[0], res); + } + break; + + case Instruction::LONG_TO_FLOAT: + case Instruction::LONG_TO_INT: + case Instruction::DOUBLE_TO_FLOAT: + case Instruction::DOUBLE_TO_INT: { + // res = op + 1 wide operand + uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); + uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE); + SetOperandValue(mir->ssa_rep->defs[0], res); + } + break; + + + case Instruction::DOUBLE_TO_LONG: + case Instruction::LONG_TO_DOUBLE: + case Instruction::NEG_LONG: + case Instruction::NOT_LONG: + case Instruction::NEG_DOUBLE: { + // wide res = op + 1 wide operand + uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); + uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE); + SetOperandValueWide(mir->ssa_rep->defs[0], res); + } + break; + + case Instruction::FLOAT_TO_DOUBLE: + case Instruction::FLOAT_TO_LONG: + case Instruction::INT_TO_DOUBLE: + case Instruction::INT_TO_LONG: { + // wide res = op + 1 operand + uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); + uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE); + SetOperandValueWide(mir->ssa_rep->defs[0], res); + } + break; + + case Instruction::CMPL_DOUBLE: + case Instruction::CMPG_DOUBLE: + case Instruction::CMP_LONG: { + // res = op + 2 wide operands + uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); + uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]); + uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE); + SetOperandValue(mir->ssa_rep->defs[0], res); + } + break; + + case Instruction::CMPG_FLOAT: + case Instruction::CMPL_FLOAT: + case Instruction::ADD_INT: + case Instruction::ADD_INT_2ADDR: + case Instruction::MUL_INT: + case Instruction::MUL_INT_2ADDR: + case Instruction::AND_INT: + case Instruction::AND_INT_2ADDR: + case Instruction::OR_INT: + case Instruction::OR_INT_2ADDR: + case Instruction::XOR_INT: + case Instruction::XOR_INT_2ADDR: + case Instruction::SUB_INT: + case Instruction::SUB_INT_2ADDR: + case Instruction::DIV_INT: + case Instruction::DIV_INT_2ADDR: + case Instruction::REM_INT: + case Instruction::REM_INT_2ADDR: + case Instruction::SHL_INT: + case Instruction::SHL_INT_2ADDR: + case Instruction::SHR_INT: + case Instruction::SHR_INT_2ADDR: + case Instruction::USHR_INT: + case Instruction::USHR_INT_2ADDR: { + // res = op + 2 operands + uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); + uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[1]); + uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE); + SetOperandValue(mir->ssa_rep->defs[0], res); + } + break; + + case Instruction::ADD_LONG: + case Instruction::SUB_LONG: + case Instruction::MUL_LONG: + case Instruction::DIV_LONG: + case Instruction::REM_LONG: + case Instruction::AND_LONG: + case Instruction::OR_LONG: + case Instruction::XOR_LONG: + case Instruction::ADD_LONG_2ADDR: + case Instruction::SUB_LONG_2ADDR: + case Instruction::MUL_LONG_2ADDR: + case Instruction::DIV_LONG_2ADDR: + case Instruction::REM_LONG_2ADDR: + case Instruction::AND_LONG_2ADDR: + case Instruction::OR_LONG_2ADDR: + case Instruction::XOR_LONG_2ADDR: + case Instruction::ADD_DOUBLE: + case Instruction::SUB_DOUBLE: + case Instruction::MUL_DOUBLE: + case Instruction::DIV_DOUBLE: + case Instruction::REM_DOUBLE: + case Instruction::ADD_DOUBLE_2ADDR: + case Instruction::SUB_DOUBLE_2ADDR: + case Instruction::MUL_DOUBLE_2ADDR: + case Instruction::DIV_DOUBLE_2ADDR: + case Instruction::REM_DOUBLE_2ADDR: { + // wide res = op + 2 wide operands + uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); + uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]); + uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE); + SetOperandValueWide(mir->ssa_rep->defs[0], res); + } + break; + + case Instruction::SHL_LONG: + case Instruction::SHR_LONG: + case Instruction::USHR_LONG: + case Instruction::SHL_LONG_2ADDR: + case Instruction::SHR_LONG_2ADDR: + case Instruction::USHR_LONG_2ADDR: { + // wide res = op + 1 wide operand + 1 operand + uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); + uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]); + uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE); + SetOperandValueWide(mir->ssa_rep->defs[0], res); + } + break; + + case Instruction::ADD_FLOAT: + case Instruction::SUB_FLOAT: + case Instruction::MUL_FLOAT: + case Instruction::DIV_FLOAT: + case Instruction::REM_FLOAT: + case Instruction::ADD_FLOAT_2ADDR: + case Instruction::SUB_FLOAT_2ADDR: + case Instruction::MUL_FLOAT_2ADDR: + case Instruction::DIV_FLOAT_2ADDR: + case Instruction::REM_FLOAT_2ADDR: { + // res = op + 2 operands + uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); + uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[1]); + uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE); + SetOperandValue(mir->ssa_rep->defs[0], res); + } + break; + + case Instruction::RSUB_INT: + case Instruction::ADD_INT_LIT16: + case Instruction::MUL_INT_LIT16: + case Instruction::DIV_INT_LIT16: + case Instruction::REM_INT_LIT16: + case Instruction::AND_INT_LIT16: + case Instruction::OR_INT_LIT16: + case Instruction::XOR_INT_LIT16: + case Instruction::ADD_INT_LIT8: + case Instruction::RSUB_INT_LIT8: + case Instruction::MUL_INT_LIT8: + case Instruction::DIV_INT_LIT8: + case Instruction::REM_INT_LIT8: + case Instruction::AND_INT_LIT8: + case Instruction::OR_INT_LIT8: + case Instruction::XOR_INT_LIT8: + case Instruction::SHL_INT_LIT8: + case Instruction::SHR_INT_LIT8: + case Instruction::USHR_INT_LIT8: { + // Same as res = op + 2 operands, except use vB as operand 2 + uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); + uint16_t operand2 = LookupValue(Instruction::CONST, mir->dalvikInsn.vB, 0, 0); + uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE); + SetOperandValue(mir->ssa_rep->defs[0], res); + } + break; + + case Instruction::AGET_WIDE: + case Instruction::AGET: + case Instruction::AGET_OBJECT: + case Instruction::AGET_BOOLEAN: + case Instruction::AGET_BYTE: + case Instruction::AGET_CHAR: + case Instruction::AGET_SHORT: { + uint16_t array = GetOperandValue(mir->ssa_rep->uses[0]); + if (null_checked_.find(array) != null_checked_.end()) { + if (cu_->verbose) { + LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset; + } + mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; + } else { + null_checked_.insert(array); + } + uint16_t index = GetOperandValue(mir->ssa_rep->uses[1]); + if (ValueExists(ARRAY_REF, array, index, NO_VALUE)) { + if (cu_->verbose) { + LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset; + } + mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK; + } + mir->meta.throw_insn->optimization_flags |= mir->optimization_flags; + // Use side effect to note range check completed. + (void)LookupValue(ARRAY_REF, array, index, NO_VALUE); + // Establish value number for loaded register. Note use of memory version. + uint16_t memory_version = GetMemoryVersion(array, NO_VALUE); + uint16_t res = LookupValue(ARRAY_REF, array, index, memory_version); + if (opcode == Instruction::AGET_WIDE) { + SetOperandValueWide(mir->ssa_rep->defs[0], res); + } else { + SetOperandValue(mir->ssa_rep->defs[0], res); + } + } + break; + + case Instruction::APUT_WIDE: + case Instruction::APUT: + case Instruction::APUT_OBJECT: + case Instruction::APUT_SHORT: + case Instruction::APUT_CHAR: + case Instruction::APUT_BYTE: + case Instruction::APUT_BOOLEAN: { + int array_idx = (opcode == Instruction::APUT_WIDE) ? 2 : 1; + int index_idx = array_idx + 1; + uint16_t array = GetOperandValue(mir->ssa_rep->uses[array_idx]); + if (null_checked_.find(array) != null_checked_.end()) { + if (cu_->verbose) { + LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset; + } + mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; + } else { + null_checked_.insert(array); + } + uint16_t index = GetOperandValue(mir->ssa_rep->uses[index_idx]); + if (ValueExists(ARRAY_REF, array, index, NO_VALUE)) { + if (cu_->verbose) { + LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset; + } + mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK; + } + mir->meta.throw_insn->optimization_flags |= mir->optimization_flags; + // Use side effect to note range check completed. + (void)LookupValue(ARRAY_REF, array, index, NO_VALUE); + // Rev the memory version + AdvanceMemoryVersion(array, NO_VALUE); + } + break; + + case Instruction::IGET_OBJECT: + case Instruction::IGET_WIDE: + case Instruction::IGET: + case Instruction::IGET_CHAR: + case Instruction::IGET_SHORT: + case Instruction::IGET_BOOLEAN: + case Instruction::IGET_BYTE: { + uint16_t base = GetOperandValue(mir->ssa_rep->uses[0]); + if (null_checked_.find(base) != null_checked_.end()) { + if (cu_->verbose) { + LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset; + } + mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; + } else { + null_checked_.insert(base); + } + mir->meta.throw_insn->optimization_flags |= mir->optimization_flags; + uint16_t field_ref = mir->dalvikInsn.vC; + uint16_t memory_version = GetMemoryVersion(base, field_ref); + if (opcode == Instruction::IGET_WIDE) { + uint16_t res = LookupValue(Instruction::IGET_WIDE, base, field_ref, memory_version); + SetOperandValueWide(mir->ssa_rep->defs[0], res); + } else { + uint16_t res = LookupValue(Instruction::IGET, base, field_ref, memory_version); + SetOperandValue(mir->ssa_rep->defs[0], res); + } + } + break; + + case Instruction::IPUT_WIDE: + case Instruction::IPUT_OBJECT: + case Instruction::IPUT: + case Instruction::IPUT_BOOLEAN: + case Instruction::IPUT_BYTE: + case Instruction::IPUT_CHAR: + case Instruction::IPUT_SHORT: { + int base_reg = (opcode == Instruction::IPUT_WIDE) ? 2 : 1; + uint16_t base = GetOperandValue(mir->ssa_rep->uses[base_reg]); + if (null_checked_.find(base) != null_checked_.end()) { + if (cu_->verbose) { + LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset; + } + mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; + } else { + null_checked_.insert(base); + } + mir->meta.throw_insn->optimization_flags |= mir->optimization_flags; + uint16_t field_ref = mir->dalvikInsn.vC; + AdvanceMemoryVersion(base, field_ref); + } + break; + + case Instruction::SGET_OBJECT: + case Instruction::SGET: + case Instruction::SGET_BOOLEAN: + case Instruction::SGET_BYTE: + case Instruction::SGET_CHAR: + case Instruction::SGET_SHORT: + case Instruction::SGET_WIDE: { + uint16_t field_ref = mir->dalvikInsn.vB; + uint16_t memory_version = GetMemoryVersion(NO_VALUE, field_ref); + if (opcode == Instruction::SGET_WIDE) { + uint16_t res = LookupValue(Instruction::SGET_WIDE, NO_VALUE, field_ref, memory_version); + SetOperandValueWide(mir->ssa_rep->defs[0], res); + } else { + uint16_t res = LookupValue(Instruction::SGET, NO_VALUE, field_ref, memory_version); + SetOperandValue(mir->ssa_rep->defs[0], res); + } + } + break; + + case Instruction::SPUT_OBJECT: + case Instruction::SPUT: + case Instruction::SPUT_BOOLEAN: + case Instruction::SPUT_BYTE: + case Instruction::SPUT_CHAR: + case Instruction::SPUT_SHORT: + case Instruction::SPUT_WIDE: { + uint16_t field_ref = mir->dalvikInsn.vB; + AdvanceMemoryVersion(NO_VALUE, field_ref); + } + break; + } + return res; +} + +} // namespace art diff --git a/compiler/dex/local_value_numbering.h b/compiler/dex/local_value_numbering.h new file mode 100644 index 0000000000000000000000000000000000000000..33ca8f1ad858221d4c473ad6e8f294fc84bbb480 --- /dev/null +++ b/compiler/dex/local_value_numbering.h @@ -0,0 +1,142 @@ +/* + * 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_DEX_LOCAL_VALUE_NUMBERING_H_ +#define ART_COMPILER_DEX_LOCAL_VALUE_NUMBERING_H_ + +#include "compiler_internals.h" + +#define NO_VALUE 0xffff +#define ARRAY_REF 0xfffe + +namespace art { + +// Key is s_reg, value is value name. +typedef SafeMap SregValueMap; +// Key is concatenation of quad, value is value name. +typedef SafeMap ValueMap; +// Key represents a memory address, value is generation. +typedef SafeMap MemoryVersionMap; + +class LocalValueNumbering { + public: + explicit LocalValueNumbering(CompilationUnit* cu) : cu_(cu) {} + + static uint64_t BuildKey(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) { + return (static_cast(op) << 48 | static_cast(operand1) << 32 | + static_cast(operand2) << 16 | static_cast(modifier)); + }; + + uint16_t LookupValue(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) { + uint16_t res; + uint64_t key = BuildKey(op, operand1, operand2, modifier); + ValueMap::iterator it = value_map_.find(key); + if (it != value_map_.end()) { + res = it->second; + } else { + res = value_map_.size() + 1; + value_map_.Put(key, res); + } + return res; + }; + + bool ValueExists(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) const { + uint64_t key = BuildKey(op, operand1, operand2, modifier); + ValueMap::const_iterator it = value_map_.find(key); + return (it != value_map_.end()); + }; + + uint16_t GetMemoryVersion(uint16_t base, uint16_t field) { + uint32_t key = (base << 16) | field; + uint16_t res; + MemoryVersionMap::iterator it = memory_version_map_.find(key); + if (it == memory_version_map_.end()) { + res = 0; + memory_version_map_.Put(key, res); + } else { + res = it->second; + } + return res; + }; + + void AdvanceMemoryVersion(uint16_t base, uint16_t field) { + uint32_t key = (base << 16) | field; + MemoryVersionMap::iterator it = memory_version_map_.find(key); + if (it == memory_version_map_.end()) { + memory_version_map_.Put(key, 0); + } else { + it->second++; + } + }; + + void SetOperandValue(uint16_t s_reg, uint16_t value) { + SregValueMap::iterator it = sreg_value_map_.find(s_reg); + if (it != sreg_value_map_.end()) { + DCHECK_EQ(it->second, value); + } else { + sreg_value_map_.Put(s_reg, value); + } + }; + + uint16_t GetOperandValue(int s_reg) { + uint16_t res = NO_VALUE; + SregValueMap::iterator it = sreg_value_map_.find(s_reg); + if (it != sreg_value_map_.end()) { + res = it->second; + } else { + // First use + res = LookupValue(NO_VALUE, s_reg, NO_VALUE, NO_VALUE); + sreg_value_map_.Put(s_reg, res); + } + return res; + }; + + void SetOperandValueWide(uint16_t s_reg, uint16_t value) { + SregValueMap::iterator it = sreg_wide_value_map_.find(s_reg); + if (it != sreg_wide_value_map_.end()) { + DCHECK_EQ(it->second, value); + } else { + sreg_wide_value_map_.Put(s_reg, value); + } + }; + + uint16_t GetOperandValueWide(int s_reg) { + uint16_t res = NO_VALUE; + SregValueMap::iterator it = sreg_wide_value_map_.find(s_reg); + if (it != sreg_wide_value_map_.end()) { + res = it->second; + } else { + // First use + res = LookupValue(NO_VALUE, s_reg, NO_VALUE, NO_VALUE); + sreg_wide_value_map_.Put(s_reg, res); + } + return res; + }; + + uint16_t GetValueNumber(MIR* mir); + + private: + CompilationUnit* const cu_; + SregValueMap sreg_value_map_; + SregValueMap sreg_wide_value_map_; + ValueMap value_map_; + MemoryVersionMap memory_version_map_; + std::set null_checked_; +}; + +} // namespace art + +#endif // ART_COMPILER_DEX_LOCAL_VALUE_NUMBERING_H_ diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc new file mode 100644 index 0000000000000000000000000000000000000000..d7a4136a01aa35d0f2ca1201d54431552dddd1cd --- /dev/null +++ b/compiler/dex/mir_analysis.cc @@ -0,0 +1,1072 @@ +/* + * 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 "compiler_internals.h" +#include "dataflow_iterator-inl.h" + +namespace art { + + // Instruction characteristics used to statically identify computation-intensive methods. +const uint32_t MIRGraph::analysis_attributes_[kMirOpLast] = { + // 00 NOP + AN_NONE, + + // 01 MOVE vA, vB + AN_MOVE, + + // 02 MOVE_FROM16 vAA, vBBBB + AN_MOVE, + + // 03 MOVE_16 vAAAA, vBBBB + AN_MOVE, + + // 04 MOVE_WIDE vA, vB + AN_MOVE, + + // 05 MOVE_WIDE_FROM16 vAA, vBBBB + AN_MOVE, + + // 06 MOVE_WIDE_16 vAAAA, vBBBB + AN_MOVE, + + // 07 MOVE_OBJECT vA, vB + AN_MOVE, + + // 08 MOVE_OBJECT_FROM16 vAA, vBBBB + AN_MOVE, + + // 09 MOVE_OBJECT_16 vAAAA, vBBBB + AN_MOVE, + + // 0A MOVE_RESULT vAA + AN_MOVE, + + // 0B MOVE_RESULT_WIDE vAA + AN_MOVE, + + // 0C MOVE_RESULT_OBJECT vAA + AN_MOVE, + + // 0D MOVE_EXCEPTION vAA + AN_MOVE, + + // 0E RETURN_VOID + AN_BRANCH, + + // 0F RETURN vAA + AN_BRANCH, + + // 10 RETURN_WIDE vAA + AN_BRANCH, + + // 11 RETURN_OBJECT vAA + AN_BRANCH, + + // 12 CONST_4 vA, #+B + AN_SIMPLECONST, + + // 13 CONST_16 vAA, #+BBBB + AN_SIMPLECONST, + + // 14 CONST vAA, #+BBBBBBBB + AN_SIMPLECONST, + + // 15 CONST_HIGH16 VAA, #+BBBB0000 + AN_SIMPLECONST, + + // 16 CONST_WIDE_16 vAA, #+BBBB + AN_SIMPLECONST, + + // 17 CONST_WIDE_32 vAA, #+BBBBBBBB + AN_SIMPLECONST, + + // 18 CONST_WIDE vAA, #+BBBBBBBBBBBBBBBB + AN_SIMPLECONST, + + // 19 CONST_WIDE_HIGH16 vAA, #+BBBB000000000000 + AN_SIMPLECONST, + + // 1A CONST_STRING vAA, string@BBBB + AN_NONE, + + // 1B CONST_STRING_JUMBO vAA, string@BBBBBBBB + AN_NONE, + + // 1C CONST_CLASS vAA, type@BBBB + AN_NONE, + + // 1D MONITOR_ENTER vAA + AN_NONE, + + // 1E MONITOR_EXIT vAA + AN_NONE, + + // 1F CHK_CAST vAA, type@BBBB + AN_NONE, + + // 20 INSTANCE_OF vA, vB, type@CCCC + AN_NONE, + + // 21 ARRAY_LENGTH vA, vB + AN_ARRAYOP, + + // 22 NEW_INSTANCE vAA, type@BBBB + AN_HEAVYWEIGHT, + + // 23 NEW_ARRAY vA, vB, type@CCCC + AN_HEAVYWEIGHT, + + // 24 FILLED_NEW_ARRAY {vD, vE, vF, vG, vA} + AN_HEAVYWEIGHT, + + // 25 FILLED_NEW_ARRAY_RANGE {vCCCC .. vNNNN}, type@BBBB + AN_HEAVYWEIGHT, + + // 26 FILL_ARRAY_DATA vAA, +BBBBBBBB + AN_NONE, + + // 27 THROW vAA + AN_HEAVYWEIGHT | AN_BRANCH, + + // 28 GOTO + AN_BRANCH, + + // 29 GOTO_16 + AN_BRANCH, + + // 2A GOTO_32 + AN_BRANCH, + + // 2B PACKED_SWITCH vAA, +BBBBBBBB + AN_SWITCH, + + // 2C SPARSE_SWITCH vAA, +BBBBBBBB + AN_SWITCH, + + // 2D CMPL_FLOAT vAA, vBB, vCC + AN_MATH | AN_FP | AN_SINGLE, + + // 2E CMPG_FLOAT vAA, vBB, vCC + AN_MATH | AN_FP | AN_SINGLE, + + // 2F CMPL_DOUBLE vAA, vBB, vCC + AN_MATH | AN_FP | AN_DOUBLE, + + // 30 CMPG_DOUBLE vAA, vBB, vCC + AN_MATH | AN_FP | AN_DOUBLE, + + // 31 CMP_LONG vAA, vBB, vCC + AN_MATH | AN_LONG, + + // 32 IF_EQ vA, vB, +CCCC + AN_MATH | AN_BRANCH | AN_INT, + + // 33 IF_NE vA, vB, +CCCC + AN_MATH | AN_BRANCH | AN_INT, + + // 34 IF_LT vA, vB, +CCCC + AN_MATH | AN_BRANCH | AN_INT, + + // 35 IF_GE vA, vB, +CCCC + AN_MATH | AN_BRANCH | AN_INT, + + // 36 IF_GT vA, vB, +CCCC + AN_MATH | AN_BRANCH | AN_INT, + + // 37 IF_LE vA, vB, +CCCC + AN_MATH | AN_BRANCH | AN_INT, + + // 38 IF_EQZ vAA, +BBBB + AN_MATH | AN_BRANCH | AN_INT, + + // 39 IF_NEZ vAA, +BBBB + AN_MATH | AN_BRANCH | AN_INT, + + // 3A IF_LTZ vAA, +BBBB + AN_MATH | AN_BRANCH | AN_INT, + + // 3B IF_GEZ vAA, +BBBB + AN_MATH | AN_BRANCH | AN_INT, + + // 3C IF_GTZ vAA, +BBBB + AN_MATH | AN_BRANCH | AN_INT, + + // 3D IF_LEZ vAA, +BBBB + AN_MATH | AN_BRANCH | AN_INT, + + // 3E UNUSED_3E + AN_NONE, + + // 3F UNUSED_3F + AN_NONE, + + // 40 UNUSED_40 + AN_NONE, + + // 41 UNUSED_41 + AN_NONE, + + // 42 UNUSED_42 + AN_NONE, + + // 43 UNUSED_43 + AN_NONE, + + // 44 AGET vAA, vBB, vCC + AN_ARRAYOP, + + // 45 AGET_WIDE vAA, vBB, vCC + AN_ARRAYOP, + + // 46 AGET_OBJECT vAA, vBB, vCC + AN_ARRAYOP, + + // 47 AGET_BOOLEAN vAA, vBB, vCC + AN_ARRAYOP, + + // 48 AGET_BYTE vAA, vBB, vCC + AN_ARRAYOP, + + // 49 AGET_CHAR vAA, vBB, vCC + AN_ARRAYOP, + + // 4A AGET_SHORT vAA, vBB, vCC + AN_ARRAYOP, + + // 4B APUT vAA, vBB, vCC + AN_ARRAYOP, + + // 4C APUT_WIDE vAA, vBB, vCC + AN_ARRAYOP, + + // 4D APUT_OBJECT vAA, vBB, vCC + AN_ARRAYOP, + + // 4E APUT_BOOLEAN vAA, vBB, vCC + AN_ARRAYOP, + + // 4F APUT_BYTE vAA, vBB, vCC + AN_ARRAYOP, + + // 50 APUT_CHAR vAA, vBB, vCC + AN_ARRAYOP, + + // 51 APUT_SHORT vAA, vBB, vCC + AN_ARRAYOP, + + // 52 IGET vA, vB, field@CCCC + AN_NONE, + + // 53 IGET_WIDE vA, vB, field@CCCC + AN_NONE, + + // 54 IGET_OBJECT vA, vB, field@CCCC + AN_NONE, + + // 55 IGET_BOOLEAN vA, vB, field@CCCC + AN_NONE, + + // 56 IGET_BYTE vA, vB, field@CCCC + AN_NONE, + + // 57 IGET_CHAR vA, vB, field@CCCC + AN_NONE, + + // 58 IGET_SHORT vA, vB, field@CCCC + AN_NONE, + + // 59 IPUT vA, vB, field@CCCC + AN_NONE, + + // 5A IPUT_WIDE vA, vB, field@CCCC + AN_NONE, + + // 5B IPUT_OBJECT vA, vB, field@CCCC + AN_NONE, + + // 5C IPUT_BOOLEAN vA, vB, field@CCCC + AN_NONE, + + // 5D IPUT_BYTE vA, vB, field@CCCC + AN_NONE, + + // 5E IPUT_CHAR vA, vB, field@CCCC + AN_NONE, + + // 5F IPUT_SHORT vA, vB, field@CCCC + AN_NONE, + + // 60 SGET vAA, field@BBBB + AN_NONE, + + // 61 SGET_WIDE vAA, field@BBBB + AN_NONE, + + // 62 SGET_OBJECT vAA, field@BBBB + AN_NONE, + + // 63 SGET_BOOLEAN vAA, field@BBBB + AN_NONE, + + // 64 SGET_BYTE vAA, field@BBBB + AN_NONE, + + // 65 SGET_CHAR vAA, field@BBBB + AN_NONE, + + // 66 SGET_SHORT vAA, field@BBBB + AN_NONE, + + // 67 SPUT vAA, field@BBBB + AN_NONE, + + // 68 SPUT_WIDE vAA, field@BBBB + AN_NONE, + + // 69 SPUT_OBJECT vAA, field@BBBB + AN_NONE, + + // 6A SPUT_BOOLEAN vAA, field@BBBB + AN_NONE, + + // 6B SPUT_BYTE vAA, field@BBBB + AN_NONE, + + // 6C SPUT_CHAR vAA, field@BBBB + AN_NONE, + + // 6D SPUT_SHORT vAA, field@BBBB + AN_NONE, + + // 6E INVOKE_VIRTUAL {vD, vE, vF, vG, vA} + AN_INVOKE | AN_HEAVYWEIGHT, + + // 6F INVOKE_SUPER {vD, vE, vF, vG, vA} + AN_INVOKE | AN_HEAVYWEIGHT, + + // 70 INVOKE_DIRECT {vD, vE, vF, vG, vA} + AN_INVOKE | AN_HEAVYWEIGHT, + + // 71 INVOKE_STATIC {vD, vE, vF, vG, vA} + AN_INVOKE | AN_HEAVYWEIGHT, + + // 72 INVOKE_INTERFACE {vD, vE, vF, vG, vA} + AN_INVOKE | AN_HEAVYWEIGHT, + + // 73 UNUSED_73 + AN_NONE, + + // 74 INVOKE_VIRTUAL_RANGE {vCCCC .. vNNNN} + AN_INVOKE | AN_HEAVYWEIGHT, + + // 75 INVOKE_SUPER_RANGE {vCCCC .. vNNNN} + AN_INVOKE | AN_HEAVYWEIGHT, + + // 76 INVOKE_DIRECT_RANGE {vCCCC .. vNNNN} + AN_INVOKE | AN_HEAVYWEIGHT, + + // 77 INVOKE_STATIC_RANGE {vCCCC .. vNNNN} + AN_INVOKE | AN_HEAVYWEIGHT, + + // 78 INVOKE_INTERFACE_RANGE {vCCCC .. vNNNN} + AN_INVOKE | AN_HEAVYWEIGHT, + + // 79 UNUSED_79 + AN_NONE, + + // 7A UNUSED_7A + AN_NONE, + + // 7B NEG_INT vA, vB + AN_MATH | AN_INT, + + // 7C NOT_INT vA, vB + AN_MATH | AN_INT, + + // 7D NEG_LONG vA, vB + AN_MATH | AN_LONG, + + // 7E NOT_LONG vA, vB + AN_MATH | AN_LONG, + + // 7F NEG_FLOAT vA, vB + AN_MATH | AN_FP | AN_SINGLE, + + // 80 NEG_DOUBLE vA, vB + AN_MATH | AN_FP | AN_DOUBLE, + + // 81 INT_TO_LONG vA, vB + AN_MATH | AN_INT | AN_LONG, + + // 82 INT_TO_FLOAT vA, vB + AN_MATH | AN_FP | AN_INT | AN_SINGLE, + + // 83 INT_TO_DOUBLE vA, vB + AN_MATH | AN_FP | AN_INT | AN_DOUBLE, + + // 84 LONG_TO_INT vA, vB + AN_MATH | AN_INT | AN_LONG, + + // 85 LONG_TO_FLOAT vA, vB + AN_MATH | AN_FP | AN_LONG | AN_SINGLE, + + // 86 LONG_TO_DOUBLE vA, vB + AN_MATH | AN_FP | AN_LONG | AN_DOUBLE, + + // 87 FLOAT_TO_INT vA, vB + AN_MATH | AN_FP | AN_INT | AN_SINGLE, + + // 88 FLOAT_TO_LONG vA, vB + AN_MATH | AN_FP | AN_LONG | AN_SINGLE, + + // 89 FLOAT_TO_DOUBLE vA, vB + AN_MATH | AN_FP | AN_SINGLE | AN_DOUBLE, + + // 8A DOUBLE_TO_INT vA, vB + AN_MATH | AN_FP | AN_INT | AN_DOUBLE, + + // 8B DOUBLE_TO_LONG vA, vB + AN_MATH | AN_FP | AN_LONG | AN_DOUBLE, + + // 8C DOUBLE_TO_FLOAT vA, vB + AN_MATH | AN_FP | AN_SINGLE | AN_DOUBLE, + + // 8D INT_TO_BYTE vA, vB + AN_MATH | AN_INT, + + // 8E INT_TO_CHAR vA, vB + AN_MATH | AN_INT, + + // 8F INT_TO_SHORT vA, vB + AN_MATH | AN_INT, + + // 90 ADD_INT vAA, vBB, vCC + AN_MATH | AN_INT, + + // 91 SUB_INT vAA, vBB, vCC + AN_MATH | AN_INT, + + // 92 MUL_INT vAA, vBB, vCC + AN_MATH | AN_INT, + + // 93 DIV_INT vAA, vBB, vCC + AN_MATH | AN_INT, + + // 94 REM_INT vAA, vBB, vCC + AN_MATH | AN_INT, + + // 95 AND_INT vAA, vBB, vCC + AN_MATH | AN_INT, + + // 96 OR_INT vAA, vBB, vCC + AN_MATH | AN_INT, + + // 97 XOR_INT vAA, vBB, vCC + AN_MATH | AN_INT, + + // 98 SHL_INT vAA, vBB, vCC + AN_MATH | AN_INT, + + // 99 SHR_INT vAA, vBB, vCC + AN_MATH | AN_INT, + + // 9A USHR_INT vAA, vBB, vCC + AN_MATH | AN_INT, + + // 9B ADD_LONG vAA, vBB, vCC + AN_MATH | AN_LONG, + + // 9C SUB_LONG vAA, vBB, vCC + AN_MATH | AN_LONG, + + // 9D MUL_LONG vAA, vBB, vCC + AN_MATH | AN_LONG, + + // 9E DIV_LONG vAA, vBB, vCC + AN_MATH | AN_LONG, + + // 9F REM_LONG vAA, vBB, vCC + AN_MATH | AN_LONG, + + // A0 AND_LONG vAA, vBB, vCC + AN_MATH | AN_LONG, + + // A1 OR_LONG vAA, vBB, vCC + AN_MATH | AN_LONG, + + // A2 XOR_LONG vAA, vBB, vCC + AN_MATH | AN_LONG, + + // A3 SHL_LONG vAA, vBB, vCC + AN_MATH | AN_LONG, + + // A4 SHR_LONG vAA, vBB, vCC + AN_MATH | AN_LONG, + + // A5 USHR_LONG vAA, vBB, vCC + AN_MATH | AN_LONG, + + // A6 ADD_FLOAT vAA, vBB, vCC + AN_MATH | AN_FP | AN_SINGLE, + + // A7 SUB_FLOAT vAA, vBB, vCC + AN_MATH | AN_FP | AN_SINGLE, + + // A8 MUL_FLOAT vAA, vBB, vCC + AN_MATH | AN_FP | AN_SINGLE, + + // A9 DIV_FLOAT vAA, vBB, vCC + AN_MATH | AN_FP | AN_SINGLE, + + // AA REM_FLOAT vAA, vBB, vCC + AN_MATH | AN_FP | AN_SINGLE, + + // AB ADD_DOUBLE vAA, vBB, vCC + AN_MATH | AN_FP | AN_DOUBLE, + + // AC SUB_DOUBLE vAA, vBB, vCC + AN_MATH | AN_FP | AN_DOUBLE, + + // AD MUL_DOUBLE vAA, vBB, vCC + AN_MATH | AN_FP | AN_DOUBLE, + + // AE DIV_DOUBLE vAA, vBB, vCC + AN_MATH | AN_FP | AN_DOUBLE, + + // AF REM_DOUBLE vAA, vBB, vCC + AN_MATH | AN_FP | AN_DOUBLE, + + // B0 ADD_INT_2ADDR vA, vB + AN_MATH | AN_INT, + + // B1 SUB_INT_2ADDR vA, vB + AN_MATH | AN_INT, + + // B2 MUL_INT_2ADDR vA, vB + AN_MATH | AN_INT, + + // B3 DIV_INT_2ADDR vA, vB + AN_MATH | AN_INT, + + // B4 REM_INT_2ADDR vA, vB + AN_MATH | AN_INT, + + // B5 AND_INT_2ADDR vA, vB + AN_MATH | AN_INT, + + // B6 OR_INT_2ADDR vA, vB + AN_MATH | AN_INT, + + // B7 XOR_INT_2ADDR vA, vB + AN_MATH | AN_INT, + + // B8 SHL_INT_2ADDR vA, vB + AN_MATH | AN_INT, + + // B9 SHR_INT_2ADDR vA, vB + AN_MATH | AN_INT, + + // BA USHR_INT_2ADDR vA, vB + AN_MATH | AN_INT, + + // BB ADD_LONG_2ADDR vA, vB + AN_MATH | AN_LONG, + + // BC SUB_LONG_2ADDR vA, vB + AN_MATH | AN_LONG, + + // BD MUL_LONG_2ADDR vA, vB + AN_MATH | AN_LONG, + + // BE DIV_LONG_2ADDR vA, vB + AN_MATH | AN_LONG, + + // BF REM_LONG_2ADDR vA, vB + AN_MATH | AN_LONG, + + // C0 AND_LONG_2ADDR vA, vB + AN_MATH | AN_LONG, + + // C1 OR_LONG_2ADDR vA, vB + AN_MATH | AN_LONG, + + // C2 XOR_LONG_2ADDR vA, vB + AN_MATH | AN_LONG, + + // C3 SHL_LONG_2ADDR vA, vB + AN_MATH | AN_LONG, + + // C4 SHR_LONG_2ADDR vA, vB + AN_MATH | AN_LONG, + + // C5 USHR_LONG_2ADDR vA, vB + AN_MATH | AN_LONG, + + // C6 ADD_FLOAT_2ADDR vA, vB + AN_MATH | AN_FP | AN_SINGLE, + + // C7 SUB_FLOAT_2ADDR vA, vB + AN_MATH | AN_FP | AN_SINGLE, + + // C8 MUL_FLOAT_2ADDR vA, vB + AN_MATH | AN_FP | AN_SINGLE, + + // C9 DIV_FLOAT_2ADDR vA, vB + AN_MATH | AN_FP | AN_SINGLE, + + // CA REM_FLOAT_2ADDR vA, vB + AN_MATH | AN_FP | AN_SINGLE, + + // CB ADD_DOUBLE_2ADDR vA, vB + AN_MATH | AN_FP | AN_DOUBLE, + + // CC SUB_DOUBLE_2ADDR vA, vB + AN_MATH | AN_FP | AN_DOUBLE, + + // CD MUL_DOUBLE_2ADDR vA, vB + AN_MATH | AN_FP | AN_DOUBLE, + + // CE DIV_DOUBLE_2ADDR vA, vB + AN_MATH | AN_FP | AN_DOUBLE, + + // CF REM_DOUBLE_2ADDR vA, vB + AN_MATH | AN_FP | AN_DOUBLE, + + // D0 ADD_INT_LIT16 vA, vB, #+CCCC + AN_MATH | AN_INT, + + // D1 RSUB_INT vA, vB, #+CCCC + AN_MATH | AN_INT, + + // D2 MUL_INT_LIT16 vA, vB, #+CCCC + AN_MATH | AN_INT, + + // D3 DIV_INT_LIT16 vA, vB, #+CCCC + AN_MATH | AN_INT, + + // D4 REM_INT_LIT16 vA, vB, #+CCCC + AN_MATH | AN_INT, + + // D5 AND_INT_LIT16 vA, vB, #+CCCC + AN_MATH | AN_INT, + + // D6 OR_INT_LIT16 vA, vB, #+CCCC + AN_MATH | AN_INT, + + // D7 XOR_INT_LIT16 vA, vB, #+CCCC + AN_MATH | AN_INT, + + // D8 ADD_INT_LIT8 vAA, vBB, #+CC + AN_MATH | AN_INT, + + // D9 RSUB_INT_LIT8 vAA, vBB, #+CC + AN_MATH | AN_INT, + + // DA MUL_INT_LIT8 vAA, vBB, #+CC + AN_MATH | AN_INT, + + // DB DIV_INT_LIT8 vAA, vBB, #+CC + AN_MATH | AN_INT, + + // DC REM_INT_LIT8 vAA, vBB, #+CC + AN_MATH | AN_INT, + + // DD AND_INT_LIT8 vAA, vBB, #+CC + AN_MATH | AN_INT, + + // DE OR_INT_LIT8 vAA, vBB, #+CC + AN_MATH | AN_INT, + + // DF XOR_INT_LIT8 vAA, vBB, #+CC + AN_MATH | AN_INT, + + // E0 SHL_INT_LIT8 vAA, vBB, #+CC + AN_MATH | AN_INT, + + // E1 SHR_INT_LIT8 vAA, vBB, #+CC + AN_MATH | AN_INT, + + // E2 USHR_INT_LIT8 vAA, vBB, #+CC + AN_MATH | AN_INT, + + // E3 IGET_VOLATILE + AN_NONE, + + // E4 IPUT_VOLATILE + AN_NONE, + + // E5 SGET_VOLATILE + AN_NONE, + + // E6 SPUT_VOLATILE + AN_NONE, + + // E7 IGET_OBJECT_VOLATILE + AN_NONE, + + // E8 IGET_WIDE_VOLATILE + AN_NONE, + + // E9 IPUT_WIDE_VOLATILE + AN_NONE, + + // EA SGET_WIDE_VOLATILE + AN_NONE, + + // EB SPUT_WIDE_VOLATILE + AN_NONE, + + // EC BREAKPOINT + AN_NONE, + + // ED THROW_VERIFICATION_ERROR + AN_HEAVYWEIGHT | AN_BRANCH, + + // EE EXECUTE_INLINE + AN_NONE, + + // EF EXECUTE_INLINE_RANGE + AN_NONE, + + // F0 INVOKE_OBJECT_INIT_RANGE + AN_INVOKE | AN_HEAVYWEIGHT, + + // F1 RETURN_VOID_BARRIER + AN_BRANCH, + + // F2 IGET_QUICK + AN_NONE, + + // F3 IGET_WIDE_QUICK + AN_NONE, + + // F4 IGET_OBJECT_QUICK + AN_NONE, + + // F5 IPUT_QUICK + AN_NONE, + + // F6 IPUT_WIDE_QUICK + AN_NONE, + + // F7 IPUT_OBJECT_QUICK + AN_NONE, + + // F8 INVOKE_VIRTUAL_QUICK + AN_INVOKE | AN_HEAVYWEIGHT, + + // F9 INVOKE_VIRTUAL_QUICK_RANGE + AN_INVOKE | AN_HEAVYWEIGHT, + + // FA INVOKE_SUPER_QUICK + AN_INVOKE | AN_HEAVYWEIGHT, + + // FB INVOKE_SUPER_QUICK_RANGE + AN_INVOKE | AN_HEAVYWEIGHT, + + // FC IPUT_OBJECT_VOLATILE + AN_NONE, + + // FD SGET_OBJECT_VOLATILE + AN_NONE, + + // FE SPUT_OBJECT_VOLATILE + AN_NONE, + + // FF UNUSED_FF + AN_NONE, + + // Beginning of extended MIR opcodes + // 100 MIR_PHI + AN_NONE, + + // 101 MIR_COPY + AN_NONE, + + // 102 MIR_FUSED_CMPL_FLOAT + AN_NONE, + + // 103 MIR_FUSED_CMPG_FLOAT + AN_NONE, + + // 104 MIR_FUSED_CMPL_DOUBLE + AN_NONE, + + // 105 MIR_FUSED_CMPG_DOUBLE + AN_NONE, + + // 106 MIR_FUSED_CMP_LONG + AN_NONE, + + // 107 MIR_NOP + AN_NONE, + + // 108 MIR_NULL_CHECK + AN_NONE, + + // 109 MIR_RANGE_CHECK + AN_NONE, + + // 110 MIR_DIV_ZERO_CHECK + AN_NONE, + + // 111 MIR_CHECK + AN_NONE, + + // 112 MIR_CHECKPART2 + AN_NONE, + + // 113 MIR_SELECT + AN_NONE, +}; + +struct MethodStats { + int dex_instructions; + int math_ops; + int fp_ops; + int array_ops; + int branch_ops; + int heavyweight_ops; + bool has_computational_loop; + bool has_switch; + float math_ratio; + float fp_ratio; + float array_ratio; + float branch_ratio; + float heavyweight_ratio; +}; + +void MIRGraph::AnalyzeBlock(BasicBlock* bb, MethodStats* stats) { + if (bb->visited || (bb->block_type != kDalvikByteCode)) { + return; + } + bool computational_block = true; + bool has_math = false; + /* + * For the purposes of this scan, we want to treat the set of basic blocks broken + * by an exception edge as a single basic block. We'll scan forward along the fallthrough + * edges until we reach an explicit branch or return. + */ + BasicBlock* ending_bb = bb; + if (ending_bb->last_mir_insn != NULL) { + uint32_t ending_flags = analysis_attributes_[ending_bb->last_mir_insn->dalvikInsn.opcode]; + while ((ending_flags & AN_BRANCH) == 0) { + ending_bb = ending_bb->fall_through; + ending_flags = analysis_attributes_[ending_bb->last_mir_insn->dalvikInsn.opcode]; + } + } + /* + * Ideally, we'd weight the operations by loop nesting level, but to do so we'd + * first need to do some expensive loop detection - and the point of this is to make + * an informed guess before investing in computation. However, we can cheaply detect + * many simple loop forms without having to do full dataflow analysis. + */ + int loop_scale_factor = 1; + // Simple for and while loops + if ((ending_bb->taken != NULL) && (ending_bb->fall_through == NULL)) { + if ((ending_bb->taken->taken == bb) || (ending_bb->taken->fall_through == bb)) { + loop_scale_factor = 25; + } + } + // Simple do-while loop + if ((ending_bb->taken != NULL) && (ending_bb->taken == bb)) { + loop_scale_factor = 25; + } + + BasicBlock* tbb = bb; + bool done = false; + while (!done) { + tbb->visited = true; + for (MIR* mir = tbb->first_mir_insn; mir != NULL; mir = mir->next) { + if (static_cast(mir->dalvikInsn.opcode) >= kMirOpFirst) { + // Skip any MIR pseudo-op. + continue; + } + uint32_t flags = analysis_attributes_[mir->dalvikInsn.opcode]; + stats->dex_instructions += loop_scale_factor; + if ((flags & AN_BRANCH) == 0) { + computational_block &= ((flags & AN_COMPUTATIONAL) != 0); + } else { + stats->branch_ops += loop_scale_factor; + } + if ((flags & AN_MATH) != 0) { + stats->math_ops += loop_scale_factor; + has_math = true; + } + if ((flags & AN_FP) != 0) { + stats->fp_ops += loop_scale_factor; + } + if ((flags & AN_ARRAYOP) != 0) { + stats->array_ops += loop_scale_factor; + } + if ((flags & AN_HEAVYWEIGHT) != 0) { + stats->heavyweight_ops += loop_scale_factor; + } + if ((flags & AN_SWITCH) != 0) { + stats->has_switch = true; + } + } + if (tbb == ending_bb) { + done = true; + } else { + tbb = tbb->fall_through; + } + } + if (has_math && computational_block && (loop_scale_factor > 1)) { + stats->has_computational_loop = true; + } +} + +bool MIRGraph::ComputeSkipCompilation(MethodStats* stats, bool skip_default) { + float count = stats->dex_instructions; + stats->math_ratio = stats->math_ops / count; + stats->fp_ratio = stats->fp_ops / count; + stats->branch_ratio = stats->branch_ops / count; + stats->array_ratio = stats->array_ops / count; + stats->heavyweight_ratio = stats->heavyweight_ops / count; + + if (cu_->enable_debug & (1 << kDebugShowFilterStats)) { + LOG(INFO) << "STATS " << stats->dex_instructions << ", math:" + << stats->math_ratio << ", fp:" + << stats->fp_ratio << ", br:" + << stats->branch_ratio << ", hw:" + << stats->heavyweight_ratio << ", arr:" + << stats->array_ratio << ", hot:" + << stats->has_computational_loop << ", " + << PrettyMethod(cu_->method_idx, *cu_->dex_file); + } + + // Computation intensive? + if (stats->has_computational_loop && (stats->heavyweight_ratio < 0.04)) { + return false; + } + + // Complex, logic-intensive? + if ((GetNumDalvikInsns() > Runtime::Current()->GetSmallMethodThreshold()) && + stats->branch_ratio > 0.3) { + return false; + } + + // Significant floating point? + if (stats->fp_ratio > 0.05) { + return false; + } + + // Significant generic math? + if (stats->math_ratio > 0.3) { + return false; + } + + // If array-intensive, compiling is probably worthwhile. + if (stats->array_ratio > 0.1) { + return false; + } + + // Switch operations benefit greatly from compilation, so go ahead and spend the cycles. + if (stats->has_switch) { + return false; + } + + // If significant in size and high proportion of expensive operations, skip. + if ((GetNumDalvikInsns() > Runtime::Current()->GetSmallMethodThreshold()) && + (stats->heavyweight_ratio > 0.3)) { + return true; + } + + return skip_default; +} + + /* + * Will eventually want this to be a bit more sophisticated and happen at verification time. + * Ultimate goal is to drive with profile data. + */ +bool MIRGraph::SkipCompilation(Runtime::CompilerFilter compiler_filter) { + if (compiler_filter == Runtime::kEverything) { + return false; + } + + if (compiler_filter == Runtime::kInterpretOnly) { + LOG(WARNING) << "InterpretOnly should ideally be filtered out prior to parsing."; + return true; + } + + // Set up compilation cutoffs based on current filter mode. + size_t small_cutoff = 0; + size_t default_cutoff = 0; + switch (compiler_filter) { + case Runtime::kBalanced: + small_cutoff = Runtime::Current()->GetSmallMethodThreshold(); + default_cutoff = Runtime::Current()->GetLargeMethodThreshold(); + break; + case Runtime::kSpace: + small_cutoff = Runtime::Current()->GetTinyMethodThreshold(); + default_cutoff = Runtime::Current()->GetSmallMethodThreshold(); + break; + case Runtime::kSpeed: + small_cutoff = Runtime::Current()->GetHugeMethodThreshold(); + default_cutoff = Runtime::Current()->GetHugeMethodThreshold(); + break; + default: + LOG(FATAL) << "Unexpected compiler_filter_: " << compiler_filter; + } + + // If size < cutoff, assume we'll compile - but allow removal. + bool skip_compilation = (GetNumDalvikInsns() >= default_cutoff); + + /* + * Filter 1: Huge methods are likely to be machine generated, but some aren't. + * If huge, assume we won't compile, but allow futher analysis to turn it back on. + */ + if (GetNumDalvikInsns() > Runtime::Current()->GetHugeMethodThreshold()) { + skip_compilation = true; + } else if (compiler_filter == Runtime::kSpeed) { + // If not huge, compile. + return false; + } + + // Filter 2: Skip class initializers. + if (((cu_->access_flags & kAccConstructor) != 0) && ((cu_->access_flags & kAccStatic) != 0)) { + return true; + } + + // Filter 3: if this method is a special pattern, go ahead and emit the canned pattern. + if (IsSpecialCase()) { + return false; + } + + // Filter 4: if small, just compile. + if (GetNumDalvikInsns() < small_cutoff) { + return false; + } + + // Analyze graph for: + // o floating point computation + // o basic blocks contained in loop with heavy arithmetic. + // o proportion of conditional branches. + + MethodStats stats; + memset(&stats, 0, sizeof(stats)); + + ClearAllVisitedFlags(); + AllNodesIterator iter(this, false /* not iterative */); + for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { + AnalyzeBlock(bb, &stats); + } + + return ComputeSkipCompilation(&stats, skip_compilation); +} + +} // namespace art diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc new file mode 100644 index 0000000000000000000000000000000000000000..3a73717a7b03ccca5292d826facae858b7e80828 --- /dev/null +++ b/compiler/dex/mir_dataflow.cc @@ -0,0 +1,1340 @@ +/* + * 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 "compiler_internals.h" +#include "local_value_numbering.h" +#include "dataflow_iterator-inl.h" + +namespace art { + +/* + * Main table containing data flow attributes for each bytecode. The + * first kNumPackedOpcodes entries are for Dalvik bytecode + * instructions, where extended opcode at the MIR level are appended + * afterwards. + * + * TODO - many optimization flags are incomplete - they will only limit the + * scope of optimizations but will not cause mis-optimizations. + */ +const int MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { + // 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, + + // Beginning of extended MIR opcodes + // 100 MIR_PHI + DF_DA | DF_NULL_TRANSFER_N, + + // 101 MIR_COPY + DF_DA | DF_UB | DF_IS_MOVE, + + // 102 MIR_FUSED_CMPL_FLOAT + DF_UA | DF_UB | DF_FP_A | DF_FP_B, + + // 103 MIR_FUSED_CMPG_FLOAT + DF_UA | DF_UB | DF_FP_A | DF_FP_B, + + // 104 MIR_FUSED_CMPL_DOUBLE + DF_UA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, + + // 105 MIR_FUSED_CMPG_DOUBLE + DF_UA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, + + // 106 MIR_FUSED_CMP_LONG + DF_UA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, + + // 107 MIR_NOP + DF_NOP, + + // 108 MIR_NULL_CHECK + 0, + + // 109 MIR_RANGE_CHECK + 0, + + // 110 MIR_DIV_ZERO_CHECK + 0, + + // 111 MIR_CHECK + 0, + + // 112 MIR_CHECKPART2 + 0, + + // 113 MIR_SELECT + DF_DA | DF_UB, +}; + +/* Return the base virtual register for a SSA name */ +int MIRGraph::SRegToVReg(int ssa_reg) const { + return ssa_base_vregs_->Get(ssa_reg); +} + +/* 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) { + use_v->SetBit(dalvik_reg_id); + if (!def_v->IsBitSet(dalvik_reg_id)) { + live_in_v->SetBit(dalvik_reg_id); + } +} + +/* Mark a reg as being defined */ +void MIRGraph::HandleDef(ArenaBitVector* def_v, int dalvik_reg_id) { + def_v->SetBit(dalvik_reg_id); +} + +/* + * Find out live-in variables for natural loops. Variables that are live-in in + * the main loop body are considered to be defined in the entry block. + */ +bool MIRGraph::FindLocalLiveIn(BasicBlock* bb) { + MIR* mir; + ArenaBitVector *use_v, *def_v, *live_in_v; + + if (bb->data_flow_info == NULL) return false; + + use_v = bb->data_flow_info->use_v = + new (arena_) ArenaBitVector(arena_, cu_->num_dalvik_registers, false, kBitMapUse); + def_v = bb->data_flow_info->def_v = + new (arena_) ArenaBitVector(arena_, cu_->num_dalvik_registers, false, kBitMapDef); + live_in_v = bb->data_flow_info->live_in_v = + new (arena_) ArenaBitVector(arena_, cu_->num_dalvik_registers, false, kBitMapLiveIn); + + for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { + int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; + DecodedInstruction *d_insn = &mir->dalvikInsn; + + if (df_attributes & DF_HAS_USES) { + if (df_attributes & DF_UA) { + HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vA); + if (df_attributes & DF_A_WIDE) { + HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vA+1); + } + } + if (df_attributes & DF_UB) { + HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vB); + if (df_attributes & DF_B_WIDE) { + HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vB+1); + } + } + if (df_attributes & DF_UC) { + HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vC); + if (df_attributes & DF_C_WIDE) { + HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vC+1); + } + } + } + if (df_attributes & DF_FORMAT_35C) { + for (unsigned int i = 0; i < d_insn->vA; i++) { + HandleLiveInUse(use_v, def_v, live_in_v, d_insn->arg[i]); + } + } + if (df_attributes & DF_FORMAT_3RC) { + for (unsigned int i = 0; i < d_insn->vA; i++) { + HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vC+i); + } + } + if (df_attributes & DF_HAS_DEFS) { + HandleDef(def_v, d_insn->vA); + if (df_attributes & DF_A_WIDE) { + HandleDef(def_v, d_insn->vA+1); + } + } + } + return true; +} + +int MIRGraph::AddNewSReg(int v_reg) { + // Compiler temps always have a subscript of 0 + int subscript = (v_reg < 0) ? 0 : ++ssa_last_defs_[v_reg]; + int ssa_reg = GetNumSSARegs(); + SetNumSSARegs(ssa_reg + 1); + ssa_base_vregs_->Insert(v_reg); + ssa_subscripts_->Insert(subscript); + DCHECK_EQ(ssa_base_vregs_->Size(), ssa_subscripts_->Size()); + return ssa_reg; +} + +/* Find out the latest SSA register for a given Dalvik register */ +void MIRGraph::HandleSSAUse(int* uses, int dalvik_reg, int reg_index) { + DCHECK((dalvik_reg >= 0) && (dalvik_reg < cu_->num_dalvik_registers)); + uses[reg_index] = vreg_to_ssa_map_[dalvik_reg]; +} + +/* Setup a new SSA register for a given Dalvik register */ +void MIRGraph::HandleSSADef(int* defs, int dalvik_reg, int reg_index) { + DCHECK((dalvik_reg >= 0) && (dalvik_reg < cu_->num_dalvik_registers)); + int ssa_reg = AddNewSReg(dalvik_reg); + vreg_to_ssa_map_[dalvik_reg] = ssa_reg; + defs[reg_index] = ssa_reg; +} + +/* Look up new SSA names for format_35c instructions */ +void MIRGraph::DataFlowSSAFormat35C(MIR* mir) { + DecodedInstruction *d_insn = &mir->dalvikInsn; + int num_uses = d_insn->vA; + int i; + + mir->ssa_rep->num_uses = num_uses; + mir->ssa_rep->uses = static_cast(arena_->Alloc(sizeof(int) * num_uses, + ArenaAllocator::kAllocDFInfo)); + // NOTE: will be filled in during type & size inference pass + mir->ssa_rep->fp_use = static_cast(arena_->Alloc(sizeof(bool) * num_uses, + ArenaAllocator::kAllocDFInfo)); + + for (i = 0; i < num_uses; i++) { + HandleSSAUse(mir->ssa_rep->uses, d_insn->arg[i], i); + } +} + +/* Look up new SSA names for format_3rc instructions */ +void MIRGraph::DataFlowSSAFormat3RC(MIR* mir) { + DecodedInstruction *d_insn = &mir->dalvikInsn; + int num_uses = d_insn->vA; + int i; + + mir->ssa_rep->num_uses = num_uses; + mir->ssa_rep->uses = static_cast(arena_->Alloc(sizeof(int) * num_uses, + ArenaAllocator::kAllocDFInfo)); + // NOTE: will be filled in during type & size inference pass + mir->ssa_rep->fp_use = static_cast(arena_->Alloc(sizeof(bool) * num_uses, + ArenaAllocator::kAllocDFInfo)); + + for (i = 0; i < num_uses; i++) { + HandleSSAUse(mir->ssa_rep->uses, d_insn->vC+i, i); + } +} + +/* 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) { + mir->ssa_rep = + static_cast(arena_->Alloc(sizeof(SSARepresentation), + ArenaAllocator::kAllocDFInfo)); + + int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; + + // If not a pseudo-op, note non-leaf or can throw + if (static_cast(mir->dalvikInsn.opcode) < + static_cast(kNumPackedOpcodes)) { + int flags = Instruction::FlagsOf(mir->dalvikInsn.opcode); + + if (flags & Instruction::kInvoke) { + attributes_ &= ~METHOD_IS_LEAF; + } + } + + int num_uses = 0; + + if (df_attributes & DF_FORMAT_35C) { + DataFlowSSAFormat35C(mir); + continue; + } + + if (df_attributes & DF_FORMAT_3RC) { + DataFlowSSAFormat3RC(mir); + continue; + } + + if (df_attributes & DF_HAS_USES) { + if (df_attributes & DF_UA) { + num_uses++; + if (df_attributes & DF_A_WIDE) { + num_uses++; + } + } + if (df_attributes & DF_UB) { + num_uses++; + if (df_attributes & DF_B_WIDE) { + num_uses++; + } + } + if (df_attributes & DF_UC) { + num_uses++; + if (df_attributes & DF_C_WIDE) { + num_uses++; + } + } + } + + if (num_uses) { + mir->ssa_rep->num_uses = num_uses; + mir->ssa_rep->uses = static_cast(arena_->Alloc(sizeof(int) * num_uses, + ArenaAllocator::kAllocDFInfo)); + mir->ssa_rep->fp_use = static_cast(arena_->Alloc(sizeof(bool) * num_uses, + ArenaAllocator::kAllocDFInfo)); + } + + int num_defs = 0; + + if (df_attributes & DF_HAS_DEFS) { + num_defs++; + if (df_attributes & DF_A_WIDE) { + num_defs++; + } + } + + if (num_defs) { + mir->ssa_rep->num_defs = num_defs; + mir->ssa_rep->defs = static_cast(arena_->Alloc(sizeof(int) * num_defs, + ArenaAllocator::kAllocDFInfo)); + mir->ssa_rep->fp_def = static_cast(arena_->Alloc(sizeof(bool) * num_defs, + ArenaAllocator::kAllocDFInfo)); + } + + DecodedInstruction *d_insn = &mir->dalvikInsn; + + if (df_attributes & DF_HAS_USES) { + num_uses = 0; + if (df_attributes & DF_UA) { + mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_A; + HandleSSAUse(mir->ssa_rep->uses, d_insn->vA, num_uses++); + if (df_attributes & DF_A_WIDE) { + mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_A; + HandleSSAUse(mir->ssa_rep->uses, d_insn->vA+1, num_uses++); + } + } + if (df_attributes & DF_UB) { + mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_B; + HandleSSAUse(mir->ssa_rep->uses, d_insn->vB, num_uses++); + if (df_attributes & DF_B_WIDE) { + mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_B; + HandleSSAUse(mir->ssa_rep->uses, d_insn->vB+1, num_uses++); + } + } + if (df_attributes & DF_UC) { + mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_C; + HandleSSAUse(mir->ssa_rep->uses, d_insn->vC, num_uses++); + if (df_attributes & DF_C_WIDE) { + mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_C; + HandleSSAUse(mir->ssa_rep->uses, d_insn->vC+1, num_uses++); + } + } + } + if (df_attributes & DF_HAS_DEFS) { + mir->ssa_rep->fp_def[0] = df_attributes & DF_FP_A; + HandleSSADef(mir->ssa_rep->defs, d_insn->vA, 0); + if (df_attributes & DF_A_WIDE) { + mir->ssa_rep->fp_def[1] = df_attributes & DF_FP_A; + HandleSSADef(mir->ssa_rep->defs, d_insn->vA+1, 1); + } + } + } + + /* + * Take a snapshot of Dalvik->SSA mapping at the end of each block. The + * input to PHI nodes can be derived from the snapshot of all + * predecessor blocks. + */ + bb->data_flow_info->vreg_to_ssa_map = + static_cast(arena_->Alloc(sizeof(int) * cu_->num_dalvik_registers, + ArenaAllocator::kAllocDFInfo)); + + memcpy(bb->data_flow_info->vreg_to_ssa_map, vreg_to_ssa_map_, + sizeof(int) * cu_->num_dalvik_registers); + return true; +} + +/* Setup the basic data structures for SSA conversion */ +void MIRGraph::CompilerInitializeSSAConversion() { + size_t num_dalvik_reg = cu_->num_dalvik_registers; + + ssa_base_vregs_ = new (arena_) GrowableArray(arena_, num_dalvik_reg + GetDefCount() + 128, + kGrowableArraySSAtoDalvikMap); + ssa_subscripts_ = new (arena_) GrowableArray(arena_, num_dalvik_reg + GetDefCount() + 128, + kGrowableArraySSAtoDalvikMap); + /* + * Initial number of SSA registers is equal to the number of Dalvik + * registers. + */ + SetNumSSARegs(num_dalvik_reg); + + /* + * Initialize the SSA2Dalvik map list. For the first num_dalvik_reg elements, + * the subscript is 0 so we use the ENCODE_REG_SUB macro to encode the value + * into "(0 << 16) | i" + */ + for (unsigned int i = 0; i < num_dalvik_reg; i++) { + ssa_base_vregs_->Insert(i); + ssa_subscripts_->Insert(0); + } + + /* + * Initialize the DalvikToSSAMap map. There is one entry for each + * Dalvik register, and the SSA names for those are the same. + */ + vreg_to_ssa_map_ = + static_cast(arena_->Alloc(sizeof(int) * num_dalvik_reg, + ArenaAllocator::kAllocDFInfo)); + /* Keep track of the higest def for each dalvik reg */ + ssa_last_defs_ = + static_cast(arena_->Alloc(sizeof(int) * num_dalvik_reg, + ArenaAllocator::kAllocDFInfo)); + + for (unsigned int i = 0; i < num_dalvik_reg; i++) { + vreg_to_ssa_map_[i] = i; + ssa_last_defs_[i] = 0; + } + + /* Add ssa reg for Method* */ + method_sreg_ = AddNewSReg(SSA_METHOD_BASEREG); + + /* + * Allocate the BasicBlockDataFlow structure for the entry and code blocks + */ + GrowableArray::Iterator iterator(&block_list_); + + while (true) { + BasicBlock* bb = iterator.Next(); + if (bb == NULL) break; + if (bb->hidden == true) continue; + if (bb->block_type == kDalvikByteCode || + bb->block_type == kEntryBlock || + bb->block_type == kExitBlock) { + bb->data_flow_info = + static_cast(arena_->Alloc(sizeof(BasicBlockDataFlow), + ArenaAllocator::kAllocDFInfo)); + } + } +} + +/* + * This function will make a best guess at whether the invoke will + * end up using Method*. It isn't critical to get it exactly right, + * and attempting to do would involve more complexity than it's + * worth. + */ +bool MIRGraph::InvokeUsesMethodStar(MIR* mir) { + InvokeType type; + Instruction::Code opcode = mir->dalvikInsn.opcode; + switch (opcode) { + case Instruction::INVOKE_STATIC: + case Instruction::INVOKE_STATIC_RANGE: + type = kStatic; + break; + case Instruction::INVOKE_DIRECT: + case Instruction::INVOKE_DIRECT_RANGE: + type = kDirect; + break; + case Instruction::INVOKE_VIRTUAL: + case Instruction::INVOKE_VIRTUAL_RANGE: + type = kVirtual; + break; + case Instruction::INVOKE_INTERFACE: + case Instruction::INVOKE_INTERFACE_RANGE: + return false; + case Instruction::INVOKE_SUPER_RANGE: + case Instruction::INVOKE_SUPER: + type = kSuper; + break; + default: + LOG(WARNING) << "Unexpected invoke op: " << opcode; + return false; + } + DexCompilationUnit m_unit(cu_); + MethodReference target_method(cu_->dex_file, mir->dalvikInsn.vB); + int vtable_idx; + uintptr_t direct_code; + uintptr_t direct_method; + uint32_t current_offset = static_cast(current_offset_); + bool fast_path = + cu_->compiler_driver->ComputeInvokeInfo(&m_unit, current_offset, + type, target_method, + vtable_idx, + direct_code, direct_method, + false) && + !(cu_->enable_debug & (1 << kDebugSlowInvokePath)); + return (((type == kDirect) || (type == kStatic)) && + fast_path && ((direct_code == 0) || (direct_method == 0))); +} + +/* + * Count uses, weighting by loop nesting depth. This code only + * counts explicitly used s_regs. A later phase will add implicit + * counts for things such as Method*, null-checked references, etc. + */ +bool MIRGraph::CountUses(struct BasicBlock* bb) { + if (bb->block_type != kDalvikByteCode) { + return false; + } + for (MIR* mir = bb->first_mir_insn; (mir != NULL); mir = mir->next) { + if (mir->ssa_rep == NULL) { + continue; + } + uint32_t weight = std::min(16U, static_cast(bb->nesting_depth)); + for (int i = 0; i < mir->ssa_rep->num_uses; i++) { + int s_reg = mir->ssa_rep->uses[i]; + raw_use_counts_.Increment(s_reg); + use_counts_.Put(s_reg, use_counts_.Get(s_reg) + (1 << weight)); + } + if (!(cu_->disable_opt & (1 << kPromoteCompilerTemps))) { + int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; + // Implicit use of Method* ? */ + if (df_attributes & DF_UMS) { + /* + * Some invokes will not use Method* - need to perform test similar + * to that found in GenInvoke() to decide whether to count refs + * for Method* on invoke-class opcodes. + * TODO: refactor for common test here, save results for GenInvoke + */ + int uses_method_star = true; + if ((df_attributes & (DF_FORMAT_35C | DF_FORMAT_3RC)) && + !(df_attributes & DF_NON_NULL_RET)) { + uses_method_star &= InvokeUsesMethodStar(mir); + } + if (uses_method_star) { + raw_use_counts_.Increment(method_sreg_); + use_counts_.Put(method_sreg_, use_counts_.Get(method_sreg_) + (1 << weight)); + } + } + } + } + return false; +} + +void MIRGraph::MethodUseCount() { + // Now that we know, resize the lists. + int num_ssa_regs = GetNumSSARegs(); + use_counts_.Resize(num_ssa_regs + 32); + raw_use_counts_.Resize(num_ssa_regs + 32); + // Initialize list + for (int i = 0; i < num_ssa_regs; i++) { + use_counts_.Insert(0); + raw_use_counts_.Insert(0); + } + if (cu_->disable_opt & (1 << kPromoteRegs)) { + return; + } + AllNodesIterator iter(this, false /* not iterative */); + for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { + CountUses(bb); + } +} + +/* Verify if all the successor is connected with all the claimed predecessors */ +bool MIRGraph::VerifyPredInfo(BasicBlock* bb) { + GrowableArray::Iterator iter(bb->predecessors); + + while (true) { + BasicBlock *pred_bb = iter.Next(); + if (!pred_bb) break; + bool found = false; + if (pred_bb->taken == bb) { + found = true; + } else if (pred_bb->fall_through == bb) { + found = true; + } else if (pred_bb->successor_block_list.block_list_type != kNotUsed) { + GrowableArray::Iterator iterator(pred_bb->successor_block_list.blocks); + while (true) { + SuccessorBlockInfo *successor_block_info = iterator.Next(); + if (successor_block_info == NULL) break; + BasicBlock *succ_bb = successor_block_info->block; + if (succ_bb == bb) { + found = true; + break; + } + } + } + if (found == false) { + char block_name1[BLOCK_NAME_LEN], block_name2[BLOCK_NAME_LEN]; + GetBlockName(bb, block_name1); + GetBlockName(pred_bb, block_name2); + DumpCFG("/sdcard/cfg/", false); + LOG(FATAL) << "Successor " << block_name1 << "not found from " + << block_name2; + } + } + return true; +} + +void MIRGraph::VerifyDataflow() { + /* Verify if all blocks are connected as claimed */ + AllNodesIterator iter(this, false /* not iterative */); + for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { + VerifyPredInfo(bb); + } +} + +} // namespace art diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc new file mode 100644 index 0000000000000000000000000000000000000000..a12bf39e64539452b3a4fd2c8191e6eea2359929 --- /dev/null +++ b/compiler/dex/mir_graph.cc @@ -0,0 +1,1156 @@ +/* + * 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/stl_util.h" +#include "compiler_internals.h" +#include "dex_file-inl.h" +#include "leb128.h" +#include "mir_graph.h" + +namespace art { + +#define MAX_PATTERN_LEN 5 + +struct CodePattern { + const Instruction::Code opcodes[MAX_PATTERN_LEN]; + const SpecialCaseHandler handler_code; +}; + +static const CodePattern special_patterns[] = { + {{Instruction::RETURN_VOID}, kNullMethod}, + {{Instruction::CONST, Instruction::RETURN}, kConstFunction}, + {{Instruction::CONST_4, Instruction::RETURN}, kConstFunction}, + {{Instruction::CONST_4, Instruction::RETURN_OBJECT}, kConstFunction}, + {{Instruction::CONST_16, Instruction::RETURN}, kConstFunction}, + {{Instruction::IGET, Instruction:: RETURN}, kIGet}, + {{Instruction::IGET_BOOLEAN, Instruction::RETURN}, kIGetBoolean}, + {{Instruction::IGET_OBJECT, Instruction::RETURN_OBJECT}, kIGetObject}, + {{Instruction::IGET_BYTE, Instruction::RETURN}, kIGetByte}, + {{Instruction::IGET_CHAR, Instruction::RETURN}, kIGetChar}, + {{Instruction::IGET_SHORT, Instruction::RETURN}, kIGetShort}, + {{Instruction::IGET_WIDE, Instruction::RETURN_WIDE}, kIGetWide}, + {{Instruction::IPUT, Instruction::RETURN_VOID}, kIPut}, + {{Instruction::IPUT_BOOLEAN, Instruction::RETURN_VOID}, kIPutBoolean}, + {{Instruction::IPUT_OBJECT, Instruction::RETURN_VOID}, kIPutObject}, + {{Instruction::IPUT_BYTE, Instruction::RETURN_VOID}, kIPutByte}, + {{Instruction::IPUT_CHAR, Instruction::RETURN_VOID}, kIPutChar}, + {{Instruction::IPUT_SHORT, Instruction::RETURN_VOID}, kIPutShort}, + {{Instruction::IPUT_WIDE, Instruction::RETURN_VOID}, kIPutWide}, + {{Instruction::RETURN}, kIdentity}, + {{Instruction::RETURN_OBJECT}, kIdentity}, + {{Instruction::RETURN_WIDE}, kIdentity}, +}; + +const char* MIRGraph::extended_mir_op_names_[kMirOpLast - kMirOpFirst] = { + "Phi", + "Copy", + "FusedCmplFloat", + "FusedCmpgFloat", + "FusedCmplDouble", + "FusedCmpgDouble", + "FusedCmpLong", + "Nop", + "OpNullCheck", + "OpRangeCheck", + "OpDivZeroCheck", + "Check1", + "Check2", + "Select", +}; + +MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena) + : reg_location_(NULL), + compiler_temps_(arena, 6, kGrowableArrayMisc), + cu_(cu), + ssa_base_vregs_(NULL), + ssa_subscripts_(NULL), + vreg_to_ssa_map_(NULL), + ssa_last_defs_(NULL), + is_constant_v_(NULL), + constant_values_(NULL), + use_counts_(arena, 256, kGrowableArrayMisc), + raw_use_counts_(arena, 256, kGrowableArrayMisc), + num_reachable_blocks_(0), + dfs_order_(NULL), + dfs_post_order_(NULL), + dom_post_order_traversal_(NULL), + i_dom_list_(NULL), + def_block_matrix_(NULL), + temp_block_v_(NULL), + temp_dalvik_register_v_(NULL), + temp_ssa_register_v_(NULL), + block_list_(arena, 100, kGrowableArrayBlockList), + try_block_addr_(NULL), + entry_block_(NULL), + exit_block_(NULL), + cur_block_(NULL), + num_blocks_(0), + current_code_item_(NULL), + current_method_(kInvalidEntry), + current_offset_(kInvalidEntry), + def_count_(0), + opcode_count_(NULL), + num_ssa_regs_(0), + method_sreg_(0), + attributes_(METHOD_IS_LEAF), // Start with leaf assumption, change on encountering invoke. + checkstats_(NULL), + special_case_(kNoHandler), + arena_(arena) { + try_block_addr_ = new (arena_) ArenaBitVector(arena_, 0, true /* expandable */); +} + +MIRGraph::~MIRGraph() { + STLDeleteElements(&m_units_); +} + +/* + * Parse an instruction, return the length of the instruction + */ +int MIRGraph::ParseInsn(const uint16_t* code_ptr, DecodedInstruction* decoded_instruction) { + const Instruction* instruction = Instruction::At(code_ptr); + *decoded_instruction = DecodedInstruction(instruction); + + return instruction->SizeInCodeUnits(); +} + + +/* Split an existing block from the specified code offset into two */ +BasicBlock* MIRGraph::SplitBlock(unsigned int code_offset, + BasicBlock* orig_block, BasicBlock** immed_pred_block_p) { + MIR* insn = orig_block->first_mir_insn; + while (insn) { + if (insn->offset == code_offset) break; + insn = insn->next; + } + if (insn == NULL) { + LOG(FATAL) << "Break split failed"; + } + BasicBlock *bottom_block = NewMemBB(kDalvikByteCode, num_blocks_++); + block_list_.Insert(bottom_block); + + bottom_block->start_offset = code_offset; + bottom_block->first_mir_insn = insn; + bottom_block->last_mir_insn = orig_block->last_mir_insn; + + /* If this block was terminated by a return, the flag needs to go with the bottom block */ + bottom_block->terminated_by_return = orig_block->terminated_by_return; + orig_block->terminated_by_return = false; + + /* Add it to the quick lookup cache */ + block_map_.Put(bottom_block->start_offset, bottom_block); + + /* Handle the taken path */ + bottom_block->taken = orig_block->taken; + if (bottom_block->taken) { + orig_block->taken = NULL; + bottom_block->taken->predecessors->Delete(orig_block); + bottom_block->taken->predecessors->Insert(bottom_block); + } + + /* Handle the fallthrough path */ + bottom_block->fall_through = orig_block->fall_through; + orig_block->fall_through = bottom_block; + bottom_block->predecessors->Insert(orig_block); + if (bottom_block->fall_through) { + bottom_block->fall_through->predecessors->Delete(orig_block); + bottom_block->fall_through->predecessors->Insert(bottom_block); + } + + /* Handle the successor list */ + if (orig_block->successor_block_list.block_list_type != kNotUsed) { + bottom_block->successor_block_list = orig_block->successor_block_list; + orig_block->successor_block_list.block_list_type = kNotUsed; + GrowableArray::Iterator iterator(bottom_block->successor_block_list.blocks); + while (true) { + SuccessorBlockInfo *successor_block_info = iterator.Next(); + if (successor_block_info == NULL) break; + BasicBlock *bb = successor_block_info->block; + bb->predecessors->Delete(orig_block); + bb->predecessors->Insert(bottom_block); + } + } + + orig_block->last_mir_insn = insn->prev; + + insn->prev->next = NULL; + insn->prev = NULL; + /* + * Update the immediate predecessor block pointer so that outgoing edges + * can be applied to the proper block. + */ + if (immed_pred_block_p) { + DCHECK_EQ(*immed_pred_block_p, orig_block); + *immed_pred_block_p = bottom_block; + } + return bottom_block; +} + +/* + * Given a code offset, find out the block that starts with it. If the offset + * is in the middle of an existing block, split it into two. If immed_pred_block_p + * is not non-null and is the block being split, update *immed_pred_block_p to + * point to the bottom block so that outgoing edges can be set up properly + * (by the caller) + * Utilizes a map for fast lookup of the typical cases. + */ +BasicBlock* MIRGraph::FindBlock(unsigned int code_offset, bool split, bool create, + BasicBlock** immed_pred_block_p) { + BasicBlock* bb; + unsigned int i; + SafeMap::iterator it; + + it = block_map_.find(code_offset); + if (it != block_map_.end()) { + return it->second; + } else if (!create) { + return NULL; + } + + if (split) { + for (i = 0; i < block_list_.Size(); i++) { + bb = block_list_.Get(i); + if (bb->block_type != kDalvikByteCode) continue; + /* Check if a branch jumps into the middle of an existing block */ + if ((code_offset > bb->start_offset) && (bb->last_mir_insn != NULL) && + (code_offset <= bb->last_mir_insn->offset)) { + BasicBlock *new_bb = SplitBlock(code_offset, bb, bb == *immed_pred_block_p ? + immed_pred_block_p : NULL); + return new_bb; + } + } + } + + /* Create a new one */ + bb = NewMemBB(kDalvikByteCode, num_blocks_++); + block_list_.Insert(bb); + bb->start_offset = code_offset; + block_map_.Put(bb->start_offset, bb); + return bb; +} + +/* Identify code range in try blocks and set up the empty catch blocks */ +void MIRGraph::ProcessTryCatchBlocks() { + int tries_size = current_code_item_->tries_size_; + int offset; + + if (tries_size == 0) { + return; + } + + for (int i = 0; i < tries_size; i++) { + const DexFile::TryItem* pTry = + DexFile::GetTryItems(*current_code_item_, i); + int start_offset = pTry->start_addr_; + int end_offset = start_offset + pTry->insn_count_; + for (offset = start_offset; offset < end_offset; offset++) { + try_block_addr_->SetBit(offset); + } + } + + // Iterate over each of the handlers to enqueue the empty Catch blocks + const byte* handlers_ptr = DexFile::GetCatchHandlerData(*current_code_item_, 0); + uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); + for (uint32_t idx = 0; idx < handlers_size; idx++) { + CatchHandlerIterator iterator(handlers_ptr); + for (; iterator.HasNext(); iterator.Next()) { + uint32_t address = iterator.GetHandlerAddress(); + FindBlock(address, false /* split */, true /*create*/, + /* immed_pred_block_p */ NULL); + } + handlers_ptr = iterator.EndDataPointer(); + } +} + +/* Process instructions with the kBranch flag */ +BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width, + int flags, const uint16_t* code_ptr, + const uint16_t* code_end) { + int target = cur_offset; + switch (insn->dalvikInsn.opcode) { + case Instruction::GOTO: + case Instruction::GOTO_16: + case Instruction::GOTO_32: + target += insn->dalvikInsn.vA; + break; + case Instruction::IF_EQ: + case Instruction::IF_NE: + case Instruction::IF_LT: + case Instruction::IF_GE: + case Instruction::IF_GT: + case Instruction::IF_LE: + cur_block->conditional_branch = true; + target += insn->dalvikInsn.vC; + break; + case Instruction::IF_EQZ: + case Instruction::IF_NEZ: + case Instruction::IF_LTZ: + case Instruction::IF_GEZ: + case Instruction::IF_GTZ: + case Instruction::IF_LEZ: + cur_block->conditional_branch = true; + target += insn->dalvikInsn.vB; + break; + default: + LOG(FATAL) << "Unexpected opcode(" << insn->dalvikInsn.opcode << ") with kBranch set"; + } + BasicBlock *taken_block = FindBlock(target, /* split */ true, /* create */ true, + /* immed_pred_block_p */ &cur_block); + cur_block->taken = taken_block; + taken_block->predecessors->Insert(cur_block); + + /* Always terminate the current block for conditional branches */ + if (flags & Instruction::kContinue) { + BasicBlock *fallthrough_block = FindBlock(cur_offset + width, + /* + * If the method is processed + * in sequential order from the + * beginning, we don't need to + * specify split for continue + * blocks. However, this + * routine can be called by + * compileLoop, which starts + * parsing the method from an + * arbitrary address in the + * method body. + */ + true, + /* create */ + true, + /* immed_pred_block_p */ + &cur_block); + cur_block->fall_through = fallthrough_block; + fallthrough_block->predecessors->Insert(cur_block); + } else if (code_ptr < code_end) { + FindBlock(cur_offset + width, /* split */ false, /* create */ true, + /* immed_pred_block_p */ NULL); + } + return cur_block; +} + +/* Process instructions with the kSwitch flag */ +void MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width, + int flags) { + const uint16_t* switch_data = + reinterpret_cast(GetCurrentInsns() + cur_offset + insn->dalvikInsn.vB); + int size; + const int* keyTable; + const int* target_table; + int i; + int first_key; + + /* + * 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. + */ + if (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) { + DCHECK_EQ(static_cast(switch_data[0]), + static_cast(Instruction::kPackedSwitchSignature)); + size = switch_data[1]; + first_key = switch_data[2] | (switch_data[3] << 16); + target_table = reinterpret_cast(&switch_data[4]); + keyTable = NULL; // Make the compiler happy + /* + * Sparse switch data format: + * ushort ident = 0x0200 magic value + * ushort size number of entries in the table; > 0 + * int keys[size] keys, sorted low-to-high; 32-bit aligned + * int targets[size] branch targets, relative to switch opcode + * + * Total size is (2+size*4) 16-bit code units. + */ + } else { + DCHECK_EQ(static_cast(switch_data[0]), + static_cast(Instruction::kSparseSwitchSignature)); + size = switch_data[1]; + keyTable = reinterpret_cast(&switch_data[2]); + target_table = reinterpret_cast(&switch_data[2 + size*2]); + first_key = 0; // To make the compiler happy + } + + if (cur_block->successor_block_list.block_list_type != kNotUsed) { + LOG(FATAL) << "Successor block list already in use: " + << static_cast(cur_block->successor_block_list.block_list_type); + } + cur_block->successor_block_list.block_list_type = + (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ? + kPackedSwitch : kSparseSwitch; + cur_block->successor_block_list.blocks = + new (arena_) GrowableArray(arena_, size, kGrowableArraySuccessorBlocks); + + for (i = 0; i < size; i++) { + BasicBlock *case_block = FindBlock(cur_offset + target_table[i], /* split */ true, + /* create */ true, /* immed_pred_block_p */ &cur_block); + SuccessorBlockInfo *successor_block_info = + static_cast(arena_->Alloc(sizeof(SuccessorBlockInfo), + ArenaAllocator::kAllocSuccessor)); + successor_block_info->block = case_block; + successor_block_info->key = + (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ? + first_key + i : keyTable[i]; + cur_block->successor_block_list.blocks->Insert(successor_block_info); + case_block->predecessors->Insert(cur_block); + } + + /* Fall-through case */ + BasicBlock* fallthrough_block = FindBlock(cur_offset + width, /* split */ false, + /* create */ true, /* immed_pred_block_p */ NULL); + cur_block->fall_through = fallthrough_block; + fallthrough_block->predecessors->Insert(cur_block); +} + +/* Process instructions with the kThrow flag */ +BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_offset, int width, + int flags, ArenaBitVector* try_block_addr, + const uint16_t* code_ptr, const uint16_t* code_end) { + bool in_try_block = try_block_addr->IsBitSet(cur_offset); + + /* In try block */ + if (in_try_block) { + CatchHandlerIterator iterator(*current_code_item_, cur_offset); + + if (cur_block->successor_block_list.block_list_type != kNotUsed) { + LOG(INFO) << PrettyMethod(cu_->method_idx, *cu_->dex_file); + LOG(FATAL) << "Successor block list already in use: " + << static_cast(cur_block->successor_block_list.block_list_type); + } + + cur_block->successor_block_list.block_list_type = kCatch; + cur_block->successor_block_list.blocks = + new (arena_) GrowableArray(arena_, 2, kGrowableArraySuccessorBlocks); + + for (; iterator.HasNext(); iterator.Next()) { + BasicBlock *catch_block = FindBlock(iterator.GetHandlerAddress(), false /* split*/, + false /* creat */, NULL /* immed_pred_block_p */); + catch_block->catch_entry = true; + if (kIsDebugBuild) { + catches_.insert(catch_block->start_offset); + } + SuccessorBlockInfo *successor_block_info = reinterpret_cast + (arena_->Alloc(sizeof(SuccessorBlockInfo), ArenaAllocator::kAllocSuccessor)); + successor_block_info->block = catch_block; + successor_block_info->key = iterator.GetHandlerTypeIndex(); + cur_block->successor_block_list.blocks->Insert(successor_block_info); + catch_block->predecessors->Insert(cur_block); + } + } else { + BasicBlock *eh_block = NewMemBB(kExceptionHandling, num_blocks_++); + cur_block->taken = eh_block; + block_list_.Insert(eh_block); + eh_block->start_offset = cur_offset; + eh_block->predecessors->Insert(cur_block); + } + + if (insn->dalvikInsn.opcode == Instruction::THROW) { + cur_block->explicit_throw = true; + if (code_ptr < code_end) { + // Force creation of new block following THROW via side-effect + FindBlock(cur_offset + width, /* split */ false, /* create */ true, + /* immed_pred_block_p */ NULL); + } + if (!in_try_block) { + // Don't split a THROW that can't rethrow - we're done. + return cur_block; + } + } + + /* + * Split the potentially-throwing instruction into two parts. + * The first half will be a pseudo-op that captures the exception + * edges and terminates the basic block. It always falls through. + * Then, create a new basic block that begins with the throwing instruction + * (minus exceptions). Note: this new basic block must NOT be entered into + * the block_map. If the potentially-throwing instruction is the target of a + * future branch, we need to find the check psuedo half. The new + * basic block containing the work portion of the instruction should + * only be entered via fallthrough from the block containing the + * pseudo exception edge MIR. Note also that this new block is + * not automatically terminated after the work portion, and may + * contain following instructions. + */ + BasicBlock *new_block = NewMemBB(kDalvikByteCode, num_blocks_++); + block_list_.Insert(new_block); + new_block->start_offset = insn->offset; + cur_block->fall_through = new_block; + new_block->predecessors->Insert(cur_block); + MIR* new_insn = static_cast(arena_->Alloc(sizeof(MIR), ArenaAllocator::kAllocMIR)); + *new_insn = *insn; + insn->dalvikInsn.opcode = + static_cast(kMirOpCheck); + // Associate the two halves + insn->meta.throw_insn = new_insn; + new_insn->meta.throw_insn = insn; + AppendMIR(new_block, new_insn); + return new_block; +} + +/* Parse a Dex method and insert it into the MIRGraph at the current insert point. */ +void MIRGraph::InlineMethod(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) { + current_code_item_ = code_item; + method_stack_.push_back(std::make_pair(current_method_, current_offset_)); + current_method_ = m_units_.size(); + current_offset_ = 0; + // TODO: will need to snapshot stack image and use that as the mir context identification. + m_units_.push_back(new DexCompilationUnit(cu_, class_loader, Runtime::Current()->GetClassLinker(), + dex_file, current_code_item_, class_def_idx, method_idx, access_flags)); + const uint16_t* code_ptr = current_code_item_->insns_; + const uint16_t* code_end = + current_code_item_->insns_ + current_code_item_->insns_size_in_code_units_; + + // TODO: need to rework expansion of block list & try_block_addr when inlining activated. + block_list_.Resize(block_list_.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_); + try_block_addr_->ClearBit(current_code_item_->insns_size_in_code_units_); + + // If this is the first method, set up default entry and exit blocks. + if (current_method_ == 0) { + DCHECK(entry_block_ == NULL); + DCHECK(exit_block_ == NULL); + DCHECK_EQ(num_blocks_, 0); + entry_block_ = NewMemBB(kEntryBlock, num_blocks_++); + exit_block_ = NewMemBB(kExitBlock, num_blocks_++); + block_list_.Insert(entry_block_); + block_list_.Insert(exit_block_); + // TODO: deprecate all "cu->" fields; move what's left to wherever CompilationUnit is allocated. + cu_->dex_file = &dex_file; + cu_->class_def_idx = class_def_idx; + cu_->method_idx = method_idx; + cu_->access_flags = access_flags; + cu_->invoke_type = invoke_type; + cu_->shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx)); + cu_->num_ins = current_code_item_->ins_size_; + cu_->num_regs = current_code_item_->registers_size_ - cu_->num_ins; + cu_->num_outs = current_code_item_->outs_size_; + cu_->num_dalvik_registers = current_code_item_->registers_size_; + cu_->insns = current_code_item_->insns_; + cu_->code_item = current_code_item_; + } else { + UNIMPLEMENTED(FATAL) << "Nested inlining not implemented."; + /* + * Will need to manage storage for ins & outs, push prevous state and update + * insert point. + */ + } + + /* Current block to record parsed instructions */ + BasicBlock *cur_block = NewMemBB(kDalvikByteCode, num_blocks_++); + DCHECK_EQ(current_offset_, 0); + cur_block->start_offset = current_offset_; + block_list_.Insert(cur_block); + /* Add first block to the fast lookup cache */ +// FIXME: block map needs association with offset/method pair rather than just offset + block_map_.Put(cur_block->start_offset, cur_block); +// FIXME: this needs to insert at the insert point rather than entry block. + entry_block_->fall_through = cur_block; + cur_block->predecessors->Insert(entry_block_); + + /* Identify code range in try blocks and set up the empty catch blocks */ + ProcessTryCatchBlocks(); + + /* Set up for simple method detection */ + int num_patterns = sizeof(special_patterns)/sizeof(special_patterns[0]); + bool live_pattern = (num_patterns > 0) && !(cu_->disable_opt & (1 << kMatch)); + bool* dead_pattern = + static_cast(arena_->Alloc(sizeof(bool) * num_patterns, ArenaAllocator::kAllocMisc)); + int pattern_pos = 0; + + /* Parse all instructions and put them into containing basic blocks */ + while (code_ptr < code_end) { + MIR *insn = static_cast(arena_->Alloc(sizeof(MIR), ArenaAllocator::kAllocMIR)); + insn->offset = current_offset_; + insn->m_unit_index = current_method_; + int width = ParseInsn(code_ptr, &insn->dalvikInsn); + insn->width = width; + Instruction::Code opcode = insn->dalvikInsn.opcode; + if (opcode_count_ != NULL) { + opcode_count_[static_cast(opcode)]++; + } + + + /* Possible simple method? */ + if (live_pattern) { + live_pattern = false; + special_case_ = kNoHandler; + for (int i = 0; i < num_patterns; i++) { + if (!dead_pattern[i]) { + if (special_patterns[i].opcodes[pattern_pos] == opcode) { + live_pattern = true; + special_case_ = special_patterns[i].handler_code; + } else { + dead_pattern[i] = true; + } + } + } + pattern_pos++; + } + + int flags = Instruction::FlagsOf(insn->dalvikInsn.opcode); + + int df_flags = oat_data_flow_attributes_[insn->dalvikInsn.opcode]; + + if (df_flags & DF_HAS_DEFS) { + def_count_ += (df_flags & DF_A_WIDE) ? 2 : 1; + } + + // Check for inline data block signatures + if (opcode == Instruction::NOP) { + // A simple NOP will have a width of 1 at this point, embedded data NOP > 1. + if ((width == 1) && ((current_offset_ & 0x1) == 0x1) && ((code_end - code_ptr) > 1)) { + // Could be an aligning nop. If an embedded data NOP follows, treat pair as single unit. + uint16_t following_raw_instruction = code_ptr[1]; + if ((following_raw_instruction == Instruction::kSparseSwitchSignature) || + (following_raw_instruction == Instruction::kPackedSwitchSignature) || + (following_raw_instruction == Instruction::kArrayDataSignature)) { + width += Instruction::At(code_ptr + 1)->SizeInCodeUnits(); + } + } + if (width == 1) { + // It is a simple nop - treat normally. + AppendMIR(cur_block, insn); + } else { + DCHECK(cur_block->fall_through == NULL); + DCHECK(cur_block->taken == NULL); + // Unreachable instruction, mark for no continuation. + flags &= ~Instruction::kContinue; + } + } else { + AppendMIR(cur_block, insn); + } + + code_ptr += width; + + if (flags & Instruction::kBranch) { + cur_block = ProcessCanBranch(cur_block, insn, current_offset_, + width, flags, code_ptr, code_end); + } else if (flags & Instruction::kReturn) { + cur_block->terminated_by_return = true; + cur_block->fall_through = exit_block_; + exit_block_->predecessors->Insert(cur_block); + /* + * Terminate the current block if there are instructions + * afterwards. + */ + if (code_ptr < code_end) { + /* + * Create a fallthrough block for real instructions + * (incl. NOP). + */ + FindBlock(current_offset_ + width, /* split */ false, /* create */ true, + /* immed_pred_block_p */ NULL); + } + } else if (flags & Instruction::kThrow) { + cur_block = ProcessCanThrow(cur_block, insn, current_offset_, width, flags, try_block_addr_, + code_ptr, code_end); + } else if (flags & Instruction::kSwitch) { + ProcessCanSwitch(cur_block, insn, current_offset_, width, flags); + } + current_offset_ += width; + BasicBlock *next_block = FindBlock(current_offset_, /* split */ false, /* create */ + false, /* immed_pred_block_p */ NULL); + if (next_block) { + /* + * The next instruction could be the target of a previously parsed + * forward branch so a block is already created. If the current + * instruction is not an unconditional branch, connect them through + * the fall-through link. + */ + DCHECK(cur_block->fall_through == NULL || + cur_block->fall_through == next_block || + cur_block->fall_through == exit_block_); + + if ((cur_block->fall_through == NULL) && (flags & Instruction::kContinue)) { + cur_block->fall_through = next_block; + next_block->predecessors->Insert(cur_block); + } + cur_block = next_block; + } + } + if (cu_->enable_debug & (1 << kDebugDumpCFG)) { + DumpCFG("/sdcard/1_post_parse_cfg/", true); + } + + if (cu_->verbose) { + DumpMIRGraph(); + } +} + +void MIRGraph::ShowOpcodeStats() { + DCHECK(opcode_count_ != NULL); + LOG(INFO) << "Opcode Count"; + for (int i = 0; i < kNumPackedOpcodes; i++) { + if (opcode_count_[i] != 0) { + LOG(INFO) << "-C- " << Instruction::Name(static_cast(i)) + << " " << opcode_count_[i]; + } + } +} + +// 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) { + FILE* file; + std::string fname(PrettyMethod(cu_->method_idx, *cu_->dex_file)); + ReplaceSpecialChars(fname); + fname = StringPrintf("%s%s%x.dot", dir_prefix, fname.c_str(), + GetEntryBlock()->fall_through->start_offset); + file = fopen(fname.c_str(), "w"); + if (file == NULL) { + return; + } + fprintf(file, "digraph G {\n"); + + fprintf(file, " rankdir=TB\n"); + + int num_blocks = all_blocks ? GetNumBlocks() : num_reachable_blocks_; + int idx; + + for (idx = 0; idx < num_blocks; idx++) { + int block_idx = all_blocks ? idx : dfs_order_->Get(idx); + BasicBlock *bb = GetBasicBlock(block_idx); + if (bb == NULL) break; + if (bb->block_type == kDead) continue; + if (bb->block_type == kEntryBlock) { + fprintf(file, " entry_%d [shape=Mdiamond];\n", bb->id); + } else if (bb->block_type == kExitBlock) { + fprintf(file, " exit_%d [shape=Mdiamond];\n", bb->id); + } else if (bb->block_type == kDalvikByteCode) { + fprintf(file, " block%04x_%d [shape=record,label = \"{ \\\n", + bb->start_offset, bb->id); + const MIR *mir; + fprintf(file, " {block id %d\\l}%s\\\n", bb->id, + bb->first_mir_insn ? " | " : " "); + for (mir = bb->first_mir_insn; mir; mir = mir->next) { + int opcode = mir->dalvikInsn.opcode; + fprintf(file, " {%04x %s %s %s\\l}%s\\\n", mir->offset, + mir->ssa_rep ? GetDalvikDisassembly(mir) : + (opcode < kMirOpFirst) ? Instruction::Name(mir->dalvikInsn.opcode) : + extended_mir_op_names_[opcode - kMirOpFirst], + (mir->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0 ? " no_rangecheck" : " ", + (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0 ? " no_nullcheck" : " ", + mir->next ? " | " : " "); + } + fprintf(file, " }\"];\n\n"); + } else if (bb->block_type == kExceptionHandling) { + char block_name[BLOCK_NAME_LEN]; + + GetBlockName(bb, block_name); + fprintf(file, " %s [shape=invhouse];\n", block_name); + } + + char block_name1[BLOCK_NAME_LEN], block_name2[BLOCK_NAME_LEN]; + + if (bb->taken) { + GetBlockName(bb, block_name1); + GetBlockName(bb->taken, block_name2); + fprintf(file, " %s:s -> %s:n [style=dotted]\n", + block_name1, block_name2); + } + if (bb->fall_through) { + GetBlockName(bb, block_name1); + GetBlockName(bb->fall_through, block_name2); + fprintf(file, " %s:s -> %s:n\n", block_name1, block_name2); + } + + if (bb->successor_block_list.block_list_type != kNotUsed) { + fprintf(file, " succ%04x_%d [shape=%s,label = \"{ \\\n", + bb->start_offset, bb->id, + (bb->successor_block_list.block_list_type == kCatch) ? + "Mrecord" : "record"); + GrowableArray::Iterator iterator(bb->successor_block_list.blocks); + SuccessorBlockInfo *successor_block_info = iterator.Next(); + + int succ_id = 0; + while (true) { + if (successor_block_info == NULL) break; + + BasicBlock *dest_block = successor_block_info->block; + SuccessorBlockInfo *next_successor_block_info = iterator.Next(); + + fprintf(file, " { %04x: %04x\\l}%s\\\n", + succ_id++, + successor_block_info->key, + dest_block->start_offset, + (next_successor_block_info != NULL) ? " | " : " "); + + successor_block_info = next_successor_block_info; + } + fprintf(file, " }\"];\n\n"); + + GetBlockName(bb, block_name1); + fprintf(file, " %s:s -> succ%04x_%d:n [style=dashed]\n", + block_name1, bb->start_offset, bb->id); + + if (bb->successor_block_list.block_list_type == kPackedSwitch || + bb->successor_block_list.block_list_type == kSparseSwitch) { + GrowableArray::Iterator iter(bb->successor_block_list.blocks); + + succ_id = 0; + while (true) { + SuccessorBlockInfo *successor_block_info = iter.Next(); + if (successor_block_info == NULL) break; + + BasicBlock *dest_block = successor_block_info->block; + + GetBlockName(dest_block, block_name2); + fprintf(file, " succ%04x_%d:f%d:e -> %s:n\n", bb->start_offset, + bb->id, succ_id++, block_name2); + } + } + } + fprintf(file, "\n"); + + if (cu_->verbose) { + /* Display the dominator tree */ + GetBlockName(bb, block_name1); + fprintf(file, " cfg%s [label=\"%s\", shape=none];\n", + block_name1, block_name1); + if (bb->i_dom) { + GetBlockName(bb->i_dom, block_name2); + fprintf(file, " cfg%s:s -> cfg%s:n\n\n", block_name2, block_name1); + } + } + } + fprintf(file, "}\n"); + fclose(file); +} + +/* Insert an MIR instruction to the end of a basic block */ +void MIRGraph::AppendMIR(BasicBlock* bb, MIR* mir) { + if (bb->first_mir_insn == NULL) { + DCHECK(bb->last_mir_insn == NULL); + bb->last_mir_insn = bb->first_mir_insn = mir; + mir->prev = mir->next = NULL; + } else { + bb->last_mir_insn->next = mir; + mir->prev = bb->last_mir_insn; + mir->next = NULL; + bb->last_mir_insn = mir; + } +} + +/* Insert an MIR instruction to the head of a basic block */ +void MIRGraph::PrependMIR(BasicBlock* bb, MIR* mir) { + if (bb->first_mir_insn == NULL) { + DCHECK(bb->last_mir_insn == NULL); + bb->last_mir_insn = bb->first_mir_insn = mir; + mir->prev = mir->next = NULL; + } else { + bb->first_mir_insn->prev = mir; + mir->next = bb->first_mir_insn; + mir->prev = NULL; + bb->first_mir_insn = mir; + } +} + +/* Insert a MIR instruction after the specified MIR */ +void MIRGraph::InsertMIRAfter(BasicBlock* bb, MIR* current_mir, MIR* new_mir) { + new_mir->prev = current_mir; + new_mir->next = current_mir->next; + current_mir->next = new_mir; + + if (new_mir->next) { + /* Is not the last MIR in the block */ + new_mir->next->prev = new_mir; + } else { + /* Is the last MIR in the block */ + bb->last_mir_insn = new_mir; + } +} + +char* MIRGraph::GetDalvikDisassembly(const MIR* mir) { + DecodedInstruction insn = mir->dalvikInsn; + std::string str; + int flags = 0; + int opcode = insn.opcode; + char* ret; + bool nop = false; + SSARepresentation* ssa_rep = mir->ssa_rep; + Instruction::Format dalvik_format = Instruction::k10x; // Default to no-operand format + int defs = (ssa_rep != NULL) ? ssa_rep->num_defs : 0; + int uses = (ssa_rep != NULL) ? ssa_rep->num_uses : 0; + + // Handle special cases. + if ((opcode == kMirOpCheck) || (opcode == kMirOpCheckPart2)) { + str.append(extended_mir_op_names_[opcode - kMirOpFirst]); + str.append(": "); + // Recover the original Dex instruction + insn = mir->meta.throw_insn->dalvikInsn; + ssa_rep = mir->meta.throw_insn->ssa_rep; + defs = ssa_rep->num_defs; + uses = ssa_rep->num_uses; + opcode = insn.opcode; + } else if (opcode == kMirOpNop) { + str.append("["); + insn.opcode = mir->meta.original_opcode; + opcode = mir->meta.original_opcode; + nop = true; + } + + if (opcode >= kMirOpFirst) { + str.append(extended_mir_op_names_[opcode - kMirOpFirst]); + } else { + dalvik_format = Instruction::FormatOf(insn.opcode); + flags = Instruction::FlagsOf(insn.opcode); + str.append(Instruction::Name(insn.opcode)); + } + + if (opcode == kMirOpPhi) { + int* incoming = reinterpret_cast(insn.vB); + str.append(StringPrintf(" %s = (%s", + GetSSANameWithConst(ssa_rep->defs[0], true).c_str(), + GetSSANameWithConst(ssa_rep->uses[0], true).c_str())); + str.append(StringPrintf(":%d", incoming[0])); + int i; + for (i = 1; i < uses; i++) { + str.append(StringPrintf(", %s:%d", + GetSSANameWithConst(ssa_rep->uses[i], true).c_str(), + incoming[i])); + } + str.append(")"); + } else if ((flags & Instruction::kBranch) != 0) { + // For branches, decode the instructions to print out the branch targets. + int offset = 0; + switch (dalvik_format) { + case Instruction::k21t: + str.append(StringPrintf(" %s,", GetSSANameWithConst(ssa_rep->uses[0], false).c_str())); + offset = insn.vB; + break; + case Instruction::k22t: + str.append(StringPrintf(" %s, %s,", GetSSANameWithConst(ssa_rep->uses[0], false).c_str(), + GetSSANameWithConst(ssa_rep->uses[1], false).c_str())); + offset = insn.vC; + break; + case Instruction::k10t: + case Instruction::k20t: + case Instruction::k30t: + offset = insn.vA; + break; + default: + LOG(FATAL) << "Unexpected branch format " << dalvik_format << " from " << insn.opcode; + } + str.append(StringPrintf(" 0x%x (%c%x)", mir->offset + offset, + offset > 0 ? '+' : '-', offset > 0 ? offset : -offset)); + } else { + // For invokes-style formats, treat wide regs as a pair of singles + bool show_singles = ((dalvik_format == Instruction::k35c) || + (dalvik_format == Instruction::k3rc)); + if (defs != 0) { + str.append(StringPrintf(" %s", GetSSANameWithConst(ssa_rep->defs[0], false).c_str())); + if (uses != 0) { + str.append(", "); + } + } + for (int i = 0; i < uses; i++) { + str.append( + StringPrintf(" %s", GetSSANameWithConst(ssa_rep->uses[i], show_singles).c_str())); + if (!show_singles && (reg_location_ != NULL) && reg_location_[i].wide) { + // For the listing, skip the high sreg. + i++; + } + if (i != (uses -1)) { + str.append(","); + } + } + switch (dalvik_format) { + case Instruction::k11n: // Add one immediate from vB + case Instruction::k21s: + case Instruction::k31i: + case Instruction::k21h: + str.append(StringPrintf(", #%d", insn.vB)); + break; + case Instruction::k51l: // Add one wide immediate + str.append(StringPrintf(", #%lld", insn.vB_wide)); + break; + case Instruction::k21c: // One register, one string/type/method index + case Instruction::k31c: + str.append(StringPrintf(", index #%d", insn.vB)); + break; + case Instruction::k22c: // Two registers, one string/type/method index + str.append(StringPrintf(", index #%d", insn.vC)); + break; + case Instruction::k22s: // Add one immediate from vC + case Instruction::k22b: + str.append(StringPrintf(", #%d", insn.vC)); + break; + default: { + // Nothing left to print + } + } + } + if (nop) { + str.append("]--optimized away"); + } + int length = str.length() + 1; + ret = static_cast(arena_->Alloc(length, ArenaAllocator::kAllocDFInfo)); + strncpy(ret, str.c_str(), length); + return ret; +} + +/* Turn method name into a legal Linux file name */ +void MIRGraph::ReplaceSpecialChars(std::string& str) { + static const struct { const char before; const char after; } match[] = { + {'/', '-'}, {';', '#'}, {' ', '#'}, {'$', '+'}, + {'(', '@'}, {')', '@'}, {'<', '='}, {'>', '='} + }; + for (unsigned int i = 0; i < sizeof(match)/sizeof(match[0]); i++) { + std::replace(str.begin(), str.end(), match[i].before, match[i].after); + } +} + +std::string MIRGraph::GetSSAName(int ssa_reg) { + // TODO: This value is needed for LLVM and debugging. Currently, we compute this and then copy to + // the arena. We should be smarter and just place straight into the arena, or compute the + // value more lazily. + return StringPrintf("v%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg)); +} + +// Similar to GetSSAName, but if ssa name represents an immediate show that as well. +std::string MIRGraph::GetSSANameWithConst(int ssa_reg, bool singles_only) { + if (reg_location_ == NULL) { + // Pre-SSA - just use the standard name + return GetSSAName(ssa_reg); + } + if (IsConst(reg_location_[ssa_reg])) { + if (!singles_only && reg_location_[ssa_reg].wide) { + return StringPrintf("v%d_%d#0x%llx", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg), + ConstantValueWide(reg_location_[ssa_reg])); + } else { + return StringPrintf("v%d_%d#0x%x", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg), + ConstantValue(reg_location_[ssa_reg])); + } + } else { + return StringPrintf("v%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg)); + } +} + +void MIRGraph::GetBlockName(BasicBlock* bb, char* name) { + switch (bb->block_type) { + case kEntryBlock: + snprintf(name, BLOCK_NAME_LEN, "entry_%d", bb->id); + break; + case kExitBlock: + snprintf(name, BLOCK_NAME_LEN, "exit_%d", bb->id); + break; + case kDalvikByteCode: + snprintf(name, BLOCK_NAME_LEN, "block%04x_%d", bb->start_offset, bb->id); + break; + case kExceptionHandling: + snprintf(name, BLOCK_NAME_LEN, "exception%04x_%d", bb->start_offset, + bb->id); + break; + default: + snprintf(name, BLOCK_NAME_LEN, "_%d", bb->id); + break; + } +} + +const char* MIRGraph::GetShortyFromTargetIdx(int target_idx) { + // FIXME: use current code unit for inline support. + const DexFile::MethodId& method_id = cu_->dex_file->GetMethodId(target_idx); + return cu_->dex_file->GetShorty(method_id.proto_idx_); +} + +/* Debug Utility - dump a compilation unit */ +void MIRGraph::DumpMIRGraph() { + BasicBlock* bb; + const char* block_type_names[] = { + "Entry Block", + "Code Block", + "Exit Block", + "Exception Handling", + "Catch Block" + }; + + LOG(INFO) << "Compiling " << PrettyMethod(cu_->method_idx, *cu_->dex_file); + LOG(INFO) << cu_->insns << " insns"; + LOG(INFO) << GetNumBlocks() << " blocks in total"; + GrowableArray::Iterator iterator(&block_list_); + + while (true) { + bb = iterator.Next(); + if (bb == NULL) break; + LOG(INFO) << StringPrintf("Block %d (%s) (insn %04x - %04x%s)", + bb->id, + block_type_names[bb->block_type], + bb->start_offset, + bb->last_mir_insn ? bb->last_mir_insn->offset : bb->start_offset, + bb->last_mir_insn ? "" : " empty"); + if (bb->taken) { + LOG(INFO) << " Taken branch: block " << bb->taken->id + << "(0x" << std::hex << bb->taken->start_offset << ")"; + } + if (bb->fall_through) { + LOG(INFO) << " Fallthrough : block " << bb->fall_through->id + << " (0x" << std::hex << bb->fall_through->start_offset << ")"; + } + } +} + +/* + * Build an array of location records for the incoming arguments. + * Note: one location record per word of arguments, with dummy + * high-word loc for wide arguments. Also pull up any following + * MOVE_RESULT and incorporate it into the invoke. + */ +CallInfo* MIRGraph::NewMemCallInfo(BasicBlock* bb, MIR* mir, InvokeType type, + bool is_range) { + CallInfo* info = static_cast(arena_->Alloc(sizeof(CallInfo), + ArenaAllocator::kAllocMisc)); + MIR* move_result_mir = FindMoveResult(bb, mir); + if (move_result_mir == NULL) { + info->result.location = kLocInvalid; + } else { + info->result = GetRawDest(move_result_mir); + move_result_mir->meta.original_opcode = move_result_mir->dalvikInsn.opcode; + move_result_mir->dalvikInsn.opcode = static_cast(kMirOpNop); + } + info->num_arg_words = mir->ssa_rep->num_uses; + info->args = (info->num_arg_words == 0) ? NULL : static_cast + (arena_->Alloc(sizeof(RegLocation) * info->num_arg_words, ArenaAllocator::kAllocMisc)); + for (int i = 0; i < info->num_arg_words; i++) { + info->args[i] = GetRawSrc(mir, i); + } + info->opt_flags = mir->optimization_flags; + info->type = type; + info->is_range = is_range; + info->index = mir->dalvikInsn.vB; + info->offset = mir->offset; + return info; +} + +// Allocate a new basic block. +BasicBlock* MIRGraph::NewMemBB(BBType block_type, int block_id) { + BasicBlock* bb = static_cast(arena_->Alloc(sizeof(BasicBlock), + ArenaAllocator::kAllocBB)); + bb->block_type = block_type; + bb->id = block_id; + // TUNING: better estimate of the exit block predecessors? + bb->predecessors = new (arena_) GrowableArray(arena_, + (block_type == kExitBlock) ? 2048 : 2, + kGrowableArrayPredecessors); + bb->successor_block_list.block_list_type = kNotUsed; + block_id_map_.Put(block_id, block_id); + return bb; +} + +} // namespace art diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h new file mode 100644 index 0000000000000000000000000000000000000000..6f8bd85630f33497e7dda0820bb73858c5a69bcc --- /dev/null +++ b/compiler/dex/mir_graph.h @@ -0,0 +1,727 @@ +/* + * 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_MIR_GRAPH_H_ +#define ART_COMPILER_DEX_MIR_GRAPH_H_ + +#include "dex_file.h" +#include "dex_instruction.h" +#include "compiler_ir.h" +#include "arena_bit_vector.h" +#include "growable_array.h" + +namespace art { + +enum InstructionAnalysisAttributePos { + kUninterestingOp = 0, + kArithmeticOp, + kFPOp, + kSingleOp, + kDoubleOp, + kIntOp, + kLongOp, + kBranchOp, + kInvokeOp, + kArrayOp, + kHeavyweightOp, + kSimpleConstOp, + kMoveOp, + kSwitch +}; + +#define AN_NONE (1 << kUninterestingOp) +#define AN_MATH (1 << kArithmeticOp) +#define AN_FP (1 << kFPOp) +#define AN_LONG (1 << kLongOp) +#define AN_INT (1 << kIntOp) +#define AN_SINGLE (1 << kSingleOp) +#define AN_DOUBLE (1 << kDoubleOp) +#define AN_FLOATMATH (1 << kFPOp) +#define AN_BRANCH (1 << kBranchOp) +#define AN_INVOKE (1 << kInvokeOp) +#define AN_ARRAYOP (1 << kArrayOp) +#define AN_HEAVYWEIGHT (1 << kHeavyweightOp) +#define AN_SIMPLECONST (1 << kSimpleConstOp) +#define AN_MOVE (1 << kMoveOp) +#define AN_SWITCH (1 << kSwitch) +#define AN_COMPUTATIONAL (AN_MATH | AN_ARRAYOP | AN_MOVE | AN_SIMPLECONST) + +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*. +}; + +#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 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_REG (0xFF) +#define INVALID_OFFSET (0xDEADF00FU) + +/* SSA encodings for special registers */ +#define SSA_METHOD_BASEREG (-2) +/* First compiler temp basereg, grows smaller */ +#define SSA_CTEMP_BASEREG (SSA_METHOD_BASEREG - 1) + +#define MIR_IGNORE_NULL_CHECK (1 << kMIRIgnoreNullCheck) +#define MIR_NULL_CHECK_ONLY (1 << kMIRNullCheckOnly) +#define MIR_IGNORE_RANGE_CHECK (1 << kMIRIgnoreRangeCheck) +#define MIR_RANGE_CHECK_ONLY (1 << kMIRRangeCheckOnly) +#define MIR_INLINED (1 << kMIRInlined) +#define MIR_INLINED_PRED (1 << kMIRInlinedPred) +#define MIR_CALLEE (1 << kMIRCallee) +#define MIR_IGNORE_SUSPEND_CHECK (1 << kMIRIgnoreSuspendCheck) +#define MIR_DUP (1 << kMIRDup) + +#define BLOCK_NAME_LEN 80 + +/* + * In general, vreg/sreg describe Dalvik registers that originated with dx. However, + * it is useful to have compiler-generated temporary registers and have them treated + * in the same manner as dx-generated virtual registers. This struct records the SSA + * name of compiler-introduced temporaries. + */ +struct CompilerTemp { + int s_reg; +}; + +// When debug option enabled, records effectiveness of null and range check elimination. +struct Checkstats { + int null_checks; + int null_checks_eliminated; + int range_checks; + int range_checks_eliminated; +}; + +// Dataflow attributes of a basic block. +struct BasicBlockDataFlow { + ArenaBitVector* use_v; + ArenaBitVector* def_v; + ArenaBitVector* live_in_v; + ArenaBitVector* phi_v; + int* vreg_to_ssa_map; + ArenaBitVector* ending_null_check_v; +}; + +/* + * Normalized use/def for a MIR operation using SSA names rather than vregs. Note that + * uses/defs retain the Dalvik convention that long operations operate on a pair of 32-bit + * vregs. For example, "ADD_LONG v0, v2, v3" would have 2 defs (v0/v1) and 4 uses (v2/v3, v4/v5). + * Following SSA renaming, this is the primary struct used by code generators to locate + * operand and result registers. This is a somewhat confusing and unhelpful convention that + * we may want to revisit in the future. + */ +struct SSARepresentation { + int num_uses; + int* uses; + bool* fp_use; + int num_defs; + int* defs; + bool* fp_def; +}; + +/* + * The Midlevel Intermediate Representation node, which may be largely considered a + * wrapper around a Dalvik byte code. + */ +struct MIR { + DecodedInstruction dalvikInsn; + uint32_t width; // NOTE: only need 16 bits for width. + unsigned int offset; + int m_unit_index; // From which method was this MIR included + MIR* prev; + MIR* next; + SSARepresentation* ssa_rep; + int optimization_flags; + union { + // Establish link between two halves of throwing instructions. + MIR* throw_insn; + // Saved opcode for NOP'd MIRs + Instruction::Code original_opcode; + } meta; +}; + +struct SuccessorBlockInfo; + +struct BasicBlock { + int id; + int dfs_id; + bool visited; + bool hidden; + bool catch_entry; + bool explicit_throw; + bool conditional_branch; + bool terminated_by_return; // Block ends with a Dalvik return opcode. + bool dominates_return; // Is a member of return extended basic block. + uint16_t start_offset; + uint16_t nesting_depth; + BBType block_type; + MIR* first_mir_insn; + MIR* last_mir_insn; + BasicBlock* fall_through; + BasicBlock* taken; + BasicBlock* i_dom; // Immediate dominator. + BasicBlockDataFlow* data_flow_info; + GrowableArray* predecessors; + ArenaBitVector* dominators; + ArenaBitVector* i_dominated; // Set nodes being immediately dominated. + ArenaBitVector* dom_frontier; // Dominance frontier. + struct { // For one-to-many successors like. + BlockListType block_list_type; // switch and exception handling. + GrowableArray* blocks; + } successor_block_list; +}; + +/* + * The "blocks" field in "successor_block_list" points to an array of elements with the type + * "SuccessorBlockInfo". For catch blocks, key is type index for the exception. For swtich + * blocks, key is the case value. + */ +// TODO: make class with placement new. +struct SuccessorBlockInfo { + BasicBlock* block; + int key; +}; + +/* + * Whereas a SSA name describes a definition of a Dalvik vreg, the RegLocation describes + * the type of an SSA name (and, can also be used by code generators to record where the + * value is located (i.e. - physical register, frame, spill, etc.). For each SSA name (SReg) + * there is a RegLocation. + * FIXME: The orig_sreg field was added as a workaround for llvm bitcode generation. With + * the latest restructuring, we should be able to remove it and rely on s_reg_low throughout. + */ +struct RegLocation { + RegLocationType location:3; + unsigned wide:1; + unsigned defined:1; // Do we know the type? + unsigned is_const:1; // Constant, value in mir_graph->constant_values[]. + unsigned fp:1; // Floating point? + unsigned core:1; // Non-floating point? + unsigned ref:1; // Something GC cares about. + unsigned high_word:1; // High word of pair? + unsigned home:1; // Does this represent the home location? + uint8_t low_reg; // First physical register. + uint8_t high_reg; // 2nd physical register (if wide). + int32_t s_reg_low; // SSA name for low Dalvik word. + int32_t orig_sreg; // TODO: remove after Bitcode gen complete + // and consolodate usage w/ s_reg_low. +}; + +/* + * Collection of information describing an invoke, and the destination of + * the subsequent MOVE_RESULT (if applicable). Collected as a unit to enable + * more efficient invoke code generation. + */ +struct CallInfo { + int num_arg_words; // Note: word count, not arg count. + RegLocation* args; // One for each word of arguments. + RegLocation result; // Eventual target of MOVE_RESULT. + int opt_flags; + InvokeType type; + uint32_t dex_idx; + uint32_t index; // Method idx for invokes, type idx for FilledNewArray. + uintptr_t direct_code; + uintptr_t direct_method; + RegLocation target; // Target of following move_result. + bool skip_this; + bool is_range; + int offset; // Dalvik offset. +}; + + +const RegLocation bad_loc = {kLocDalvikFrame, 0, 0, 0, 0, 0, 0, 0, 0, + INVALID_REG, INVALID_REG, INVALID_SREG, INVALID_SREG}; + +class MIRGraph { + public: + MIRGraph(CompilationUnit* cu, ArenaAllocator* arena); + ~MIRGraph(); + + /* + * Examine the graph to determine whether it's worthwile to spend the time compiling + * this method. + */ + bool SkipCompilation(Runtime::CompilerFilter compiler_filter); + + /* + * Parse dex method and add MIR at current insert point. Returns id (which is + * actually the index of the method in the m_units_ array). + */ + void InlineMethod(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); + + /* Find existing block */ + BasicBlock* FindBlock(unsigned int code_offset) { + return FindBlock(code_offset, false, false, NULL); + } + + const uint16_t* GetCurrentInsns() const { + return current_code_item_->insns_; + } + + const uint16_t* GetInsns(int m_unit_index) const { + return m_units_[m_unit_index]->GetCodeItem()->insns_; + } + + int GetNumBlocks() const { + return num_blocks_; + } + + size_t GetNumDalvikInsns() const { + return cu_->code_item->insns_size_in_code_units_; + } + + ArenaBitVector* GetTryBlockAddr() const { + return try_block_addr_; + } + + BasicBlock* GetEntryBlock() const { + return entry_block_; + } + + BasicBlock* GetExitBlock() const { + return exit_block_; + } + + BasicBlock* GetBasicBlock(int block_id) const { + return block_list_.Get(block_id); + } + + size_t GetBasicBlockListCount() const { + return block_list_.Size(); + } + + GrowableArray* GetBlockList() { + return &block_list_; + } + + GrowableArray* GetDfsOrder() { + return dfs_order_; + } + + GrowableArray* GetDfsPostOrder() { + return dfs_post_order_; + } + + GrowableArray* GetDomPostOrder() { + return dom_post_order_traversal_; + } + + int GetDefCount() const { + return def_count_; + } + + ArenaAllocator* GetArena() { + return arena_; + } + + void EnableOpcodeCounting() { + opcode_count_ = static_cast(arena_->Alloc(kNumPackedOpcodes * sizeof(int), + ArenaAllocator::kAllocMisc)); + } + + void ShowOpcodeStats(); + + DexCompilationUnit* GetCurrentDexCompilationUnit() const { + return m_units_[current_method_]; + } + + void DumpCFG(const char* dir_prefix, bool all_blocks); + + void BuildRegLocations(); + + void DumpRegLocTable(RegLocation* table, int count); + + void BasicBlockOptimization(); + + bool IsConst(int32_t s_reg) const { + return is_constant_v_->IsBitSet(s_reg); + } + + bool IsConst(RegLocation loc) const { + return (IsConst(loc.orig_sreg)); + } + + int32_t ConstantValue(RegLocation loc) const { + DCHECK(IsConst(loc)); + return constant_values_[loc.orig_sreg]; + } + + int32_t ConstantValue(int32_t s_reg) const { + DCHECK(IsConst(s_reg)); + return constant_values_[s_reg]; + } + + int64_t ConstantValueWide(RegLocation loc) const { + DCHECK(IsConst(loc)); + return (static_cast(constant_values_[loc.orig_sreg + 1]) << 32) | + Low32Bits(static_cast(constant_values_[loc.orig_sreg])); + } + + bool IsConstantNullRef(RegLocation loc) const { + return loc.ref && loc.is_const && (ConstantValue(loc) == 0); + } + + int GetNumSSARegs() const { + return num_ssa_regs_; + } + + void SetNumSSARegs(int new_num) { + num_ssa_regs_ = new_num; + } + + unsigned int GetNumReachableBlocks() const { + return num_reachable_blocks_; + } + + int GetUseCount(int vreg) const { + return use_counts_.Get(vreg); + } + + int GetRawUseCount(int vreg) const { + return raw_use_counts_.Get(vreg); + } + + int GetSSASubscript(int ssa_reg) const { + return ssa_subscripts_->Get(ssa_reg); + } + + RegLocation GetRawSrc(MIR* mir, int num) { + DCHECK(num < mir->ssa_rep->num_uses); + RegLocation res = reg_location_[mir->ssa_rep->uses[num]]; + return res; + } + + RegLocation GetRawDest(MIR* mir) { + DCHECK_GT(mir->ssa_rep->num_defs, 0); + RegLocation res = reg_location_[mir->ssa_rep->defs[0]]; + return res; + } + + RegLocation GetDest(MIR* mir) { + RegLocation res = GetRawDest(mir); + DCHECK(!res.wide); + return res; + } + + RegLocation GetSrc(MIR* mir, int num) { + RegLocation res = GetRawSrc(mir, num); + DCHECK(!res.wide); + return res; + } + + RegLocation GetDestWide(MIR* mir) { + RegLocation res = GetRawDest(mir); + DCHECK(res.wide); + return res; + } + + RegLocation GetSrcWide(MIR* mir, int low) { + RegLocation res = GetRawSrc(mir, low); + DCHECK(res.wide); + return res; + } + + RegLocation GetBadLoc() { + return bad_loc; + } + + int GetMethodSReg() { + return method_sreg_; + } + + bool MethodIsLeaf() { + return attributes_ & METHOD_IS_LEAF; + } + + RegLocation GetRegLocation(int index) { + DCHECK((index >= 0) && (index > num_ssa_regs_)); + return reg_location_[index]; + } + + RegLocation GetMethodLoc() { + return reg_location_[method_sreg_]; + } + + bool IsSpecialCase() { + return special_case_ != kNoHandler; + } + + SpecialCaseHandler GetSpecialCase() { + return special_case_; + } + + bool IsBackedge(BasicBlock* branch_bb, BasicBlock* target_bb) { + return ((target_bb != NULL) && (target_bb->start_offset <= branch_bb->start_offset)); + } + + bool IsBackwardsBranch(BasicBlock* branch_bb) { + return IsBackedge(branch_bb, branch_bb->taken) || IsBackedge(branch_bb, branch_bb->fall_through); + } + + void BasicBlockCombine(); + void CodeLayout(); + void DumpCheckStats(); + void PropagateConstants(); + MIR* FindMoveResult(BasicBlock* bb, MIR* mir); + int SRegToVReg(int ssa_reg) const; + void VerifyDataflow(); + void MethodUseCount(); + void SSATransformation(); + void CheckForDominanceFrontier(BasicBlock* dom_bb, const BasicBlock* succ_bb); + void NullCheckElimination(); + bool SetFp(int index, bool is_fp); + bool SetCore(int index, bool is_core); + bool SetRef(int index, bool is_ref); + bool SetWide(int index, bool is_wide); + bool SetHigh(int index, bool is_high); + void AppendMIR(BasicBlock* bb, MIR* mir); + void PrependMIR(BasicBlock* bb, MIR* mir); + void InsertMIRAfter(BasicBlock* bb, MIR* current_mir, MIR* new_mir); + char* GetDalvikDisassembly(const MIR* mir); + void ReplaceSpecialChars(std::string& str); + std::string GetSSAName(int ssa_reg); + std::string GetSSANameWithConst(int ssa_reg, bool singles_only); + void GetBlockName(BasicBlock* bb, char* name); + const char* GetShortyFromTargetIdx(int); + void DumpMIRGraph(); + CallInfo* NewMemCallInfo(BasicBlock* bb, MIR* mir, InvokeType type, bool is_range); + BasicBlock* NewMemBB(BBType block_type, int block_id); + + /* + * 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. + */ + std::set catches_; + + // TODO: make these private. + RegLocation* reg_location_; // Map SSA names to location. + GrowableArray compiler_temps_; + SafeMap block_id_map_; // Block collapse lookup cache. + + static const int oat_data_flow_attributes_[kMirOpLast]; + static const char* extended_mir_op_names_[kMirOpLast - kMirOpFirst]; + static const uint32_t analysis_attributes_[kMirOpLast]; + + private: + int FindCommonParent(int block1, int block2); + void ComputeSuccLineIn(ArenaBitVector* dest, const ArenaBitVector* src1, + const ArenaBitVector* src2); + void HandleLiveInUse(ArenaBitVector* use_v, ArenaBitVector* def_v, + ArenaBitVector* live_in_v, int dalvik_reg_id); + void HandleDef(ArenaBitVector* def_v, int dalvik_reg_id); + void CompilerInitializeSSAConversion(); + bool DoSSAConversion(BasicBlock* bb); + bool InvokeUsesMethodStar(MIR* mir); + int ParseInsn(const uint16_t* code_ptr, DecodedInstruction* decoded_instruction); + bool ContentIsInsn(const uint16_t* code_ptr); + BasicBlock* SplitBlock(unsigned int code_offset, BasicBlock* orig_block, + BasicBlock** immed_pred_block_p); + BasicBlock* FindBlock(unsigned int code_offset, bool split, bool create, + BasicBlock** immed_pred_block_p); + void ProcessTryCatchBlocks(); + BasicBlock* ProcessCanBranch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width, + int flags, const uint16_t* code_ptr, const uint16_t* code_end); + void ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width, int flags); + BasicBlock* ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_offset, int width, + int flags, ArenaBitVector* try_block_addr, const uint16_t* code_ptr, + const uint16_t* code_end); + int AddNewSReg(int v_reg); + void HandleSSAUse(int* uses, int dalvik_reg, int reg_index); + void HandleSSADef(int* defs, int dalvik_reg, int reg_index); + void DataFlowSSAFormat35C(MIR* mir); + void DataFlowSSAFormat3RC(MIR* mir); + bool FindLocalLiveIn(BasicBlock* bb); + void ClearAllVisitedFlags(); + bool CountUses(struct BasicBlock* bb); + bool InferTypeAndSize(BasicBlock* bb); + bool VerifyPredInfo(BasicBlock* bb); + BasicBlock* NeedsVisit(BasicBlock* bb); + BasicBlock* NextUnvisitedSuccessor(BasicBlock* bb); + void MarkPreOrder(BasicBlock* bb); + void RecordDFSOrders(BasicBlock* bb); + void ComputeDFSOrders(); + void ComputeDefBlockMatrix(); + void ComputeDomPostOrderTraversal(BasicBlock* bb); + void ComputeDominators(); + void InsertPhiNodes(); + void DoDFSPreOrderSSARename(BasicBlock* block); + void SetConstant(int32_t ssa_reg, int value); + void SetConstantWide(int ssa_reg, int64_t value); + int GetSSAUseCount(int s_reg); + bool BasicBlockOpt(BasicBlock* bb); + bool EliminateNullChecks(BasicBlock* bb); + void NullCheckEliminationInit(BasicBlock* bb); + bool BuildExtendedBBList(struct BasicBlock* bb); + bool FillDefBlockMatrix(BasicBlock* bb); + void InitializeDominationInfo(BasicBlock* bb); + bool ComputeblockIDom(BasicBlock* bb); + bool ComputeBlockDominators(BasicBlock* bb); + bool SetDominators(BasicBlock* bb); + bool ComputeBlockLiveIns(BasicBlock* bb); + bool InsertPhiNodeOperands(BasicBlock* bb); + bool ComputeDominanceFrontier(BasicBlock* bb); + void DoConstantPropogation(BasicBlock* bb); + void CountChecks(BasicBlock* bb); + bool CombineBlocks(BasicBlock* bb); + void AnalyzeBlock(BasicBlock* bb, struct MethodStats* stats); + bool ComputeSkipCompilation(struct MethodStats* stats, bool skip_default); + + CompilationUnit* const cu_; + GrowableArray* ssa_base_vregs_; + GrowableArray* ssa_subscripts_; + // Map original Dalvik virtual reg i to the current SSA name. + int* vreg_to_ssa_map_; // length == method->registers_size + int* ssa_last_defs_; // length == method->registers_size + ArenaBitVector* is_constant_v_; // length == num_ssa_reg + int* constant_values_; // length == num_ssa_reg + // Use counts of ssa names. + GrowableArray use_counts_; // Weighted by nesting depth + GrowableArray raw_use_counts_; // Not weighted + unsigned int num_reachable_blocks_; + GrowableArray* dfs_order_; + GrowableArray* dfs_post_order_; + GrowableArray* dom_post_order_traversal_; + int* i_dom_list_; + ArenaBitVector** def_block_matrix_; // num_dalvik_register x num_blocks. + ArenaBitVector* temp_block_v_; + ArenaBitVector* temp_dalvik_register_v_; + ArenaBitVector* temp_ssa_register_v_; // num_ssa_regs. + static const int kInvalidEntry = -1; + GrowableArray block_list_; + ArenaBitVector* try_block_addr_; + BasicBlock* entry_block_; + BasicBlock* exit_block_; + BasicBlock* cur_block_; + int num_blocks_; + const DexFile::CodeItem* current_code_item_; + SafeMap block_map_; // FindBlock lookup cache. + std::vector m_units_; // List of methods included in this graph + typedef std::pair MIRLocation; // Insert point, (m_unit_ index, offset) + std::vector method_stack_; // Include stack + int current_method_; + int current_offset_; + int def_count_; // Used to estimate size of ssa name storage. + int* opcode_count_; // Dex opcode coverage stats. + int num_ssa_regs_; // Number of names following SSA transformation. + std::vector extended_basic_blocks_; // Heads of block "traces". + int method_sreg_; + unsigned int attributes_; + Checkstats* checkstats_; + SpecialCaseHandler special_case_; + ArenaAllocator* arena_; +}; + +} // namespace art + +#endif // ART_COMPILER_DEX_MIR_GRAPH_H_ diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc new file mode 100644 index 0000000000000000000000000000000000000000..b7611f8f5bf74adef1c40c17bcc99f62f157474f --- /dev/null +++ b/compiler/dex/mir_optimization.cc @@ -0,0 +1,875 @@ +/* + * 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 "compiler_internals.h" +#include "local_value_numbering.h" +#include "dataflow_iterator-inl.h" + +namespace art { + +static unsigned int Predecessors(BasicBlock* bb) { + return bb->predecessors->Size(); +} + +/* Setup a constant value for opcodes thare have the DF_SETS_CONST attribute */ +void MIRGraph::SetConstant(int32_t ssa_reg, int value) { + is_constant_v_->SetBit(ssa_reg); + constant_values_[ssa_reg] = value; +} + +void MIRGraph::SetConstantWide(int ssa_reg, int64_t value) { + is_constant_v_->SetBit(ssa_reg); + constant_values_[ssa_reg] = Low32Bits(value); + constant_values_[ssa_reg + 1] = High32Bits(value); +} + +void MIRGraph::DoConstantPropogation(BasicBlock* bb) { + MIR* mir; + + for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { + int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; + + DecodedInstruction *d_insn = &mir->dalvikInsn; + + if (!(df_attributes & DF_HAS_DEFS)) continue; + + /* Handle instructions that set up constants directly */ + if (df_attributes & DF_SETS_CONST) { + if (df_attributes & DF_DA) { + int32_t vB = static_cast(d_insn->vB); + switch (d_insn->opcode) { + case Instruction::CONST_4: + case Instruction::CONST_16: + case Instruction::CONST: + SetConstant(mir->ssa_rep->defs[0], vB); + break; + case Instruction::CONST_HIGH16: + SetConstant(mir->ssa_rep->defs[0], vB << 16); + break; + case Instruction::CONST_WIDE_16: + case Instruction::CONST_WIDE_32: + SetConstantWide(mir->ssa_rep->defs[0], static_cast(vB)); + break; + case Instruction::CONST_WIDE: + SetConstantWide(mir->ssa_rep->defs[0], d_insn->vB_wide); + break; + case Instruction::CONST_WIDE_HIGH16: + SetConstantWide(mir->ssa_rep->defs[0], static_cast(vB) << 48); + break; + default: + break; + } + } + /* Handle instructions that set up constants directly */ + } else if (df_attributes & DF_IS_MOVE) { + int i; + + for (i = 0; i < mir->ssa_rep->num_uses; i++) { + if (!is_constant_v_->IsBitSet(mir->ssa_rep->uses[i])) break; + } + /* Move a register holding a constant to another register */ + if (i == mir->ssa_rep->num_uses) { + SetConstant(mir->ssa_rep->defs[0], constant_values_[mir->ssa_rep->uses[0]]); + if (df_attributes & DF_A_WIDE) { + SetConstant(mir->ssa_rep->defs[1], constant_values_[mir->ssa_rep->uses[1]]); + } + } + } + } + /* TODO: implement code to handle arithmetic operations */ +} + +void MIRGraph::PropagateConstants() { + is_constant_v_ = new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false); + constant_values_ = static_cast(arena_->Alloc(sizeof(int) * GetNumSSARegs(), + ArenaAllocator::kAllocDFInfo)); + AllNodesIterator iter(this, false /* not iterative */); + for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { + DoConstantPropogation(bb); + } +} + +/* Advance to next strictly dominated MIR node in an extended basic block */ +static MIR* AdvanceMIR(BasicBlock** p_bb, MIR* mir) { + BasicBlock* bb = *p_bb; + if (mir != NULL) { + mir = mir->next; + if (mir == NULL) { + bb = bb->fall_through; + if ((bb == NULL) || Predecessors(bb) != 1) { + mir = NULL; + } else { + *p_bb = bb; + mir = bb->first_mir_insn; + } + } + } + return mir; +} + +/* + * To be used at an invoke mir. If the logically next mir node represents + * a move-result, return it. Else, return NULL. If a move-result exists, + * it is required to immediately follow the invoke with no intervening + * opcodes or incoming arcs. However, if the result of the invoke is not + * used, a move-result may not be present. + */ +MIR* MIRGraph::FindMoveResult(BasicBlock* bb, MIR* mir) { + BasicBlock* tbb = bb; + mir = AdvanceMIR(&tbb, mir); + while (mir != NULL) { + int opcode = mir->dalvikInsn.opcode; + if ((mir->dalvikInsn.opcode == Instruction::MOVE_RESULT) || + (mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) || + (mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_WIDE)) { + break; + } + // Keep going if pseudo op, otherwise terminate + if (opcode < kNumPackedOpcodes) { + mir = NULL; + } else { + mir = AdvanceMIR(&tbb, mir); + } + } + return mir; +} + +static BasicBlock* NextDominatedBlock(BasicBlock* bb) { + if (bb->block_type == kDead) { + return NULL; + } + DCHECK((bb->block_type == kEntryBlock) || (bb->block_type == kDalvikByteCode) + || (bb->block_type == kExitBlock)); + if (((bb->taken != NULL) && (bb->fall_through == NULL)) && + ((bb->taken->block_type == kDalvikByteCode) || (bb->taken->block_type == kExitBlock))) { + // Follow simple unconditional branches. + bb = bb->taken; + } else { + // Follow simple fallthrough + bb = (bb->taken != NULL) ? NULL : bb->fall_through; + } + if (bb == NULL || (Predecessors(bb) != 1)) { + return NULL; + } + DCHECK((bb->block_type == kDalvikByteCode) || (bb->block_type == kExitBlock)); + return bb; +} + +static MIR* FindPhi(BasicBlock* bb, int ssa_name) { + for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { + if (static_cast(mir->dalvikInsn.opcode) == kMirOpPhi) { + for (int i = 0; i < mir->ssa_rep->num_uses; i++) { + if (mir->ssa_rep->uses[i] == ssa_name) { + return mir; + } + } + } + } + return NULL; +} + +static SelectInstructionKind SelectKind(MIR* mir) { + switch (mir->dalvikInsn.opcode) { + case Instruction::MOVE: + case Instruction::MOVE_OBJECT: + case Instruction::MOVE_16: + case Instruction::MOVE_OBJECT_16: + case Instruction::MOVE_FROM16: + case Instruction::MOVE_OBJECT_FROM16: + return kSelectMove; + case Instruction::CONST: + case Instruction::CONST_4: + case Instruction::CONST_16: + return kSelectConst; + case Instruction::GOTO: + case Instruction::GOTO_16: + case Instruction::GOTO_32: + return kSelectGoto; + default: + return kSelectNone; + } +} + +int MIRGraph::GetSSAUseCount(int s_reg) { + return raw_use_counts_.Get(s_reg); +} + + +/* Do some MIR-level extended basic block optimizations */ +bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { + if (bb->block_type == kDead) { + return true; + } + int num_temps = 0; + LocalValueNumbering local_valnum(cu_); + while (bb != NULL) { + for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { + // TUNING: use the returned value number for CSE. + local_valnum.GetValueNumber(mir); + // Look for interesting opcodes, skip otherwise + Instruction::Code opcode = mir->dalvikInsn.opcode; + switch (opcode) { + case Instruction::CMPL_FLOAT: + case Instruction::CMPL_DOUBLE: + case Instruction::CMPG_FLOAT: + case Instruction::CMPG_DOUBLE: + case Instruction::CMP_LONG: + if ((cu_->disable_opt & (1 << kBranchFusing)) != 0) { + // Bitcode doesn't allow this optimization. + break; + } + if (mir->next != NULL) { + MIR* mir_next = mir->next; + Instruction::Code br_opcode = mir_next->dalvikInsn.opcode; + ConditionCode ccode = kCondNv; + switch (br_opcode) { + case Instruction::IF_EQZ: + ccode = kCondEq; + break; + case Instruction::IF_NEZ: + ccode = kCondNe; + break; + case Instruction::IF_LTZ: + ccode = kCondLt; + break; + case Instruction::IF_GEZ: + ccode = kCondGe; + break; + case Instruction::IF_GTZ: + ccode = kCondGt; + break; + case Instruction::IF_LEZ: + ccode = kCondLe; + break; + default: + break; + } + // Make sure result of cmp is used by next insn and nowhere else + if ((ccode != kCondNv) && + (mir->ssa_rep->defs[0] == mir_next->ssa_rep->uses[0]) && + (GetSSAUseCount(mir->ssa_rep->defs[0]) == 1)) { + mir_next->dalvikInsn.arg[0] = ccode; + switch (opcode) { + case Instruction::CMPL_FLOAT: + mir_next->dalvikInsn.opcode = + static_cast(kMirOpFusedCmplFloat); + break; + case Instruction::CMPL_DOUBLE: + mir_next->dalvikInsn.opcode = + static_cast(kMirOpFusedCmplDouble); + break; + case Instruction::CMPG_FLOAT: + mir_next->dalvikInsn.opcode = + static_cast(kMirOpFusedCmpgFloat); + break; + case Instruction::CMPG_DOUBLE: + mir_next->dalvikInsn.opcode = + static_cast(kMirOpFusedCmpgDouble); + break; + case Instruction::CMP_LONG: + mir_next->dalvikInsn.opcode = + static_cast(kMirOpFusedCmpLong); + break; + default: LOG(ERROR) << "Unexpected opcode: " << opcode; + } + mir->dalvikInsn.opcode = static_cast(kMirOpNop); + mir_next->ssa_rep->num_uses = mir->ssa_rep->num_uses; + mir_next->ssa_rep->uses = mir->ssa_rep->uses; + mir_next->ssa_rep->fp_use = mir->ssa_rep->fp_use; + mir_next->ssa_rep->num_defs = 0; + mir->ssa_rep->num_uses = 0; + mir->ssa_rep->num_defs = 0; + } + } + break; + case Instruction::GOTO: + case Instruction::GOTO_16: + case Instruction::GOTO_32: + case Instruction::IF_EQ: + case Instruction::IF_NE: + case Instruction::IF_LT: + case Instruction::IF_GE: + case Instruction::IF_GT: + case Instruction::IF_LE: + case Instruction::IF_EQZ: + case Instruction::IF_NEZ: + case Instruction::IF_LTZ: + case Instruction::IF_GEZ: + case Instruction::IF_GTZ: + case Instruction::IF_LEZ: + // If we've got a backwards branch to return, no need to suspend check. + if ((IsBackedge(bb, bb->taken) && bb->taken->dominates_return) || + (IsBackedge(bb, bb->fall_through) && bb->fall_through->dominates_return)) { + mir->optimization_flags |= MIR_IGNORE_SUSPEND_CHECK; + if (cu_->verbose) { + LOG(INFO) << "Suppressed suspend check on branch to return at 0x" << std::hex << mir->offset; + } + } + break; + default: + break; + } + // Is this the select pattern? + // TODO: flesh out support for Mips and X86. NOTE: llvm's select op doesn't quite work here. + // TUNING: expand to support IF_xx compare & branches + if (!(cu_->compiler_backend == kPortable) && (cu_->instruction_set == kThumb2) && + ((mir->dalvikInsn.opcode == Instruction::IF_EQZ) || + (mir->dalvikInsn.opcode == Instruction::IF_NEZ))) { + BasicBlock* ft = bb->fall_through; + DCHECK(ft != NULL); + BasicBlock* ft_ft = ft->fall_through; + BasicBlock* ft_tk = ft->taken; + + BasicBlock* tk = bb->taken; + DCHECK(tk != NULL); + BasicBlock* tk_ft = tk->fall_through; + BasicBlock* tk_tk = tk->taken; + + /* + * In the select pattern, the taken edge goes to a block that unconditionally + * transfers to the rejoin block and the fall_though edge goes to a block that + * unconditionally falls through to the rejoin block. + */ + 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. + */ + if (SelectKind(tk->last_mir_insn) == kSelectGoto) { + tk->last_mir_insn->optimization_flags |= (MIR_IGNORE_SUSPEND_CHECK); + } + // Are the block bodies something we can handle? + if ((ft->first_mir_insn == ft->last_mir_insn) && + (tk->first_mir_insn != tk->last_mir_insn) && + (tk->first_mir_insn->next == tk->last_mir_insn) && + ((SelectKind(ft->first_mir_insn) == kSelectMove) || + (SelectKind(ft->first_mir_insn) == kSelectConst)) && + (SelectKind(ft->first_mir_insn) == SelectKind(tk->first_mir_insn)) && + (SelectKind(tk->last_mir_insn) == kSelectGoto)) { + // Almost there. Are the instructions targeting the same vreg? + MIR* if_true = tk->first_mir_insn; + MIR* if_false = ft->first_mir_insn; + // It's possible that the target of the select isn't used - skip those (rare) cases. + MIR* phi = FindPhi(tk_tk, if_true->ssa_rep->defs[0]); + if ((phi != NULL) && (if_true->dalvikInsn.vA == if_false->dalvikInsn.vA)) { + /* + * We'll convert the IF_EQZ/IF_NEZ to a SELECT. We need to find the + * Phi node in the merge block and delete it (while using the SSA name + * of the merge as the target of the SELECT. Delete both taken and + * fallthrough blocks, and set fallthrough to merge block. + * NOTE: not updating other dataflow info (no longer used at this point). + * If this changes, need to update i_dom, etc. here (and in CombineBlocks). + */ + if (opcode == Instruction::IF_NEZ) { + // Normalize. + MIR* tmp_mir = if_true; + if_true = if_false; + if_false = tmp_mir; + } + mir->dalvikInsn.opcode = static_cast(kMirOpSelect); + bool const_form = (SelectKind(if_true) == kSelectConst); + if ((SelectKind(if_true) == kSelectMove)) { + if (IsConst(if_true->ssa_rep->uses[0]) && + IsConst(if_false->ssa_rep->uses[0])) { + const_form = true; + if_true->dalvikInsn.vB = ConstantValue(if_true->ssa_rep->uses[0]); + if_false->dalvikInsn.vB = ConstantValue(if_false->ssa_rep->uses[0]); + } + } + if (const_form) { + // "true" set val in vB + mir->dalvikInsn.vB = if_true->dalvikInsn.vB; + // "false" set val in vC + mir->dalvikInsn.vC = if_false->dalvikInsn.vB; + } else { + DCHECK_EQ(SelectKind(if_true), kSelectMove); + DCHECK_EQ(SelectKind(if_false), kSelectMove); + int* src_ssa = + static_cast(arena_->Alloc(sizeof(int) * 3, ArenaAllocator::kAllocDFInfo)); + src_ssa[0] = mir->ssa_rep->uses[0]; + src_ssa[1] = if_true->ssa_rep->uses[0]; + src_ssa[2] = if_false->ssa_rep->uses[0]; + mir->ssa_rep->uses = src_ssa; + mir->ssa_rep->num_uses = 3; + } + mir->ssa_rep->num_defs = 1; + mir->ssa_rep->defs = + static_cast(arena_->Alloc(sizeof(int) * 1, ArenaAllocator::kAllocDFInfo)); + mir->ssa_rep->fp_def = + static_cast(arena_->Alloc(sizeof(bool) * 1, ArenaAllocator::kAllocDFInfo)); + mir->ssa_rep->fp_def[0] = if_true->ssa_rep->fp_def[0]; + // Match type of uses to def. + mir->ssa_rep->fp_use = + static_cast(arena_->Alloc(sizeof(bool) * mir->ssa_rep->num_uses, + ArenaAllocator::kAllocDFInfo)); + for (int i = 0; i < mir->ssa_rep->num_uses; i++) { + mir->ssa_rep->fp_use[i] = mir->ssa_rep->fp_def[0]; + } + /* + * There is usually a Phi node in the join block for our two cases. If the + * 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 + * 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(kMirOpNop); + } else { + int dead_def = if_false->ssa_rep->defs[0]; + int live_def = if_true->ssa_rep->defs[0]; + mir->ssa_rep->defs[0] = live_def; + int* incoming = reinterpret_cast(phi->dalvikInsn.vB); + 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 = NULL; + tk->block_type = kDead; + for (MIR* tmir = ft->first_mir_insn; tmir != NULL; tmir = tmir->next) { + tmir->dalvikInsn.opcode = static_cast(kMirOpNop); + } + } + } + } + } + } + bb = NextDominatedBlock(bb); + } + + if (num_temps > cu_->num_compiler_temps) { + cu_->num_compiler_temps = num_temps; + } + return true; +} + +void MIRGraph::NullCheckEliminationInit(struct BasicBlock* bb) { + if (bb->data_flow_info != NULL) { + bb->data_flow_info->ending_null_check_v = + new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false, kBitMapNullCheck); + } +} + +/* Collect stats on number of checks removed */ +void MIRGraph::CountChecks(struct BasicBlock* bb) { + if (bb->data_flow_info != NULL) { + for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { + if (mir->ssa_rep == NULL) { + continue; + } + int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; + if (df_attributes & DF_HAS_NULL_CHKS) { + checkstats_->null_checks++; + if (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) { + checkstats_->null_checks_eliminated++; + } + } + if (df_attributes & DF_HAS_RANGE_CHKS) { + checkstats_->range_checks++; + if (mir->optimization_flags & MIR_IGNORE_RANGE_CHECK) { + checkstats_->range_checks_eliminated++; + } + } + } + } +} + +/* Try to make common case the fallthrough path */ +static bool LayoutBlocks(struct BasicBlock* bb) { + // TODO: For now, just looking for direct throws. Consider generalizing for profile feedback + if (!bb->explicit_throw) { + return false; + } + BasicBlock* walker = bb; + while (true) { + // Check termination conditions + if ((walker->block_type == kEntryBlock) || (Predecessors(walker) != 1)) { + break; + } + BasicBlock* prev = walker->predecessors->Get(0); + if (prev->conditional_branch) { + if (prev->fall_through == walker) { + // Already done - return + break; + } + DCHECK_EQ(walker, prev->taken); + // Got one. Flip it and exit + Instruction::Code opcode = prev->last_mir_insn->dalvikInsn.opcode; + switch (opcode) { + case Instruction::IF_EQ: opcode = Instruction::IF_NE; break; + case Instruction::IF_NE: opcode = Instruction::IF_EQ; break; + case Instruction::IF_LT: opcode = Instruction::IF_GE; break; + case Instruction::IF_GE: opcode = Instruction::IF_LT; break; + case Instruction::IF_GT: opcode = Instruction::IF_LE; break; + case Instruction::IF_LE: opcode = Instruction::IF_GT; break; + case Instruction::IF_EQZ: opcode = Instruction::IF_NEZ; break; + case Instruction::IF_NEZ: opcode = Instruction::IF_EQZ; break; + case Instruction::IF_LTZ: opcode = Instruction::IF_GEZ; break; + case Instruction::IF_GEZ: opcode = Instruction::IF_LTZ; break; + case Instruction::IF_GTZ: opcode = Instruction::IF_LEZ; break; + case Instruction::IF_LEZ: opcode = Instruction::IF_GTZ; break; + default: LOG(FATAL) << "Unexpected opcode " << opcode; + } + prev->last_mir_insn->dalvikInsn.opcode = opcode; + BasicBlock* t_bb = prev->taken; + prev->taken = prev->fall_through; + prev->fall_through = t_bb; + break; + } + walker = prev; + } + return false; +} + +/* Combine any basic blocks terminated by instructions that we now know can't throw */ +bool MIRGraph::CombineBlocks(struct BasicBlock* bb) { + // Loop here to allow combining a sequence of blocks + while (true) { + // Check termination conditions + if ((bb->first_mir_insn == NULL) + || (bb->data_flow_info == NULL) + || (bb->block_type == kExceptionHandling) + || (bb->block_type == kExitBlock) + || (bb->block_type == kDead) + || ((bb->taken == NULL) || (bb->taken->block_type != kExceptionHandling)) + || (bb->successor_block_list.block_list_type != kNotUsed) + || (static_cast(bb->last_mir_insn->dalvikInsn.opcode) != kMirOpCheck)) { + break; + } + + // Test the kMirOpCheck instruction + MIR* mir = bb->last_mir_insn; + // Grab the attributes from the paired opcode + MIR* throw_insn = mir->meta.throw_insn; + int df_attributes = oat_data_flow_attributes_[throw_insn->dalvikInsn.opcode]; + bool can_combine = true; + if (df_attributes & DF_HAS_NULL_CHKS) { + can_combine &= ((throw_insn->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0); + } + if (df_attributes & DF_HAS_RANGE_CHKS) { + can_combine &= ((throw_insn->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0); + } + if (!can_combine) { + break; + } + // OK - got one. Combine + BasicBlock* bb_next = bb->fall_through; + DCHECK(!bb_next->catch_entry); + DCHECK_EQ(Predecessors(bb_next), 1U); + MIR* t_mir = bb->last_mir_insn->prev; + // Overwrite the kOpCheck insn with the paired opcode + DCHECK_EQ(bb_next->first_mir_insn, throw_insn); + *bb->last_mir_insn = *throw_insn; + bb->last_mir_insn->prev = t_mir; + // Use the successor info from the next block + bb->successor_block_list = bb_next->successor_block_list; + // Use the ending block linkage from the next block + bb->fall_through = bb_next->fall_through; + bb->taken->block_type = kDead; // Kill the unused exception block + bb->taken = bb_next->taken; + // Include the rest of the instructions + bb->last_mir_insn = bb_next->last_mir_insn; + /* + * If lower-half of pair of blocks to combine contained a return, move the flag + * to the newly combined block. + */ + bb->terminated_by_return = bb_next->terminated_by_return; + + /* + * NOTE: we aren't updating all dataflow info here. Should either make sure this pass + * happens after uses of i_dominated, dom_frontier or update the dataflow info here. + */ + + // Kill bb_next and remap now-dead id to parent + bb_next->block_type = kDead; + block_id_map_.Overwrite(bb_next->id, bb->id); + + // Now, loop back and see if we can keep going + } + return false; +} + +/* Eliminate unnecessary null checks for a basic block. */ +bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { + if (bb->data_flow_info == NULL) return false; + + /* + * Set initial state. Be conservative with catch + * blocks and start with no assumptions about null check + * status (except for "this"). + */ + if ((bb->block_type == kEntryBlock) | bb->catch_entry) { + temp_ssa_register_v_->ClearAllBits(); + if ((cu_->access_flags & kAccStatic) == 0) { + // If non-static method, mark "this" as non-null + int this_reg = cu_->num_dalvik_registers - cu_->num_ins; + temp_ssa_register_v_->SetBit(this_reg); + } + } else if (bb->predecessors->Size() == 1) { + BasicBlock* pred_bb = bb->predecessors->Get(0); + temp_ssa_register_v_->Copy(pred_bb->data_flow_info->ending_null_check_v); + if (pred_bb->block_type == kDalvikByteCode) { + // Check to see if predecessor had an explicit null-check. + MIR* last_insn = pred_bb->last_mir_insn; + Instruction::Code last_opcode = last_insn->dalvikInsn.opcode; + if (last_opcode == Instruction::IF_EQZ) { + if (pred_bb->fall_through == bb) { + // The fall-through of a block following a IF_EQZ, set the vA of the IF_EQZ to show that + // it can't be null. + temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]); + } + } else if (last_opcode == Instruction::IF_NEZ) { + if (pred_bb->taken == bb) { + // The taken block following a IF_NEZ, set the vA of the IF_NEZ to show that it can't be + // null. + temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]); + } + } + } + } else { + // Starting state is intersection of all incoming arcs + GrowableArray::Iterator iter(bb->predecessors); + BasicBlock* pred_bb = iter.Next(); + DCHECK(pred_bb != NULL); + temp_ssa_register_v_->Copy(pred_bb->data_flow_info->ending_null_check_v); + while (true) { + pred_bb = iter.Next(); + if (!pred_bb) break; + if ((pred_bb->data_flow_info == NULL) || + (pred_bb->data_flow_info->ending_null_check_v == NULL)) { + continue; + } + temp_ssa_register_v_->Intersect(pred_bb->data_flow_info->ending_null_check_v); + } + } + + // Walk through the instruction in the block, updating as necessary + for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { + if (mir->ssa_rep == NULL) { + continue; + } + int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; + + // Mark target of NEW* as non-null + if (df_attributes & DF_NON_NULL_DST) { + temp_ssa_register_v_->SetBit(mir->ssa_rep->defs[0]); + } + + // Mark non-null returns from invoke-style NEW* + if (df_attributes & DF_NON_NULL_RET) { + MIR* next_mir = mir->next; + // Next should be an MOVE_RESULT_OBJECT + if (next_mir && + next_mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) { + // Mark as null checked + temp_ssa_register_v_->SetBit(next_mir->ssa_rep->defs[0]); + } else { + if (next_mir) { + LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode; + } else if (bb->fall_through) { + // Look in next basic block + struct BasicBlock* next_bb = bb->fall_through; + for (MIR* tmir = next_bb->first_mir_insn; tmir != NULL; + tmir =tmir->next) { + if (static_cast(tmir->dalvikInsn.opcode) >= static_cast(kMirOpFirst)) { + continue; + } + // First non-pseudo should be MOVE_RESULT_OBJECT + if (tmir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) { + // Mark as null checked + temp_ssa_register_v_->SetBit(tmir->ssa_rep->defs[0]); + } else { + LOG(WARNING) << "Unexpected op after new: " << tmir->dalvikInsn.opcode; + } + break; + } + } + } + } + + /* + * Propagate nullcheck state on register copies (including + * Phi pseudo copies. For the latter, nullcheck state is + * the "and" of all the Phi's operands. + */ + if (df_attributes & (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N)) { + int tgt_sreg = mir->ssa_rep->defs[0]; + int operands = (df_attributes & DF_NULL_TRANSFER_0) ? 1 : + mir->ssa_rep->num_uses; + bool null_checked = true; + for (int i = 0; i < operands; i++) { + null_checked &= temp_ssa_register_v_->IsBitSet(mir->ssa_rep->uses[i]); + } + if (null_checked) { + temp_ssa_register_v_->SetBit(tgt_sreg); + } + } + + // Already nullchecked? + if ((df_attributes & DF_HAS_NULL_CHKS) && !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) { + int src_idx; + if (df_attributes & DF_NULL_CHK_1) { + src_idx = 1; + } else if (df_attributes & DF_NULL_CHK_2) { + src_idx = 2; + } else { + src_idx = 0; + } + int src_sreg = mir->ssa_rep->uses[src_idx]; + if (temp_ssa_register_v_->IsBitSet(src_sreg)) { + // Eliminate the null check + mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; + } else { + // Mark s_reg as null-checked + temp_ssa_register_v_->SetBit(src_sreg); + } + } + } + + // Did anything change? + bool changed = !temp_ssa_register_v_->Equal(bb->data_flow_info->ending_null_check_v); + if (changed) { + bb->data_flow_info->ending_null_check_v->Copy(temp_ssa_register_v_); + } + return changed; +} + +void MIRGraph::NullCheckElimination() { + if (!(cu_->disable_opt & (1 << kNullCheckElimination))) { + DCHECK(temp_ssa_register_v_ != NULL); + AllNodesIterator iter(this, false /* not iterative */); + for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { + NullCheckEliminationInit(bb); + } + PreOrderDfsIterator iter2(this, true /* iterative */); + bool change = false; + for (BasicBlock* bb = iter2.Next(change); bb != NULL; bb = iter2.Next(change)) { + change = EliminateNullChecks(bb); + } + } + if (cu_->enable_debug & (1 << kDebugDumpCFG)) { + DumpCFG("/sdcard/4_post_nce_cfg/", false); + } +} + +void MIRGraph::BasicBlockCombine() { + PreOrderDfsIterator iter(this, false /* not iterative */); + for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { + CombineBlocks(bb); + } + if (cu_->enable_debug & (1 << kDebugDumpCFG)) { + DumpCFG("/sdcard/5_post_bbcombine_cfg/", false); + } +} + +void MIRGraph::CodeLayout() { + if (cu_->enable_debug & (1 << kDebugVerifyDataflow)) { + VerifyDataflow(); + } + AllNodesIterator iter(this, false /* not iterative */); + for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { + LayoutBlocks(bb); + } + if (cu_->enable_debug & (1 << kDebugDumpCFG)) { + DumpCFG("/sdcard/2_post_layout_cfg/", true); + } +} + +void MIRGraph::DumpCheckStats() { + Checkstats* stats = + static_cast(arena_->Alloc(sizeof(Checkstats), ArenaAllocator::kAllocDFInfo)); + checkstats_ = stats; + AllNodesIterator iter(this, false /* not iterative */); + for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { + CountChecks(bb); + } + if (stats->null_checks > 0) { + float eliminated = static_cast(stats->null_checks_eliminated); + float checks = static_cast(stats->null_checks); + LOG(INFO) << "Null Checks: " << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " + << stats->null_checks_eliminated << " of " << stats->null_checks << " -> " + << (eliminated/checks) * 100.0 << "%"; + } + if (stats->range_checks > 0) { + float eliminated = static_cast(stats->range_checks_eliminated); + float checks = static_cast(stats->range_checks); + LOG(INFO) << "Range Checks: " << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " + << stats->range_checks_eliminated << " of " << stats->range_checks << " -> " + << (eliminated/checks) * 100.0 << "%"; + } +} + +bool MIRGraph::BuildExtendedBBList(struct BasicBlock* bb) { + if (bb->visited) return false; + if (!((bb->block_type == kEntryBlock) || (bb->block_type == kDalvikByteCode) + || (bb->block_type == kExitBlock))) { + // Ignore special blocks + bb->visited = true; + return false; + } + // Must be head of extended basic block. + BasicBlock* start_bb = bb; + extended_basic_blocks_.push_back(bb); + bool terminated_by_return = false; + // Visit blocks strictly dominated by this head. + while (bb != NULL) { + bb->visited = true; + terminated_by_return |= bb->terminated_by_return; + bb = NextDominatedBlock(bb); + } + if (terminated_by_return) { + // This extended basic block contains a return, so mark all members. + bb = start_bb; + while (bb != NULL) { + bb->dominates_return = true; + bb = NextDominatedBlock(bb); + } + } + return false; // Not iterative - return value will be ignored +} + + +void MIRGraph::BasicBlockOptimization() { + if (!(cu_->disable_opt & (1 << kBBOpt))) { + DCHECK_EQ(cu_->num_compiler_temps, 0); + ClearAllVisitedFlags(); + PreOrderDfsIterator iter2(this, false /* not iterative */); + for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) { + BuildExtendedBBList(bb); + } + // Perform extended basic block optimizations. + for (unsigned int i = 0; i < extended_basic_blocks_.size(); i++) { + BasicBlockOpt(extended_basic_blocks_[i]); + } + } + if (cu_->enable_debug & (1 << kDebugDumpCFG)) { + DumpCFG("/sdcard/6_post_bbo_cfg/", false); + } +} + +} // namespace art diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc new file mode 100644 index 0000000000000000000000000000000000000000..90cec750398934ac002fdfda103fde72b76c12a3 --- /dev/null +++ b/compiler/dex/portable/mir_to_gbc.cc @@ -0,0 +1,1992 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dex/compiler_internals.h" +#include "dex/dataflow_iterator-inl.h" +#include "dex/frontend.h" +#include "mir_to_gbc.h" + +#include "llvm/llvm_compilation_unit.h" +#include "llvm/utils_llvm.h" + +const char* kLabelFormat = "%c0x%x_%d"; +const char kInvalidBlock = 0xff; +const char kNormalBlock = 'L'; +const char kCatchBlock = 'C'; + +namespace art { + +::llvm::BasicBlock* MirConverter::GetLLVMBlock(int id) { + return id_to_block_map_.Get(id); +} + +::llvm::Value* MirConverter::GetLLVMValue(int s_reg) { + return llvm_values_.Get(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_.Put(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(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, + int32_t table_offset, RegLocation rl_src) { + const Instruction::PackedSwitchPayload* payload = + reinterpret_cast( + cu_->insns + current_dalvik_offset_ + table_offset); + + ::llvm::Value* value = GetLLVMValue(rl_src.orig_sreg); + + ::llvm::SwitchInst* sw = + irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through->id), + 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 = NULL; + bb->fall_through = NULL; +} + +void MirConverter::ConvertSparseSwitch(BasicBlock* bb, + int32_t table_offset, RegLocation rl_src) { + const Instruction::SparseSwitchPayload* payload = + reinterpret_cast( + cu_->insns + current_dalvik_offset_ + 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->id), + 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 = NULL; + bb->fall_through = NULL; +} + +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 (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->id), + GetLLVMBlock(bb->fall_through->id)); + // Don't redo the fallthrough branch in the BB driver + bb->fall_through = NULL; +} + +void MirConverter::ConvertCompareZeroAndBranch(BasicBlock* bb, + MIR* mir, ConditionCode cc, RegLocation rl_src1) { + if (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->id), + GetLLVMBlock(bb->fall_through->id)); + // Don't redo the fallthrough branch in the BB driver + bb->fall_through = NULL; +} + +::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(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(-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 (op_val < kMirOpFirst) { + 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; + int attrs = mir_graph_->oat_data_flow_attributes_[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(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(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 (bb->taken->start_offset <= bb->start_offset) { + EmitSuspendCheck(); + } + irb_->CreateBr(GetLLVMBlock(bb->taken->id)); + } + 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 == NULL) { + irb_->CreateUnreachable(); + } else { + bb->fall_through->fall_through = NULL; + bb->fall_through->taken = NULL; + } + 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(cu_->num_ins)); + reg_info.push_back(irb_->getInt32(cu_->num_regs)); + reg_info.push_back(irb_->getInt32(cu_->num_outs)); + reg_info.push_back(irb_->getInt32(cu_->num_compiler_temps)); + 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 (opcode < kMirOpFirst) { + // 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 + } + int* incoming = reinterpret_cast(mir->dalvikInsn.vB); + ::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::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(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 == NULL) && + (bb->fall_through == NULL)) { + 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(cu_->num_dalvik_registers); + irb_->CreateCall(func, entries); + } + + { // Store arguments to vregs. + uint16_t arg_reg = cu_->num_regs; + + ::llvm::Function::arg_iterator arg_iter(func_->arg_begin()); + ::llvm::Function::arg_iterator arg_end(func_->arg_end()); + + 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->meta.original_opcode = work_half->dalvikInsn.opcode; + work_half->dalvikInsn.opcode = static_cast(kMirOpNop); + if (bb->successor_block_list.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)); + GrowableArray::Iterator iter(bb->successor_block_list.blocks); + // 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_block_list.blocks->Size()); + while (true) { + SuccessorBlockInfo *successor_block_info = iter.Next(); + if (successor_block_info == NULL) break; + ::llvm::BasicBlock *target = + GetLLVMBlock(successor_block_info->block->id); + int type_index = successor_block_info->key; + sw->addCase(irb_->getInt32(type_index), target); + } + llvm_bb = work_bb; + irb_->SetInsertPoint(llvm_bb); + } + } + + if (opcode >= kMirOpFirst) { + ConvertExtendedMIR(bb, mir, llvm_bb); + continue; + } + + bool not_handled = ConvertMIRNode(mir, bb, llvm_bb); + if (not_handled) { + Instruction::Code dalvik_opcode = static_cast(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->id); + } else if ((bb->fall_through != NULL) && !bb->terminated_by_return) { + irb_->CreateBr(GetLLVMBlock(bb->fall_through->id)); + } + + 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 = cu_->num_regs; + + 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_, false /* not iterative */); + 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_.Insert(0); + } else if ((i < cu_->num_regs) || + (i >= (cu_->num_regs + cu_->num_ins))) { + ::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_.Insert(val); + } else { + // Recover previously-created argument values + ::llvm::Value* arg_val = arg_iter++; + llvm_values_.Insert(arg_val); + } + } + + PreOrderDfsIterator iter2(mir_graph_, false /* not iterative */); + 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 " << cu_->code_item->insns_size_in_code_units_; + 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 new file mode 100644 index 0000000000000000000000000000000000000000..2b681f609713d9cd404733fbc1c2db01029b23fd --- /dev/null +++ b/compiler/dex/portable/mir_to_gbc.h @@ -0,0 +1,194 @@ +/* + * 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 "invoke_type.h" +#include "compiled_method.h" +#include "dex/compiler_enums.h" +#include "dex/compiler_ir.h" +#include "dex/backend.h" +#include "llvm/llvm_compilation_unit.h" +#include "safe_map.h" + +namespace art { + +struct 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, mir_graph->GetNumSSARegs()), + temp_name_(0), + current_dalvik_offset_(0) { + 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, int32_t table_offset, + RegLocation rl_src); + void ConvertSparseSwitch(BasicBlock* bb, 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_; + GrowableArray< ::llvm::Value*> llvm_values_; + int32_t temp_name_; + SafeMap 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/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h new file mode 100644 index 0000000000000000000000000000000000000000..2f54190ae7efbbf37df39a7d6858223daa0fc882 --- /dev/null +++ b/compiler/dex/quick/arm/arm_lir.h @@ -0,0 +1,499 @@ +/* + * 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_QUICK_ARM_ARM_LIR_H_ +#define ART_COMPILER_DEX_QUICK_ARM_ARM_LIR_H_ + +#include "dex/compiler_internals.h" + +namespace art { + +/* + * Runtime register usage conventions. + * + * r0-r3: Argument registers in both Dalvik and C/C++ conventions. + * However, for Dalvik->Dalvik calls we'll pass the target's Method* + * pointer in r0 as a hidden arg0. Otherwise used as codegen scratch + * registers. + * r0-r1: As in C/C++ r0 is 32-bit return register and r0/r1 is 64-bit + * r4 : (rARM_SUSPEND) is reserved (suspend check/debugger assist) + * r5 : Callee save (promotion target) + * r6 : Callee save (promotion target) + * r7 : Callee save (promotion target) + * r8 : Callee save (promotion target) + * r9 : (rARM_SELF) is reserved (pointer to thread-local storage) + * r10 : Callee save (promotion target) + * r11 : Callee save (promotion target) + * r12 : Scratch, may be trashed by linkage stubs + * r13 : (sp) is reserved + * r14 : (lr) is reserved + * r15 : (pc) is reserved + * + * 5 core temps that codegen can use (r0, r1, r2, r3, r12) + * 7 core registers that can be used for promotion + * + * Floating pointer registers + * s0-s31 + * d0-d15, where d0={s0,s1}, d1={s2,s3}, ... , d15={s30,s31} + * + * s16-s31 (d8-d15) preserved across C calls + * s0-s15 (d0-d7) trashed across C calls + * + * s0-s15/d0-d7 used as codegen temp/scratch + * s16-s31/d8-d31 can be used for promotion. + * + * Calling convention + * o On a call to a Dalvik method, pass target's Method* in r0 + * o r1-r3 will be used for up to the first 3 words of arguments + * o Arguments past the first 3 words will be placed in appropriate + * out slots by the caller. + * o If a 64-bit argument would span the register/memory argument + * boundary, it will instead be fully passed in the frame. + * o Maintain a 16-byte stack alignment + * + * Stack frame diagram (stack grows down, higher addresses at top): + * + * +------------------------+ + * | IN[ins-1] | {Note: resides in caller's frame} + * | . | + * | IN[0] | + * | caller's Method* | + * +========================+ {Note: start of callee's frame} + * | spill region | {variable sized - will include lr if non-leaf.} + * +------------------------+ + * | ...filler word... | {Note: used as 2nd word of V[locals-1] if long] + * +------------------------+ + * | V[locals-1] | + * | V[locals-2] | + * | . | + * | . | + * | V[1] | + * | V[0] | + * +------------------------+ + * | 0 to 3 words padding | + * +------------------------+ + * | OUT[outs-1] | + * | OUT[outs-2] | + * | . | + * | OUT[0] | + * | cur_method* | <<== sp w/ 16-byte alignment + * +========================+ + */ + +// Offset to distingish FP regs. +#define ARM_FP_REG_OFFSET 32 +// Offset to distinguish DP FP regs. +#define ARM_FP_DOUBLE 64 +// First FP callee save. +#define ARM_FP_CALLEE_SAVE_BASE 16 +// Reg types. +#define ARM_REGTYPE(x) (x & (ARM_FP_REG_OFFSET | ARM_FP_DOUBLE)) +#define ARM_FPREG(x) ((x & ARM_FP_REG_OFFSET) == ARM_FP_REG_OFFSET) +#define ARM_LOWREG(x) ((x & 0x7) == x) +#define ARM_DOUBLEREG(x) ((x & ARM_FP_DOUBLE) == ARM_FP_DOUBLE) +#define ARM_SINGLEREG(x) (ARM_FPREG(x) && !ARM_DOUBLEREG(x)) + +/* + * Note: the low register of a floating point pair is sufficient to + * create the name of a double, but require both names to be passed to + * allow for asserts to verify that the pair is consecutive if significant + * rework is done in this area. Also, it is a good reminder in the calling + * code that reg locations always describe doubles as a pair of singles. + */ +#define ARM_S2D(x, y) ((x) | ARM_FP_DOUBLE) +// Mask to strip off fp flags. +#define ARM_FP_REG_MASK (ARM_FP_REG_OFFSET-1) + +// RegisterLocation templates return values (r0, or r0/r1). +#define ARM_LOC_C_RETURN {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, r0, INVALID_REG, \ + INVALID_SREG, INVALID_SREG} +#define ARM_LOC_C_RETURN_WIDE {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1, \ + INVALID_SREG, INVALID_SREG} +#define ARM_LOC_C_RETURN_FLOAT ARM_LOC_C_RETURN +#define ARM_LOC_C_RETURN_DOUBLE ARM_LOC_C_RETURN_WIDE + +enum ArmResourceEncodingPos { + kArmGPReg0 = 0, + kArmRegSP = 13, + kArmRegLR = 14, + kArmRegPC = 15, + kArmFPReg0 = 16, + kArmFPReg16 = 32, + kArmRegEnd = 48, +}; + +#define ENCODE_ARM_REG_LIST(N) (static_cast(N)) +#define ENCODE_ARM_REG_SP (1ULL << kArmRegSP) +#define ENCODE_ARM_REG_LR (1ULL << kArmRegLR) +#define ENCODE_ARM_REG_PC (1ULL << kArmRegPC) +#define ENCODE_ARM_REG_FPCS_LIST(N) (static_cast(N) << kArmFPReg16) + +enum ArmNativeRegisterPool { + r0 = 0, + r1 = 1, + r2 = 2, + r3 = 3, + rARM_SUSPEND = 4, + r5 = 5, + r6 = 6, + r7 = 7, + r8 = 8, + rARM_SELF = 9, + r10 = 10, + r11 = 11, + r12 = 12, + r13sp = 13, + rARM_SP = 13, + r14lr = 14, + rARM_LR = 14, + r15pc = 15, + rARM_PC = 15, + fr0 = 0 + ARM_FP_REG_OFFSET, + fr1 = 1 + ARM_FP_REG_OFFSET, + fr2 = 2 + ARM_FP_REG_OFFSET, + fr3 = 3 + ARM_FP_REG_OFFSET, + fr4 = 4 + ARM_FP_REG_OFFSET, + fr5 = 5 + ARM_FP_REG_OFFSET, + fr6 = 6 + ARM_FP_REG_OFFSET, + fr7 = 7 + ARM_FP_REG_OFFSET, + fr8 = 8 + ARM_FP_REG_OFFSET, + fr9 = 9 + ARM_FP_REG_OFFSET, + fr10 = 10 + ARM_FP_REG_OFFSET, + fr11 = 11 + ARM_FP_REG_OFFSET, + fr12 = 12 + ARM_FP_REG_OFFSET, + fr13 = 13 + ARM_FP_REG_OFFSET, + fr14 = 14 + ARM_FP_REG_OFFSET, + fr15 = 15 + ARM_FP_REG_OFFSET, + fr16 = 16 + ARM_FP_REG_OFFSET, + fr17 = 17 + ARM_FP_REG_OFFSET, + fr18 = 18 + ARM_FP_REG_OFFSET, + fr19 = 19 + ARM_FP_REG_OFFSET, + fr20 = 20 + ARM_FP_REG_OFFSET, + fr21 = 21 + ARM_FP_REG_OFFSET, + fr22 = 22 + ARM_FP_REG_OFFSET, + fr23 = 23 + ARM_FP_REG_OFFSET, + fr24 = 24 + ARM_FP_REG_OFFSET, + fr25 = 25 + ARM_FP_REG_OFFSET, + fr26 = 26 + ARM_FP_REG_OFFSET, + fr27 = 27 + ARM_FP_REG_OFFSET, + fr28 = 28 + ARM_FP_REG_OFFSET, + fr29 = 29 + ARM_FP_REG_OFFSET, + fr30 = 30 + ARM_FP_REG_OFFSET, + fr31 = 31 + ARM_FP_REG_OFFSET, + dr0 = fr0 + ARM_FP_DOUBLE, + dr1 = fr2 + ARM_FP_DOUBLE, + dr2 = fr4 + ARM_FP_DOUBLE, + dr3 = fr6 + ARM_FP_DOUBLE, + dr4 = fr8 + ARM_FP_DOUBLE, + dr5 = fr10 + ARM_FP_DOUBLE, + dr6 = fr12 + ARM_FP_DOUBLE, + dr7 = fr14 + ARM_FP_DOUBLE, + dr8 = fr16 + ARM_FP_DOUBLE, + dr9 = fr18 + ARM_FP_DOUBLE, + dr10 = fr20 + ARM_FP_DOUBLE, + dr11 = fr22 + ARM_FP_DOUBLE, + dr12 = fr24 + ARM_FP_DOUBLE, + dr13 = fr26 + ARM_FP_DOUBLE, + dr14 = fr28 + ARM_FP_DOUBLE, + dr15 = fr30 + ARM_FP_DOUBLE, +}; + +// Target-independent aliases. +#define rARM_ARG0 r0 +#define rARM_ARG1 r1 +#define rARM_ARG2 r2 +#define rARM_ARG3 r3 +#define rARM_FARG0 r0 +#define rARM_FARG1 r1 +#define rARM_FARG2 r2 +#define rARM_FARG3 r3 +#define rARM_RET0 r0 +#define rARM_RET1 r1 +#define rARM_INVOKE_TGT rARM_LR +#define rARM_COUNT INVALID_REG + +enum ArmShiftEncodings { + kArmLsl = 0x0, + kArmLsr = 0x1, + kArmAsr = 0x2, + kArmRor = 0x3 +}; + +/* + * The following enum defines the list of supported Thumb instructions by the + * assembler. Their corresponding EncodingMap positions will be defined in + * Assemble.cc. + */ +enum ArmOpcode { + kArmFirst = 0, + kArm16BitData = kArmFirst, // DATA [0] rd[15..0]. + kThumbAdcRR, // adc [0100000101] rm[5..3] rd[2..0]. + kThumbAddRRI3, // add(1) [0001110] imm_3[8..6] rn[5..3] rd[2..0]*/ + kThumbAddRI8, // add(2) [00110] rd[10..8] imm_8[7..0]. + kThumbAddRRR, // add(3) [0001100] rm[8..6] rn[5..3] rd[2..0]. + kThumbAddRRLH, // add(4) [01000100] H12[01] rm[5..3] rd[2..0]. + kThumbAddRRHL, // add(4) [01001000] H12[10] rm[5..3] rd[2..0]. + kThumbAddRRHH, // add(4) [01001100] H12[11] rm[5..3] rd[2..0]. + kThumbAddPcRel, // add(5) [10100] rd[10..8] imm_8[7..0]. + kThumbAddSpRel, // add(6) [10101] rd[10..8] imm_8[7..0]. + kThumbAddSpI7, // add(7) [101100000] imm_7[6..0]. + kThumbAndRR, // and [0100000000] rm[5..3] rd[2..0]. + kThumbAsrRRI5, // asr(1) [00010] imm_5[10..6] rm[5..3] rd[2..0]. + kThumbAsrRR, // asr(2) [0100000100] rs[5..3] rd[2..0]. + kThumbBCond, // b(1) [1101] cond[11..8] offset_8[7..0]. + kThumbBUncond, // b(2) [11100] offset_11[10..0]. + kThumbBicRR, // bic [0100001110] rm[5..3] rd[2..0]. + kThumbBkpt, // bkpt [10111110] imm_8[7..0]. + kThumbBlx1, // blx(1) [111] H[10] offset_11[10..0]. + kThumbBlx2, // blx(1) [111] H[01] offset_11[10..0]. + kThumbBl1, // blx(1) [111] H[10] offset_11[10..0]. + kThumbBl2, // blx(1) [111] H[11] offset_11[10..0]. + kThumbBlxR, // blx(2) [010001111] rm[6..3] [000]. + kThumbBx, // bx [010001110] H2[6..6] rm[5..3] SBZ[000]. + kThumbCmnRR, // cmn [0100001011] rm[5..3] rd[2..0]. + kThumbCmpRI8, // cmp(1) [00101] rn[10..8] imm_8[7..0]. + kThumbCmpRR, // cmp(2) [0100001010] rm[5..3] rd[2..0]. + kThumbCmpLH, // cmp(3) [01000101] H12[01] rm[5..3] rd[2..0]. + kThumbCmpHL, // cmp(3) [01000110] H12[10] rm[5..3] rd[2..0]. + kThumbCmpHH, // cmp(3) [01000111] H12[11] rm[5..3] rd[2..0]. + kThumbEorRR, // eor [0100000001] rm[5..3] rd[2..0]. + kThumbLdmia, // ldmia [11001] rn[10..8] reglist [7..0]. + kThumbLdrRRI5, // ldr(1) [01101] imm_5[10..6] rn[5..3] rd[2..0]. + kThumbLdrRRR, // ldr(2) [0101100] rm[8..6] rn[5..3] rd[2..0]. + kThumbLdrPcRel, // ldr(3) [01001] rd[10..8] imm_8[7..0]. + kThumbLdrSpRel, // ldr(4) [10011] rd[10..8] imm_8[7..0]. + kThumbLdrbRRI5, // ldrb(1) [01111] imm_5[10..6] rn[5..3] rd[2..0]. + kThumbLdrbRRR, // ldrb(2) [0101110] rm[8..6] rn[5..3] rd[2..0]. + kThumbLdrhRRI5, // ldrh(1) [10001] imm_5[10..6] rn[5..3] rd[2..0]. + kThumbLdrhRRR, // ldrh(2) [0101101] rm[8..6] rn[5..3] rd[2..0]. + kThumbLdrsbRRR, // ldrsb [0101011] rm[8..6] rn[5..3] rd[2..0]. + kThumbLdrshRRR, // ldrsh [0101111] rm[8..6] rn[5..3] rd[2..0]. + kThumbLslRRI5, // lsl(1) [00000] imm_5[10..6] rm[5..3] rd[2..0]. + kThumbLslRR, // lsl(2) [0100000010] rs[5..3] rd[2..0]. + kThumbLsrRRI5, // lsr(1) [00001] imm_5[10..6] rm[5..3] rd[2..0]. + kThumbLsrRR, // lsr(2) [0100000011] rs[5..3] rd[2..0]. + kThumbMovImm, // mov(1) [00100] rd[10..8] imm_8[7..0]. + kThumbMovRR, // mov(2) [0001110000] rn[5..3] rd[2..0]. + kThumbMovRR_H2H, // mov(3) [01000111] H12[11] rm[5..3] rd[2..0]. + kThumbMovRR_H2L, // mov(3) [01000110] H12[01] rm[5..3] rd[2..0]. + kThumbMovRR_L2H, // mov(3) [01000101] H12[10] rm[5..3] rd[2..0]. + kThumbMul, // mul [0100001101] rm[5..3] rd[2..0]. + kThumbMvn, // mvn [0100001111] rm[5..3] rd[2..0]. + kThumbNeg, // neg [0100001001] rm[5..3] rd[2..0]. + kThumbOrr, // orr [0100001100] rm[5..3] rd[2..0]. + kThumbPop, // pop [1011110] r[8..8] rl[7..0]. + kThumbPush, // push [1011010] r[8..8] rl[7..0]. + kThumbRorRR, // ror [0100000111] rs[5..3] rd[2..0]. + kThumbSbc, // sbc [0100000110] rm[5..3] rd[2..0]. + kThumbStmia, // stmia [11000] rn[10..8] reglist [7.. 0]. + kThumbStrRRI5, // str(1) [01100] imm_5[10..6] rn[5..3] rd[2..0]. + kThumbStrRRR, // str(2) [0101000] rm[8..6] rn[5..3] rd[2..0]. + kThumbStrSpRel, // str(3) [10010] rd[10..8] imm_8[7..0]. + kThumbStrbRRI5, // strb(1) [01110] imm_5[10..6] rn[5..3] rd[2..0]. + kThumbStrbRRR, // strb(2) [0101010] rm[8..6] rn[5..3] rd[2..0]. + kThumbStrhRRI5, // strh(1) [10000] imm_5[10..6] rn[5..3] rd[2..0]. + kThumbStrhRRR, // strh(2) [0101001] rm[8..6] rn[5..3] rd[2..0]. + kThumbSubRRI3, // sub(1) [0001111] imm_3[8..6] rn[5..3] rd[2..0]*/ + kThumbSubRI8, // sub(2) [00111] rd[10..8] imm_8[7..0]. + kThumbSubRRR, // sub(3) [0001101] rm[8..6] rn[5..3] rd[2..0]. + kThumbSubSpI7, // sub(4) [101100001] imm_7[6..0]. + kThumbSwi, // swi [11011111] imm_8[7..0]. + kThumbTst, // tst [0100001000] rm[5..3] rn[2..0]. + kThumb2Vldrs, // vldr low sx [111011011001] rn[19..16] rd[15-12] [1010] imm_8[7..0]. + kThumb2Vldrd, // vldr low dx [111011011001] rn[19..16] rd[15-12] [1011] imm_8[7..0]. + kThumb2Vmuls, // vmul vd, vn, vm [111011100010] rn[19..16] rd[15-12] [10100000] rm[3..0]. + kThumb2Vmuld, // vmul vd, vn, vm [111011100010] rn[19..16] rd[15-12] [10110000] rm[3..0]. + kThumb2Vstrs, // vstr low sx [111011011000] rn[19..16] rd[15-12] [1010] imm_8[7..0]. + kThumb2Vstrd, // vstr low dx [111011011000] rn[19..16] rd[15-12] [1011] imm_8[7..0]. + kThumb2Vsubs, // vsub vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10100040] rm[3..0]. + kThumb2Vsubd, // vsub vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10110040] rm[3..0]. + kThumb2Vadds, // vadd vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10100000] rm[3..0]. + kThumb2Vaddd, // vadd vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10110000] rm[3..0]. + kThumb2Vdivs, // vdiv vd, vn, vm [111011101000] rn[19..16] rd[15-12] [10100000] rm[3..0]. + kThumb2Vdivd, // vdiv vd, vn, vm [111011101000] rn[19..16] rd[15-12] [10110000] rm[3..0]. + kThumb2VcvtIF, // vcvt.F32 vd, vm [1110111010111000] vd[15..12] [10101100] vm[3..0]. + kThumb2VcvtID, // vcvt.F64 vd, vm [1110111010111000] vd[15..12] [10111100] vm[3..0]. + kThumb2VcvtFI, // vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12] [10101100] vm[3..0]. + kThumb2VcvtDI, // vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12] [10111100] vm[3..0]. + kThumb2VcvtFd, // vcvt.F64.F32 vd, vm [1110111010110111] vd[15..12] [10101100] vm[3..0]. + kThumb2VcvtDF, // vcvt.F32.F64 vd, vm [1110111010110111] vd[15..12] [10111100] vm[3..0]. + kThumb2Vsqrts, // vsqrt.f32 vd, vm [1110111010110001] vd[15..12] [10101100] vm[3..0]. + kThumb2Vsqrtd, // vsqrt.f64 vd, vm [1110111010110001] vd[15..12] [10111100] vm[3..0]. + kThumb2MovImmShift, // mov(T2) rd, # [11110] i [00001001111] imm3 rd[11..8] imm8. + kThumb2MovImm16, // mov(T3) rd, # [11110] i [0010100] imm4 [0] imm3 rd[11..8] imm8. + kThumb2StrRRI12, // str(Imm,T3) rd,[rn,#imm12] [111110001100] rn[19..16] rt[15..12] imm12[11..0]. + kThumb2LdrRRI12, // str(Imm,T3) rd,[rn,#imm12] [111110001100] rn[19..16] rt[15..12] imm12[11..0]. + kThumb2StrRRI8Predec, // str(Imm,T4) rd,[rn,#-imm8] [111110000100] rn[19..16] rt[15..12] [1100] imm[7..0]*/ + kThumb2LdrRRI8Predec, // ldr(Imm,T4) rd,[rn,#-imm8] [111110000101] rn[19..16] rt[15..12] [1100] imm[7..0]*/ + kThumb2Cbnz, // cbnz rd,