diff options
325 files changed, 14797 insertions, 5858 deletions
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index 14edb716bf..478c63c9a5 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -93,8 +93,8 @@ endif # # Used to change the default GC. Valid values are CMS, SS, GSS. The default is CMS. # -ART_DEFAULT_GC_TYPE ?= CMS -ART_DEFAULT_GC_TYPE_CFLAGS := -DART_DEFAULT_GC_TYPE_IS_$(ART_DEFAULT_GC_TYPE) +art_default_gc_type ?= CMS +art_default_gc_type_cflags := -DART_DEFAULT_GC_TYPE_IS_$(art_default_gc_type) ifeq ($(ART_USE_PORTABLE_COMPILER),true) LLVM_ROOT_PATH := external/llvm @@ -102,6 +102,9 @@ ifeq ($(ART_USE_PORTABLE_COMPILER),true) -include $(LLVM_ROOT_PATH)/llvm.mk endif +ART_HOST_CFLAGS := +ART_TARGET_CFLAGS := + # Clang build support. # Host. @@ -131,6 +134,45 @@ define set-target-local-clang-vars endif) endef +ART_TARGET_CLANG_CFLAGS := +ART_TARGET_CLANG_CFLAGS_arm := +ART_TARGET_CLANG_CFLAGS_arm64 := +ART_TARGET_CLANG_CFLAGS_mips := +ART_TARGET_CLANG_CFLAGS_x86 := +ART_TARGET_CLANG_CFLAGS_x86_64 := + +# These are necessary for Clang ARM64 ART builds. TODO: remove. +ART_TARGET_CLANG_CFLAGS_arm64 += \ + -Wno-implicit-exception-spec-mismatch \ + -DNVALGRIND \ + -Wno-unused-value + +# FIXME: upstream LLVM has a vectorizer bug that needs to be fixed +ART_TARGET_CLANG_CFLAGS_arm64 += \ + -fno-vectorize + +# Colorize clang compiler warnings. +art_clang_cflags := -fcolor-diagnostics + +# Warn about thread safety violations with clang. +art_clang_cflags += -Wthread-safety + +# Warn if switch fallthroughs aren't annotated. +art_clang_cflags += -Wimplicit-fallthrough + +# Enable float equality warnings. +art_clang_cflags += -Wfloat-equal + +ifeq ($(ART_HOST_CLANG),true) + ART_HOST_CFLAGS += $(art_clang_cflags) +endif +ifeq ($(ART_TARGET_CLANG),true) + ART_TARGET_CFLAGS += $(art_clang_cflags) +endif + +# Clear local variable now its use has ended. +art_clang_cflags := + ART_CPP_EXTENSION := .cc ART_C_INCLUDES := \ @@ -141,6 +183,7 @@ ART_C_INCLUDES := \ external/zlib \ frameworks/compile/mclinker/include +# Base set of cflags used by all things ART. art_cflags := \ -fno-rtti \ -std=gnu++11 \ @@ -153,20 +196,8 @@ art_cflags := \ -Wstrict-aliasing \ -fstrict-aliasing \ -Wunreachable-code \ - -fvisibility=protected - -ART_TARGET_CLANG_CFLAGS := -ART_TARGET_CLANG_CFLAGS_arm := -ART_TARGET_CLANG_CFLAGS_arm64 := -ART_TARGET_CLANG_CFLAGS_mips := -ART_TARGET_CLANG_CFLAGS_x86 := -ART_TARGET_CLANG_CFLAGS_x86_64 := - -# These are necessary for Clang ARM64 ART builds. TODO: remove. -ART_TARGET_CLANG_CFLAGS_arm64 += \ - -Wno-implicit-exception-spec-mismatch \ - -DNVALGRIND \ - -Wno-unused-value + -fvisibility=protected \ + $(art_default_gc_type_cflags) ifeq ($(ART_SMALL_MODE),true) art_cflags += -DART_SMALL_MODE=1 @@ -176,14 +207,18 @@ ifeq ($(ART_SEA_IR_MODE),true) art_cflags += -DART_SEA_IR_MODE=1 endif +# Cflags for non-debug ART and ART tools. art_non_debug_cflags := \ -O3 -art_host_non_debug_cflags := \ - $(art_non_debug_cflags) +# Cflags for debug ART and ART tools. +art_debug_cflags := \ + -O2 \ + -DDYNAMIC_ANNOTATIONS_ENABLED=1 \ + -UNDEBUG -art_target_non_debug_cflags := \ - $(art_non_debug_cflags) +art_host_non_debug_cflags := $(art_non_debug_cflags) +art_target_non_debug_cflags := $(art_non_debug_cflags) ifeq ($(HOST_OS),linux) # Larger frame-size for host clang builds today @@ -191,26 +226,21 @@ ifeq ($(HOST_OS),linux) art_target_non_debug_cflags += -Wframe-larger-than=1728 endif -# FIXME: upstream LLVM has a vectorizer bug that needs to be fixed -ART_TARGET_CLANG_CFLAGS_arm64 += \ - -fno-vectorize - -art_debug_cflags := \ - -O2 \ - -DDYNAMIC_ANNOTATIONS_ENABLED=1 \ - -UNDEBUG - ifndef LIBART_IMG_HOST_BASE_ADDRESS $(error LIBART_IMG_HOST_BASE_ADDRESS unset) endif -ART_HOST_CFLAGS := $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS) +ART_HOST_CFLAGS += $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS) ART_HOST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=default -ART_HOST_CFLAGS += $(ART_DEFAULT_GC_TYPE_CFLAGS) ifndef LIBART_IMG_TARGET_BASE_ADDRESS $(error LIBART_IMG_TARGET_BASE_ADDRESS unset) endif -ART_TARGET_CFLAGS := $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS) +ART_TARGET_CFLAGS += $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS) + +ART_HOST_NON_DEBUG_CFLAGS := $(art_host_non_debug_cflags) +ART_TARGET_NON_DEBUG_CFLAGS := $(art_target_non_debug_cflags) +ART_HOST_DEBUG_CFLAGS := $(art_debug_cflags) +ART_TARGET_DEBUG_CFLAGS := $(art_debug_cflags) ifndef LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA=-0x1000000 @@ -230,25 +260,6 @@ endif ART_TARGET_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA) ART_TARGET_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA) -# Colorize clang compiler warnings. -art_clang_cflags := -fcolor-diagnostics - -# Warn if switch fallthroughs aren't annotated. -art_clang_cflags += -Wimplicit-fallthrough - -# Enable float equality warnings. -art_clang_cflags += -Wfloat-equal - -ifeq ($(ART_HOST_CLANG),true) - ART_HOST_CFLAGS += $(art_clang_cflags) -endif -ifeq ($(ART_TARGET_CLANG),true) - ART_TARGET_CFLAGS += $(art_clang_cflags) -endif - -art_clang_cflags := - -ART_TARGET_LDFLAGS := ifeq ($(TARGET_CPU_SMP),true) ART_TARGET_CFLAGS += -DANDROID_SMP=1 else @@ -260,60 +271,26 @@ else ART_TARGET_CFLAGS += -DANDROID_SMP=1 endif endif -ART_TARGET_CFLAGS += $(ART_DEFAULT_GC_TYPE_CFLAGS) - -# DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES is set in ../build/core/dex_preopt.mk based on -# the TARGET_CPU_VARIANT -ifeq ($(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES),) -$(error Required DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES is not set) -endif -ART_TARGET_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) - -# Enable thread-safety for GCC 4.6, and clang, but not for GCC 4.7 or later where this feature was -# removed. Warn when -Wthread-safety is not used. -ifneq ($(filter 4.6 4.6.%, $(TARGET_GCC_VERSION)),) - ART_TARGET_CFLAGS += -Wthread-safety -else - # FIXME: add -Wthread-safety when the problem is fixed - ifeq ($(ART_TARGET_CLANG),true) - ART_TARGET_CFLAGS += - else - # Warn if -Wthread-safety is not supported and 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 -endif -# We compile with GCC 4.6 or clang on the host, both of which support -Wthread-safety. -ART_HOST_CFLAGS += -Wthread-safety # To use oprofile_android --callgraph, uncomment this and recompile with "mmm art -B -j16" # ART_TARGET_CFLAGS += -fno-omit-frame-pointer -marm -mapcs -# Addition CPU specific CFLAGS. -ifeq ($(TARGET_ARCH),arm) - ifneq ($(filter cortex-a15, $(TARGET_CPU_VARIANT)),) - # Fake a ARM feature for LPAE support. - ART_TARGET_CFLAGS += -D__ARM_FEATURE_LPAE=1 - endif -endif - -ART_HOST_NON_DEBUG_CFLAGS := $(art_host_non_debug_cflags) -ART_TARGET_NON_DEBUG_CFLAGS := $(art_target_non_debug_cflags) - -# TODO: move -fkeep-inline-functions to art_debug_cflags when target gcc > 4.4 (and -lsupc++) -ART_HOST_DEBUG_CFLAGS := $(art_debug_cflags) -fkeep-inline-functions -ART_HOST_DEBUG_LDLIBS := -lsupc++ +# Clear locals now they've served their purpose. +art_cflags := +art_debug_cflags := +art_non_debug_cflags := +art_host_non_debug_cflags := +art_target_non_debug_cflags := +art_default_gc_type := +art_default_gc_type_cflags := -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)) +ART_HOST_LDLIBS := +ifneq ($(ART_HOST_CLANG),true) + # GCC lacks libc++ assumed atomic operations, grab via libatomic. + ART_HOST_LDLIBS += -latomic endif -ART_TARGET_DEBUG_CFLAGS := $(art_debug_cflags) +ART_TARGET_LDFLAGS := # $(1): ndebug_or_debug define set-target-local-cflags-vars @@ -337,6 +314,7 @@ define set-target-local-cflags-vars art_target_cflags_ndebug_or_debug := endef +# Support for disabling certain builds. ART_BUILD_TARGET := false ART_BUILD_HOST := false ART_BUILD_NDEBUG := false @@ -358,12 +336,4 @@ ifeq ($(ART_BUILD_HOST_DEBUG),true) ART_BUILD_DEBUG := true endif -# Clear locally defined variables that aren't necessary in the rest of the build system. -ART_DEFAULT_GC_TYPE := -ART_DEFAULT_GC_TYPE_CFLAGS := -art_cflags := -art_target_non_debug_cflags := -art_host_non_debug_cflags := -art_non_debug_cflags := - endif # ANDROID_COMMON_BUILD_MK diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk index ee72706b91..3e76d91aed 100644 --- a/build/Android.common_test.mk +++ b/build/Android.common_test.mk @@ -90,6 +90,12 @@ ART_TEST_RUN_TEST_RELOCATE_NO_PATCHOAT ?= $(ART_TEST_FULL) # Do you want run-tests without a dex2oat? ART_TEST_RUN_TEST_NO_DEX2OAT ?= $(ART_TEST_FULL) +# Do you want run-tests with libartd.so? +ART_TEST_RUN_TEST_DEBUG ?= true + +# Do you want run-tests with libart.so? +ART_TEST_RUN_TEST_NDEBUG ?= $(ART_TEST_FULL) + # Do you want failed tests to have their artifacts cleaned up? ART_TEST_RUN_TEST_ALWAYS_CLEAN ?= true diff --git a/build/Android.executable.mk b/build/Android.executable.mk index d887acd1cd..86f445faa2 100644 --- a/build/Android.executable.mk +++ b/build/Android.executable.mk @@ -57,6 +57,7 @@ define build-art-executable LOCAL_SRC_FILES := $$(art_source) LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime $$(art_c_includes) LOCAL_SHARED_LIBRARIES += $$(art_shared_libraries) + LOCAL_WHOLE_STATIC_LIBRARIES += libsigchain ifeq ($$(art_ndebug_or_debug),ndebug) LOCAL_MODULE := $$(art_executable) @@ -65,18 +66,25 @@ define build-art-executable endif LOCAL_CFLAGS := $(ART_EXECUTABLES_CFLAGS) + # Mac OS linker doesn't understand --export-dynamic/--version-script. + ifneq ($$(HOST_OS)-$$(art_target_or_host),darwin-host) + LOCAL_LDFLAGS := -Wl,--version-script,art/sigchainlib/version-script.txt -Wl,--export-dynamic + endif + ifeq ($$(art_target_or_host),target) $(call set-target-local-clang-vars) $(call set-target-local-cflags-vars,$(6)) + LOCAL_SHARED_LIBRARIES += libdl else # host LOCAL_CLANG := $(ART_HOST_CLANG) + LOCAL_LDLIBS := $(ART_HOST_LDLIBS) 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 - LOCAL_LDLIBS += -lpthread + LOCAL_LDLIBS += -lpthread -ldl endif ifeq ($$(art_ndebug_or_debug),ndebug) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index af43a3c6d6..db7257a94d 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -141,7 +141,7 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ compiler/oat_test.cc \ compiler/optimizing/codegen_test.cc \ compiler/optimizing/dead_code_elimination_test.cc \ - compiler/optimizing/constant_propagation_test.cc \ + compiler/optimizing/constant_folding_test.cc \ compiler/optimizing/dominator_test.cc \ compiler/optimizing/find_loops_test.cc \ compiler/optimizing/graph_checker_test.cc \ @@ -217,10 +217,7 @@ LOCAL_SRC_FILES := runtime/common_runtime_test.cc compiler/common_compiler_test. LOCAL_C_INCLUDES := $(ART_C_INCLUDES) art/runtime art/compiler LOCAL_SHARED_LIBRARIES := libartd libartd-compiler LOCAL_STATIC_LIBRARIES := libcutils -ifneq ($(WITHOUT_HOST_CLANG),true) - # GCC host compiled tests fail with this linked, presumably due to destructors that run. - LOCAL_STATIC_LIBRARIES += libgtest_libc++_host -endif +LOCAL_STATIC_LIBRARIES += libgtest_libc++_host LOCAL_LDLIBS += -ldl -lpthread LOCAL_MULTILIB := both LOCAL_CLANG := $(ART_HOST_CLANG) @@ -264,7 +261,7 @@ $$(gtest_rule): test-art-target-sync && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID /tmp/ \ && $$(call ART_TEST_PASSED,$$@)) \ || $$(call ART_TEST_FAILED,$$@)) - $(hide) rm /tmp/$$@-$$$$PPID + $(hide) rm -f /tmp/$$@-$$$$PPID ART_TEST_TARGET_GTEST$($(2)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += $$(gtest_rule) ART_TEST_TARGET_GTEST_RULES += $$(gtest_rule) @@ -287,7 +284,7 @@ define define-art-gtest-rule-host .PHONY: $$(gtest_rule) $$(gtest_rule): $$(gtest_exe) $$(ART_GTEST_$(1)_HOST_DEPS) $(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_TEST_HOST_GTEST_$(file)_DEX)) $$(gtest_deps) - $(hide) ($$(call ART_TEST_SKIP,$$@) && LD_PRELOAD=libsigchain$$(ART_HOST_SHLIB_EXTENSION) $$< && $$(call ART_TEST_PASSED,$$@)) \ + $(hide) ($$(call ART_TEST_SKIP,$$@) && $$< && $$(call ART_TEST_PASSED,$$@)) \ || $$(call ART_TEST_FAILED,$$@) ART_TEST_HOST_GTEST$$($(2)ART_PHONY_TEST_HOST_SUFFIX)_RULES += $$(gtest_rule) @@ -338,6 +335,7 @@ define define-art-gtest LOCAL_SRC_FILES := $$(art_gtest_filename) LOCAL_C_INCLUDES += $$(ART_C_INCLUDES) art/runtime $$(art_gtest_extra_c_includes) LOCAL_SHARED_LIBRARIES += libartd $$(art_gtest_extra_shared_libraries) libart-gtest + LOCAL_WHOLE_STATIC_LIBRARIES += libsigchain LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.gtest.mk @@ -377,7 +375,7 @@ test-art-target-gtest-$$(art_gtest_name): $$(ART_TEST_TARGET_GTEST_$$(art_gtest_ LOCAL_CFLAGS += $$(ART_HOST_CFLAGS) $$(ART_HOST_DEBUG_CFLAGS) LOCAL_SHARED_LIBRARIES += libicuuc-host libicui18n-host libnativehelper libz-host LOCAL_STATIC_LIBRARIES += libcutils libvixl - LOCAL_LDLIBS += -lpthread -ldl + LOCAL_LDLIBS := $(ART_HOST_LDLIBS) -lpthread -ldl LOCAL_IS_HOST_MODULE := true LOCAL_MULTILIB := both LOCAL_MODULE_STEM_32 := $$(art_gtest_name)32 diff --git a/build/Android.oat.mk b/build/Android.oat.mk index 844f58ea47..2becbb800f 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -51,7 +51,7 @@ $(eval $(call create-core-oat-host-rules,2ND_)) endif define create-core-oat-target-rules -$$($(1)TARGET_CORE_IMG_OUT): $$($(1)TARGET_CORE_DEX_FILES) $$(DEX2OAT_DEPENDENCY) +$$($(1)TARGET_CORE_IMG_OUT): $$(TARGET_CORE_DEX_FILES) $$(DEX2OAT_DEPENDENCY) @echo "target dex2oat: $$@ ($$?)" @mkdir -p $$(dir $$@) $$(hide) $$(DEX2OAT) $$(DEX2OAT_FLAGS) --runtime-arg -Xms$(DEX2OAT_XMS) --runtime-arg -Xmx$(DEX2OAT_XMX) \ diff --git a/compiler/Android.mk b/compiler/Android.mk index edc5bd0f53..172c96cfde 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -88,9 +88,10 @@ LIBART_COMPILER_SRC_FILES := \ optimizing/builder.cc \ optimizing/code_generator.cc \ optimizing/code_generator_arm.cc \ + optimizing/code_generator_arm64.cc \ optimizing/code_generator_x86.cc \ optimizing/code_generator_x86_64.cc \ - optimizing/constant_propagation.cc \ + optimizing/constant_folding.cc \ optimizing/dead_code_elimination.cc \ optimizing/graph_checker.cc \ optimizing/graph_visualizer.cc \ @@ -98,6 +99,7 @@ LIBART_COMPILER_SRC_FILES := \ optimizing/instruction_simplifier.cc \ optimizing/locations.cc \ optimizing/nodes.cc \ + optimizing/optimization.cc \ optimizing/optimizing_compiler.cc \ optimizing/parallel_move_resolver.cc \ optimizing/prepare_for_register_allocation.cc \ @@ -223,6 +225,7 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT else # host LOCAL_CLANG := $(ART_HOST_CLANG) LOCAL_CFLAGS += $(ART_HOST_CFLAGS) + LOCAL_LDLIBS := $(ART_HOST_LDLIBS) ifeq ($$(art_ndebug_or_debug),debug) LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS) else diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index e3eb9e9915..359d6af57d 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -16,18 +16,12 @@ #include "common_compiler_test.h" -#if defined(__arm__) -#include <sys/ucontext.h> -#endif -#include <fstream> - #include "class_linker.h" #include "compiled_method.h" #include "dex/quick_compiler_callbacks.h" #include "dex/verification_results.h" #include "dex/quick/dex_file_to_method_inliner_map.h" #include "driver/compiler_driver.h" -#include "entrypoints/entrypoint_utils.h" #include "interpreter/interpreter.h" #include "mirror/art_method.h" #include "mirror/dex_cache.h" @@ -38,128 +32,9 @@ namespace art { -// Normally the ClassLinker supplies this. -extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*); - -#if defined(__arm__) -// A signal handler called when have an illegal instruction. We record the fact in -// a global boolean and then increment the PC in the signal context to return to -// the next instruction. We know the instruction is an sdiv (4 bytes long). -static void baddivideinst(int signo, siginfo *si, void *data) { - UNUSED(signo); - UNUSED(si); - struct ucontext *uc = (struct ucontext *)data; - struct sigcontext *sc = &uc->uc_mcontext; - sc->arm_r0 = 0; // set R0 to #0 to signal error - sc->arm_pc += 4; // skip offending instruction -} - -// This is in arch/arm/arm_sdiv.S. It does the following: -// mov r1,#1 -// sdiv r0,r1,r1 -// bx lr -// -// the result will be the value 1 if sdiv is supported. If it is not supported -// a SIGILL signal will be raised and the signal handler (baddivideinst) called. -// The signal handler sets r0 to #0 and then increments pc beyond the failed instruction. -// Thus if the instruction is not supported, the result of this function will be #0 - -extern "C" bool CheckForARMSDIVInstruction(); - -static InstructionSetFeatures GuessInstructionFeatures() { - InstructionSetFeatures f; - - // Uncomment this for processing of /proc/cpuinfo. - if (false) { - // Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that - // the kernel puts the appropriate feature flags in here. Sometimes it doesn't. - std::ifstream in("/proc/cpuinfo"); - if (in) { - while (!in.eof()) { - std::string line; - std::getline(in, line); - if (!in.eof()) { - if (line.find("Features") != std::string::npos) { - if (line.find("idivt") != std::string::npos) { - f.SetHasDivideInstruction(true); - } - } - } - in.close(); - } - } else { - LOG(INFO) << "Failed to open /proc/cpuinfo"; - } - } - - // See if have a sdiv instruction. Register a signal handler and try to execute - // an sdiv instruction. If we get a SIGILL then it's not supported. We can't use - // the /proc/cpuinfo method for this because Krait devices don't always put the idivt - // feature in the list. - struct sigaction sa, osa; - sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO; - sa.sa_sigaction = baddivideinst; - sigaction(SIGILL, &sa, &osa); - - if (CheckForARMSDIVInstruction()) { - f.SetHasDivideInstruction(true); - } - - // Restore the signal handler. - sigaction(SIGILL, &osa, nullptr); - - // Other feature guesses in here. - return f; -} -#endif - -// Given a set of instruction features from the build, parse it. The -// input 'str' is a comma separated list of feature names. Parse it and -// return the InstructionSetFeatures object. -static InstructionSetFeatures ParseFeatureList(std::string str) { - InstructionSetFeatures result; - typedef std::vector<std::string> FeatureList; - FeatureList features; - Split(str, ',', features); - for (FeatureList::iterator i = features.begin(); i != features.end(); i++) { - std::string feature = Trim(*i); - if (feature == "default") { - // Nothing to do. - } else if (feature == "div") { - // Supports divide instruction. - result.SetHasDivideInstruction(true); - } else if (feature == "nodiv") { - // Turn off support for divide instruction. - result.SetHasDivideInstruction(false); - } else { - LOG(FATAL) << "Unknown instruction set feature: '" << feature << "'"; - } - } - // Others... - return result; -} - CommonCompilerTest::CommonCompilerTest() {} CommonCompilerTest::~CommonCompilerTest() {} -OatFile::OatMethod CommonCompilerTest::CreateOatMethod(const void* code, const uint8_t* gc_map) { - CHECK(code != nullptr); - const uint8_t* base; - uint32_t code_offset, gc_map_offset; - if (gc_map == nullptr) { - base = reinterpret_cast<const uint8_t*>(code); // Base of data points at code. - base -= sizeof(void*); // Move backward so that code_offset != 0. - code_offset = sizeof(void*); - gc_map_offset = 0; - } else { - // TODO: 64bit support. - base = nullptr; // Base of data in oat file, ie 0. - code_offset = PointerToLowMemUInt32(code); - gc_map_offset = PointerToLowMemUInt32(gc_map); - } - return OatFile::OatMethod(base, code_offset, gc_map_offset); -} - void CommonCompilerTest::MakeExecutable(mirror::ArtMethod* method) { CHECK(method != nullptr); @@ -174,7 +49,8 @@ void CommonCompilerTest::MakeExecutable(mirror::ArtMethod* method) { if (compiled_method != nullptr) { const std::vector<uint8_t>* code = compiled_method->GetQuickCode(); const void* code_ptr; - if (code != nullptr) { + bool is_portable = (code == nullptr); + if (!is_portable) { uint32_t code_size = code->size(); CHECK_NE(0u, code_size); const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable(); @@ -210,33 +86,11 @@ void CommonCompilerTest::MakeExecutable(mirror::ArtMethod* method) { const void* method_code = CompiledMethod::CodePointer(code_ptr, compiled_method->GetInstructionSet()); LOG(INFO) << "MakeExecutable " << PrettyMethod(method) << " code=" << method_code; - OatFile::OatMethod oat_method = CreateOatMethod(method_code, nullptr); - oat_method.LinkMethod(method); - method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); + class_linker_->SetEntryPointsToCompiledCode(method, method_code, is_portable); } else { // No code? You must mean to go into the interpreter. // Or the generic JNI... - if (!method->IsNative()) { - const void* method_code = kUsePortableCompiler ? GetPortableToInterpreterBridge() - : GetQuickToInterpreterBridge(); - OatFile::OatMethod oat_method = CreateOatMethod(method_code, nullptr); - oat_method.LinkMethod(method); - method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge); - } else { - const void* method_code = reinterpret_cast<void*>(art_quick_generic_jni_trampoline); - - OatFile::OatMethod oat_method = CreateOatMethod(method_code, nullptr); - oat_method.LinkMethod(method); - method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); - } - } - // Create bridges to transition between different kinds of compiled bridge. - if (method->GetEntryPointFromPortableCompiledCode() == nullptr) { - method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge()); - } else { - CHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr); - method->SetEntryPointFromQuickCompiledCode(GetQuickToPortableBridge()); - method->SetIsPortableCompiled(); + class_linker_->SetEntryPointsToInterpreter(method); } } @@ -282,19 +136,9 @@ void CommonCompilerTest::SetUp() { { ScopedObjectAccess soa(Thread::Current()); - InstructionSet instruction_set = kRuntimeISA; - + const InstructionSet instruction_set = kRuntimeISA; // Take the default set of instruction features from the build. - InstructionSetFeatures instruction_set_features = - ParseFeatureList(Runtime::GetDefaultInstructionSetFeatures()); - -#if defined(__arm__) - InstructionSetFeatures runtime_features = GuessInstructionFeatures(); - - // for ARM, do a runtime check to make sure that the features we are passed from - // the build match the features we actually determine at runtime. - ASSERT_LE(instruction_set_features, runtime_features); -#endif + instruction_set_features_.reset(InstructionSetFeatures::FromCppDefines()); runtime_->SetInstructionSet(instruction_set); for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { @@ -306,16 +150,15 @@ void CommonCompilerTest::SetUp() { } // TODO: make selectable - Compiler::Kind compiler_kind - = (kUsePortableCompiler) ? Compiler::kPortable : Compiler::kQuick; + Compiler::Kind compiler_kind = kUsePortableCompiler ? Compiler::kPortable : Compiler::kQuick; timer_.reset(new CumulativeLogger("Compilation times")); compiler_driver_.reset(new CompilerDriver(compiler_options_.get(), verification_results_.get(), method_inliner_map_.get(), compiler_kind, instruction_set, - instruction_set_features, + instruction_set_features_.get(), true, new std::set<std::string>, - 2, true, true, timer_.get())); + 2, true, true, timer_.get(), "")); } // We typically don't generate an image in unit tests, disable this optimization by default. compiler_driver_->SetSupportBootImageFixup(false); @@ -397,6 +240,7 @@ void CommonCompilerTest::ReserveImageSpace() { // Reserve where the image will be loaded up front so that other parts of test set up don't // accidentally end up colliding with the fixed memory address when we need to load the image. std::string error_msg; + MemMap::Init(); image_reservation_.reset(MemMap::MapAnonymous("image reservation", reinterpret_cast<uint8_t*>(ART_BASE_ADDRESS), (size_t)100 * 1024 * 1024, // 100MB diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index df06b71c7d..20b750c55b 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -81,6 +81,8 @@ class CommonCompilerTest : public CommonRuntimeTest { std::unique_ptr<CompilerCallbacks> callbacks_; std::unique_ptr<CompilerDriver> compiler_driver_; std::unique_ptr<CumulativeLogger> timer_; + std::unique_ptr<const InstructionSetFeatures> instruction_set_features_; + private: std::unique_ptr<MemMap> image_reservation_; diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h index fce23bc2fb..764a4cf08e 100644 --- a/compiler/dex/bb_optimizations.h +++ b/compiler/dex/bb_optimizations.h @@ -143,7 +143,7 @@ class CodeLayout : public PassME { class NullCheckElimination : public PassME { public: NullCheckElimination() - : PassME("NCE", kRepeatingTopologicalSortTraversal, "3_post_nce_cfg") { + : PassME("NCE", kRepeatingPreOrderDFSTraversal, "3_post_nce_cfg") { } bool Gate(const PassDataHolder* data) const { @@ -178,7 +178,7 @@ class NullCheckElimination : public PassME { class TypeInference : public PassME { public: TypeInference() - : PassME("TypeInference", kRepeatingTopologicalSortTraversal, "4_post_type_cfg") { + : PassME("TypeInference", kRepeatingPreOrderDFSTraversal, "4_post_type_cfg") { } bool Worker(PassDataHolder* data) const { @@ -195,7 +195,7 @@ class TypeInference : public PassME { class ClassInitCheckElimination : public PassME { public: ClassInitCheckElimination() - : PassME("ClInitCheckElimination", kLoopRepeatingTopologicalSortTraversal) { + : PassME("ClInitCheckElimination", kRepeatingPreOrderDFSTraversal) { } bool Gate(const PassDataHolder* data) const { @@ -271,7 +271,8 @@ class BBCombine : public PassME { DCHECK(data != nullptr); CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit; DCHECK(c_unit != nullptr); - return ((c_unit->disable_opt & (1 << kSuppressExceptionEdges)) != 0); + return c_unit->mir_graph->HasTryCatchBlocks() || + ((c_unit->disable_opt & (1 << kSuppressExceptionEdges)) != 0); } bool Worker(PassDataHolder* data) const; diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index e4003bf088..78da420339 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -311,7 +311,8 @@ enum MIROptimizationFlagPositions { kMIRCallee, // Instruction is inlined from callee. kMIRIgnoreSuspendCheck, kMIRDup, - kMIRMark, // Temporary node mark. + kMIRMark, // Temporary node mark can be used by + // opt passes for their private needs. kMIRStoreNonTemporal, kMIRLastMIRFlag, }; diff --git a/compiler/dex/compiler_ir.cc b/compiler/dex/compiler_ir.cc index 909c995c32..a2b3fe432f 100644 --- a/compiler/dex/compiler_ir.cc +++ b/compiler/dex/compiler_ir.cc @@ -16,6 +16,7 @@ #include "compiler_ir.h" +#include "base/dumpable.h" #include "backend.h" #include "frontend.h" #include "mir_graph.h" diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h index 37e3a7a592..34585c1f87 100644 --- a/compiler/dex/compiler_ir.h +++ b/compiler/dex/compiler_ir.h @@ -68,7 +68,7 @@ struct CompilationUnit { InstructionSet instruction_set; bool target64; - InstructionSetFeatures GetInstructionSetFeatures() { + const InstructionSetFeatures* GetInstructionSetFeatures() { return compiler_driver->GetInstructionSetFeatures(); } diff --git a/compiler/dex/dataflow_iterator-inl.h b/compiler/dex/dataflow_iterator-inl.h index 89f2b5c692..6e25db6f04 100644 --- a/compiler/dex/dataflow_iterator-inl.h +++ b/compiler/dex/dataflow_iterator-inl.h @@ -115,6 +115,30 @@ inline BasicBlock* AllNodesIterator::Next(bool had_change) { return res; } +inline BasicBlock* TopologicalSortIterator::Next(bool had_change) { + // Update changed: if had_changed is true, we remember it for the whole iteration. + changed_ |= had_change; + + while (loop_head_stack_->size() != 0u && + (*loop_ends_)[loop_head_stack_->back().first] == idx_) { + loop_head_stack_->pop_back(); + } + + if (idx_ == end_idx_) { + return nullptr; + } + + // Get next block and return it. + BasicBlockId idx = idx_; + idx_ += 1; + BasicBlock* bb = mir_graph_->GetBasicBlock((*block_id_list_)[idx]); + DCHECK(bb != nullptr); + if ((*loop_ends_)[idx] != 0u) { + loop_head_stack_->push_back(std::make_pair(idx, false)); // Not recalculating. + } + return bb; +} + inline BasicBlock* LoopRepeatingTopologicalSortIterator::Next(bool had_change) { if (idx_ != 0) { // Mark last processed block visited. diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h index 188f1d9fff..9f17a3ec85 100644 --- a/compiler/dex/dataflow_iterator.h +++ b/compiler/dex/dataflow_iterator.h @@ -333,7 +333,9 @@ namespace art { * @param mir_graph The MIRGraph considered. */ explicit TopologicalSortIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder().size()) { + : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder().size()), + loop_ends_(&mir_graph->GetTopologicalSortOrderLoopEnds()), + loop_head_stack_(mir_graph_->GetTopologicalSortOrderLoopHeadStack()) { // Extra setup for TopologicalSortIterator. idx_ = start_idx_; block_id_list_ = &mir_graph->GetTopologicalSortOrder(); @@ -344,44 +346,11 @@ namespace art { * @param had_change did the user of the iteration change the previous BasicBlock. * @return the next BasicBlock following the iteration order, 0 if finished. */ - virtual BasicBlock* Next(bool had_change = false) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - return ForwardSingleNext(); - } - }; - - /** - * @class RepeatingTopologicalSortIterator - * @brief Used to perform a Topological Sort Iteration of a MIRGraph. - * @details If there is a change during an iteration, the iteration starts over at the end of the - * iteration. - */ - class RepeatingTopologicalSortIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit RepeatingTopologicalSortIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder().size()) { - // Extra setup for RepeatingTopologicalSortIterator. - idx_ = start_idx_; - block_id_list_ = &mir_graph->GetTopologicalSortOrder(); - } + virtual BasicBlock* Next(bool had_change = false) OVERRIDE; - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - return ForwardRepeatNext(); - } + private: + const ArenaVector<BasicBlockId>* const loop_ends_; + ArenaVector<std::pair<uint16_t, bool>>* const loop_head_stack_; }; /** diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index 07f30334f2..3dc5655919 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -19,6 +19,7 @@ #include <cstdint> #include "backend.h" +#include "base/dumpable.h" #include "compiler.h" #include "compiler_internals.h" #include "driver/compiler_driver.h" @@ -41,6 +42,7 @@ static uint32_t kCompilerOptimizerDisableFlags = 0 | // Disable specific optimi // (1 << kNullCheckElimination) | // (1 << kClassInitCheckElimination) | // (1 << kGlobalValueNumbering) | + // (1 << kLocalValueNumbering) | // (1 << kPromoteRegs) | // (1 << kTrackLiveTemps) | // (1 << kSafeOptimizations) | @@ -133,15 +135,8 @@ static CompiledMethod* CompileMethod(CompilerDriver& driver, (cu.enable_debug & (1 << kDebugVerbose)); } - if (gVerboseMethods.size() != 0) { - cu.verbose = false; - for (size_t i = 0; i < gVerboseMethods.size(); ++i) { - if (PrettyMethod(method_idx, dex_file).find(gVerboseMethods[i]) - != std::string::npos) { - cu.verbose = true; - break; - } - } + if (driver.GetCompilerOptions().HasVerboseMethods()) { + cu.verbose = driver.GetCompilerOptions().IsVerboseMethod(PrettyMethod(method_idx, dex_file)); } if (cu.verbose) { diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h index 51b6d68dcc..bed3b97304 100644 --- a/compiler/dex/frontend.h +++ b/compiler/dex/frontend.h @@ -41,6 +41,7 @@ enum opt_control_vector { kNullCheckElimination, kClassInitCheckElimination, kGlobalValueNumbering, + kLocalValueNumbering, kPromoteRegs, kTrackLiveTemps, kSafeOptimizations, diff --git a/compiler/dex/global_value_numbering.cc b/compiler/dex/global_value_numbering.cc index af57529a08..f0f7a7051b 100644 --- a/compiler/dex/global_value_numbering.cc +++ b/compiler/dex/global_value_numbering.cc @@ -20,14 +20,16 @@ namespace art { -GlobalValueNumbering::GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAllocator* allocator) +GlobalValueNumbering::GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAllocator* allocator, + Mode mode) : cu_(cu), mir_graph_(cu->mir_graph.get()), allocator_(allocator), bbs_processed_(0u), max_bbs_to_process_(kMaxBbsToProcessMultiplyFactor * mir_graph_->GetNumReachableBlocks()), last_value_(0u), - modifications_allowed_(false), + modifications_allowed_(true), + mode_(mode), global_value_map_(std::less<uint64_t>(), allocator->Adapter()), field_index_map_(FieldReferenceComparator(), allocator->Adapter()), field_index_reverse_map_(allocator->Adapter()), @@ -48,19 +50,19 @@ LocalValueNumbering* GlobalValueNumbering::PrepareBasicBlock(BasicBlock* bb, if (UNLIKELY(!Good())) { return nullptr; } - if (UNLIKELY(bb->data_flow_info == nullptr)) { - return nullptr; - } - if (UNLIKELY(bb->block_type == kExitBlock)) { + if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) { DCHECK(bb->first_mir_insn == nullptr); return nullptr; } - if (UNLIKELY(bbs_processed_ == max_bbs_to_process_)) { + if (mode_ == kModeGvn && UNLIKELY(bbs_processed_ == max_bbs_to_process_)) { // If we're still trying to converge, stop now. Otherwise, proceed to apply optimizations. - if (!modifications_allowed_) { - last_value_ = kNoValue; // Make bad. - return nullptr; - } + last_value_ = kNoValue; // Make bad. + return nullptr; + } + if (mode_ == kModeGvnPostProcessing && + mir_graph_->GetTopologicalSortOrderLoopHeadStack()->empty()) { + // Modifications outside loops are performed during the main phase. + return nullptr; } if (allocator == nullptr) { allocator = allocator_; @@ -74,6 +76,7 @@ LocalValueNumbering* GlobalValueNumbering::PrepareBasicBlock(BasicBlock* bb, uint16_t value_name = work_lvn_->GetSRegValueName(this_reg); work_lvn_->SetValueNameNullChecked(value_name); } + DCHECK(bb->first_mir_insn == nullptr); // modifications_allowed_ is irrelevant. } else { // To avoid repeated allocation on the ArenaStack, reuse a single vector kept as a member. DCHECK(merge_lvns_.empty()); @@ -83,19 +86,18 @@ LocalValueNumbering* GlobalValueNumbering::PrepareBasicBlock(BasicBlock* bb, // topological order, or we're recalculating a loop head and need to merge all incoming // LVNs. When we're not at a loop head (including having an empty loop head stack) all // predecessors should be preceding blocks and we shall merge all of them anyway. - // - // If we're running the modification phase of the full GVN, the loop head stack will be - // empty and we need to merge all incoming LVNs. If we're running just a simple LVN, - // the loop head stack will also be empty and there will be nothing to merge anyway. bool use_all_predecessors = true; uint16_t loop_head_idx = 0u; // Used only if !use_all_predecessors. - if (mir_graph_->GetTopologicalSortOrderLoopHeadStack()->size() != 0) { + if (mode_ == kModeGvn && mir_graph_->GetTopologicalSortOrderLoopHeadStack()->size() != 0) { // Full GVN inside a loop, see if we're at the loop head for the first time. + modifications_allowed_ = false; auto top = mir_graph_->GetTopologicalSortOrderLoopHeadStack()->back(); loop_head_idx = top.first; bool recalculating = top.second; use_all_predecessors = recalculating || loop_head_idx != mir_graph_->GetTopologicalSortOrderIndexes()[bb->id]; + } else { + modifications_allowed_ = true; } for (BasicBlockId pred_id : bb->predecessors) { DCHECK_NE(pred_id, NullBasicBlockId); diff --git a/compiler/dex/global_value_numbering.h b/compiler/dex/global_value_numbering.h index 27183bfe4c..df554cdade 100644 --- a/compiler/dex/global_value_numbering.h +++ b/compiler/dex/global_value_numbering.h @@ -28,7 +28,18 @@ class MirFieldInfo; class GlobalValueNumbering { public: - GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAllocator* allocator); + enum Mode { + kModeGvn, + kModeGvnPostProcessing, + kModeLvn + }; + + static bool Skip(CompilationUnit* cu) { + return (cu->disable_opt & (1u << kGlobalValueNumbering)) != 0u || + cu->mir_graph->GetMaxNestedLoops() > kMaxAllowedNestedLoops; + } + + GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAllocator* allocator, Mode mode); ~GlobalValueNumbering(); // Prepare LVN for the basic block. @@ -44,13 +55,13 @@ class GlobalValueNumbering { } // Allow modifications. - void AllowModifications() { + void StartPostProcessing() { DCHECK(Good()); - modifications_allowed_ = true; + DCHECK_EQ(mode_, kModeGvn); + mode_ = kModeGvnPostProcessing; } bool CanModify() const { - // TODO: DCHECK(Good()), see AllowModifications() and NewValueName(). return modifications_allowed_ && Good(); } @@ -67,8 +78,7 @@ class GlobalValueNumbering { // Allocate a new value name. uint16_t NewValueName() { - // TODO: No new values should be needed once we allow modifications. - // DCHECK(!modifications_allowed_); + DCHECK_NE(mode_, kModeGvnPostProcessing); ++last_value_; return last_value_; } @@ -208,6 +218,9 @@ class GlobalValueNumbering { MIRGraph* mir_graph_; ScopedArenaAllocator* const allocator_; + // The maximum number of nested loops that we accept for GVN. + static constexpr size_t kMaxAllowedNestedLoops = 6u; + // The number of BBs that we need to process grows exponentially with the number // of nested loops. Don't allow excessive processing for too many nested loops or // otherwise expensive methods. @@ -225,6 +238,9 @@ class GlobalValueNumbering { // LVN once for each BasicBlock. bool modifications_allowed_; + // Specifies the mode of operation. + Mode mode_; + ValueMap global_value_map_; FieldIndexMap field_index_map_; ScopedArenaVector<const FieldIndexMap::value_type*> field_index_reverse_map_; diff --git a/compiler/dex/global_value_numbering_test.cc b/compiler/dex/global_value_numbering_test.cc index 1d9920d24d..82a11a55b3 100644 --- a/compiler/dex/global_value_numbering_test.cc +++ b/compiler/dex/global_value_numbering_test.cc @@ -284,8 +284,8 @@ class GlobalValueNumberingTest : public testing::Test { cu_.mir_graph->ComputeTopologicalSortOrder(); cu_.mir_graph->SSATransformationEnd(); ASSERT_TRUE(gvn_ == nullptr); - gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get())); - ASSERT_FALSE(gvn_->CanModify()); + gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get(), + GlobalValueNumbering::kModeGvn)); value_names_.resize(mir_count_, 0xffffu); IteratorType iterator(cu_.mir_graph.get()); bool change = false; @@ -304,8 +304,7 @@ class GlobalValueNumberingTest : public testing::Test { void PerformGVNCodeModifications() { ASSERT_TRUE(gvn_ != nullptr); ASSERT_TRUE(gvn_->Good()); - ASSERT_FALSE(gvn_->CanModify()); - gvn_->AllowModifications(); + gvn_->StartPostProcessing(); TopologicalSortIterator iterator(cu_.mir_graph.get()); for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) { LocalValueNumbering* lvn = gvn_->PrepareBasicBlock(bb); diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc index eb9891606c..8b7ae20b95 100644 --- a/compiler/dex/local_value_numbering.cc +++ b/compiler/dex/local_value_numbering.cc @@ -1413,8 +1413,8 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { case Instruction::MONITOR_EXIT: HandleNullCheck(mir, GetOperandValue(mir->ssa_rep->uses[0])); // If we're running GVN and CanModify(), uneliminated null check indicates bytecode error. - if ((gvn_->GetCompilationUnit()->disable_opt & (1u << kGlobalValueNumbering)) == 0u && - gvn_->CanModify() && (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0) { + if ((mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0 && + gvn_->work_lvn_ != nullptr && gvn_->CanModify()) { LOG(WARNING) << "Bytecode error: MONITOR_EXIT is still null checked at 0x" << std::hex << mir->offset << " in " << PrettyMethod(gvn_->cu_->method_idx, *gvn_->cu_->dex_file); } @@ -1448,6 +1448,10 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { } break; + case kMirOpNullCheck: + HandleNullCheck(mir, GetOperandValue(mir->ssa_rep->uses[0])); + break; + case Instruction::INVOKE_DIRECT: case Instruction::INVOKE_DIRECT_RANGE: case Instruction::INVOKE_VIRTUAL: diff --git a/compiler/dex/local_value_numbering_test.cc b/compiler/dex/local_value_numbering_test.cc index 067bea28b9..33d6c14ba7 100644 --- a/compiler/dex/local_value_numbering_test.cc +++ b/compiler/dex/local_value_numbering_test.cc @@ -195,9 +195,9 @@ class LocalValueNumberingTest : public testing::Test { value_names_() { cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack)); - gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get())); + gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get(), + GlobalValueNumbering::kModeLvn)); lvn_.reset(new (allocator_.get()) LocalValueNumbering(gvn_.get(), 0u, allocator_.get())); - gvn_->AllowModifications(); } ArenaPool pool_; diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc index 51b6709533..0a6924cbca 100644 --- a/compiler/dex/mir_dataflow.cc +++ b/compiler/dex/mir_dataflow.cc @@ -118,10 +118,10 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_DA | DF_REF_A | DF_NON_NULL_DST, // 1D MONITOR_ENTER vAA - DF_UA | DF_NULL_CHK_0 | DF_REF_A, + DF_UA | DF_NULL_CHK_A | DF_REF_A, // 1E MONITOR_EXIT vAA - DF_UA | DF_NULL_CHK_0 | DF_REF_A, + DF_UA | DF_NULL_CHK_A | DF_REF_A, // 1F CHK_CAST vAA, type@BBBB DF_UA | DF_REF_A | DF_UMS, @@ -130,7 +130,7 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { 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, + DF_DA | DF_UB | DF_NULL_CHK_B | DF_CORE_A | DF_REF_B, // 22 NEW_INSTANCE vAA, type@BBBB DF_DA | DF_NON_NULL_DST | DF_REF_A | DF_UMS, @@ -235,88 +235,88 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { 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 | DF_LVN, + DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, // 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 | DF_LVN, + DF_DA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, // 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 | DF_LVN, + DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN, // 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 | DF_LVN, + DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, // 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 | DF_LVN, + DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, // 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 | DF_LVN, + DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, // 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 | DF_LVN, + DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, // 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 | DF_LVN, + DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, // 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 | DF_LVN, + DF_UA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, // 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 | DF_LVN, + DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN, // 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 | DF_LVN, + DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, // 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 | DF_LVN, + DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, // 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 | DF_LVN, + DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, // 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 | DF_LVN, + DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, // 52 IGET vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN, + DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, // 53 IGET_WIDE vA, vB, field@CCCC - DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN, + DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, // 54 IGET_OBJECT vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN, + DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN, // 55 IGET_BOOLEAN vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN, + DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, // 56 IGET_BYTE vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN, + DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, // 57 IGET_CHAR vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN, + DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, // 58 IGET_SHORT vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN, + DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, // 59 IPUT vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN, + DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, // 5A IPUT_WIDE vA, vB, field@CCCC - DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B | DF_IFIELD | DF_LVN, + DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, // 5B IPUT_OBJECT vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN, + DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN, // 5C IPUT_BOOLEAN vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN, + DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, // 5D IPUT_BYTE vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN, + DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, // 5E IPUT_CHAR vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN, + DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, // 5F IPUT_SHORT vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN, + DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, // 60 SGET vAA, field@BBBB DF_DA | DF_SFIELD | DF_UMS, @@ -712,10 +712,10 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, // E3 IGET_VOLATILE - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN, + DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, // E4 IPUT_VOLATILE - DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN, + DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, // E5 SGET_VOLATILE DF_DA | DF_SFIELD | DF_UMS, @@ -724,13 +724,13 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_UA | DF_SFIELD | DF_UMS, // E7 IGET_OBJECT_VOLATILE - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN, + DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN, // E8 IGET_WIDE_VOLATILE - DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN, + DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, // E9 IPUT_WIDE_VOLATILE - DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B | DF_IFIELD | DF_LVN, + DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, // EA SGET_WIDE_VOLATILE DF_DA | DF_A_WIDE | DF_SFIELD | DF_UMS, @@ -751,28 +751,28 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_FORMAT_3RC, // F0 INVOKE_OBJECT_INIT_RANGE - DF_NOP | DF_NULL_CHK_0, + DF_NOP, // F1 RETURN_VOID_BARRIER DF_NOP, // F2 IGET_QUICK - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_IFIELD | DF_LVN, + DF_DA | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN, // F3 IGET_WIDE_QUICK - DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_IFIELD | DF_LVN, + DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN, // F4 IGET_OBJECT_QUICK - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_IFIELD | DF_LVN, + DF_DA | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN, // F5 IPUT_QUICK - DF_UA | DF_UB | DF_NULL_CHK_1 | DF_IFIELD | DF_LVN, + DF_UA | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN, // F6 IPUT_WIDE_QUICK - DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_IFIELD | DF_LVN, + DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN, // F7 IPUT_OBJECT_QUICK - DF_UA | DF_UB | DF_NULL_CHK_1 | DF_IFIELD | DF_LVN, + DF_UA | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN, // F8 INVOKE_VIRTUAL_QUICK DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS, @@ -787,7 +787,7 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { 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 | DF_IFIELD | DF_LVN, + DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN, // FD SGET_OBJECT_VOLATILE DF_DA | DF_REF_A | DF_SFIELD | DF_UMS, @@ -824,7 +824,7 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_NOP, // 108 MIR_NULL_CHECK - DF_UA | DF_REF_A | DF_NULL_CHK_0 | DF_LVN, + DF_UA | DF_REF_A | DF_NULL_CHK_A | DF_LVN, // 109 MIR_RANGE_CHECK 0, @@ -893,10 +893,10 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { 0, // 11F MirOpPackedArrayGet - DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN, + DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, // 120 MirOpPackedArrayPut - DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN, + DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, }; /* Return the base virtual register for a SSA name */ @@ -1403,7 +1403,7 @@ bool MIRGraph::VerifyPredInfo(BasicBlock* bb) { GetBlockName(bb, block_name1); GetBlockName(pred_bb, block_name2); DumpCFG("/sdcard/cfg/", false); - LOG(FATAL) << "Successor " << block_name1 << "not found from " + LOG(FATAL) << "Successor " << block_name1 << " not found from " << block_name2; } } diff --git a/compiler/dex/mir_field_info.h b/compiler/dex/mir_field_info.h index 9745c412c9..1842a16840 100644 --- a/compiler/dex/mir_field_info.h +++ b/compiler/dex/mir_field_info.h @@ -137,6 +137,7 @@ class MirIFieldLoweringInfo : public MirFieldInfo { // The member offset of the field, 0u if unresolved. MemberOffset field_offset_; + friend class NullCheckEliminationTest; friend class GlobalValueNumberingTest; friend class LocalValueNumberingTest; }; diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index f0c9858627..3fa80b9c0b 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -86,6 +86,7 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena) raw_use_counts_(arena->Adapter()), num_reachable_blocks_(0), max_num_reachable_blocks_(0), + dfs_orders_up_to_date_(false), dfs_order_(arena->Adapter(kArenaAllocDfsPreOrder)), dfs_post_order_(arena->Adapter(kArenaAllocDfsPostOrder)), dom_post_order_traversal_(arena->Adapter(kArenaAllocDomPostOrder)), @@ -93,6 +94,7 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena) topological_order_loop_ends_(arena->Adapter(kArenaAllocTopologicalSortOrder)), topological_order_indexes_(arena->Adapter(kArenaAllocTopologicalSortOrder)), topological_order_loop_head_stack_(arena->Adapter(kArenaAllocTopologicalSortOrder)), + max_nested_loops_(0u), i_dom_list_(NULL), temp_scoped_alloc_(), temp_insn_data_(nullptr), @@ -1975,6 +1977,7 @@ void MIRGraph::ComputeTopologicalSortOrder() { // Prepare the loop head stack for iteration. topological_order_loop_head_stack_.clear(); topological_order_loop_head_stack_.reserve(max_nested_loops); + max_nested_loops_ = max_nested_loops; } bool BasicBlock::IsExceptionBlock() const { @@ -2224,7 +2227,7 @@ void BasicBlock::ResetOptimizationFlags(uint16_t reset_flags) { } } -void BasicBlock::Hide(CompilationUnit* c_unit) { +void BasicBlock::Hide(MIRGraph* mir_graph) { // First lets make it a dalvik bytecode block so it doesn't have any special meaning. block_type = kDalvikByteCode; @@ -2239,7 +2242,6 @@ void BasicBlock::Hide(CompilationUnit* c_unit) { first_mir_insn = nullptr; last_mir_insn = nullptr; - MIRGraph* mir_graph = c_unit->mir_graph.get(); for (BasicBlockId pred_id : predecessors) { BasicBlock* pred_bb = mir_graph->GetBasicBlock(pred_id); DCHECK(pred_bb != nullptr); @@ -2262,6 +2264,48 @@ void BasicBlock::Hide(CompilationUnit* c_unit) { successor_block_list_type = kNotUsed; } +/* + * Kill an unreachable block and all blocks that become unreachable by killing this one. + */ +void BasicBlock::KillUnreachable(MIRGraph* mir_graph) { + DCHECK(predecessors.empty()); // Unreachable. + + // Mark as dead and hidden. + block_type = kDead; + hidden = true; + + // Detach it from its MIRs so we don't generate code for them. Also detached MIRs + // are updated to know that they no longer have a parent. + for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) { + mir->bb = NullBasicBlockId; + } + first_mir_insn = nullptr; + last_mir_insn = nullptr; + + data_flow_info = nullptr; + + // Erase this bb from all children's predecessors and kill unreachable children. + ChildBlockIterator iter(this, mir_graph); + for (BasicBlock* succ_bb = iter.Next(); succ_bb != nullptr; succ_bb = iter.Next()) { + succ_bb->ErasePredecessor(id); + if (succ_bb->predecessors.empty()) { + succ_bb->KillUnreachable(mir_graph); + } + } + + // Remove links to children. + fall_through = NullBasicBlockId; + taken = NullBasicBlockId; + successor_block_list_type = kNotUsed; + + if (kIsDebugBuild) { + if (catch_entry) { + DCHECK_EQ(mir_graph->catches_.count(start_offset), 1u); + mir_graph->catches_.erase(start_offset); + } + } +} + bool BasicBlock::IsSSALiveOut(const CompilationUnit* c_unit, int ssa_reg) { // In order to determine if the ssa reg is live out, we scan all the MIRs. We remember // the last SSA number of the same dalvik register. At the end, if it is different than ssa_reg, @@ -2333,17 +2377,34 @@ bool BasicBlock::ReplaceChild(BasicBlockId old_bb, BasicBlockId new_bb) { void BasicBlock::ErasePredecessor(BasicBlockId old_pred) { auto pos = std::find(predecessors.begin(), predecessors.end(), old_pred); DCHECK(pos != predecessors.end()); - predecessors.erase(pos); + // It's faster to move the back() to *pos than erase(pos). + *pos = predecessors.back(); + predecessors.pop_back(); + size_t idx = std::distance(predecessors.begin(), pos); + for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) { + if (static_cast<int>(mir->dalvikInsn.opcode) != kMirOpPhi) { + break; + } + DCHECK_EQ(mir->ssa_rep->num_uses - 1u, predecessors.size()); + DCHECK_EQ(mir->meta.phi_incoming[idx], old_pred); + mir->meta.phi_incoming[idx] = mir->meta.phi_incoming[predecessors.size()]; + mir->ssa_rep->uses[idx] = mir->ssa_rep->uses[predecessors.size()]; + mir->ssa_rep->num_uses = predecessors.size(); + } } void BasicBlock::UpdatePredecessor(BasicBlockId old_pred, BasicBlockId new_pred) { DCHECK_NE(new_pred, NullBasicBlockId); auto pos = std::find(predecessors.begin(), predecessors.end(), old_pred); - if (pos != predecessors.end()) { - *pos = new_pred; - } else { - // If not found, add it. - predecessors.push_back(new_pred); + DCHECK(pos != predecessors.end()); + *pos = new_pred; + size_t idx = std::distance(predecessors.begin(), pos); + for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) { + if (static_cast<int>(mir->dalvikInsn.opcode) != kMirOpPhi) { + break; + } + DCHECK_EQ(mir->meta.phi_incoming[idx], old_pred); + mir->meta.phi_incoming[idx] = new_pred; } } diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index cc215bde06..a405af16a3 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -49,17 +49,14 @@ enum DataFlowAttributePos { kFormat35c, kFormat3rc, kFormatExtended, // Extended format for extended MIRs. - kNullCheckSrc0, // Null check of uses[0]. - kNullCheckSrc1, // Null check of uses[1]. - kNullCheckSrc2, // Null check of uses[2]. + kNullCheckA, // Null check of A. + kNullCheckB, // Null check of B. 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]. + kRangeCheckC, // Range check of C. kFPA, kFPB, kFPC, @@ -88,17 +85,14 @@ enum DataFlowAttributePos { #define DF_FORMAT_35C (UINT64_C(1) << kFormat35c) #define DF_FORMAT_3RC (UINT64_C(1) << kFormat3rc) #define DF_FORMAT_EXTENDED (UINT64_C(1) << kFormatExtended) -#define DF_NULL_CHK_0 (UINT64_C(1) << kNullCheckSrc0) -#define DF_NULL_CHK_1 (UINT64_C(1) << kNullCheckSrc1) -#define DF_NULL_CHK_2 (UINT64_C(1) << kNullCheckSrc2) +#define DF_NULL_CHK_A (UINT64_C(1) << kNullCheckA) +#define DF_NULL_CHK_B (UINT64_C(1) << kNullCheckB) #define DF_NULL_CHK_OUT0 (UINT64_C(1) << kNullCheckOut0) #define DF_NON_NULL_DST (UINT64_C(1) << kDstNonNull) #define DF_NON_NULL_RET (UINT64_C(1) << kRetNonNull) #define DF_NULL_TRANSFER_0 (UINT64_C(1) << kNullTransferSrc0) #define DF_NULL_TRANSFER_N (UINT64_C(1) << kNullTransferSrcN) -#define DF_RANGE_CHK_1 (UINT64_C(1) << kRangeCheckSrc1) -#define DF_RANGE_CHK_2 (UINT64_C(1) << kRangeCheckSrc2) -#define DF_RANGE_CHK_3 (UINT64_C(1) << kRangeCheckSrc3) +#define DF_RANGE_CHK_C (UINT64_C(1) << kRangeCheckC) #define DF_FP_A (UINT64_C(1) << kFPA) #define DF_FP_B (UINT64_C(1) << kFPB) #define DF_FP_C (UINT64_C(1) << kFPC) @@ -117,14 +111,11 @@ enum DataFlowAttributePos { #define DF_HAS_DEFS (DF_DA) -#define DF_HAS_NULL_CHKS (DF_NULL_CHK_0 | \ - DF_NULL_CHK_1 | \ - DF_NULL_CHK_2 | \ +#define DF_HAS_NULL_CHKS (DF_NULL_CHK_A | \ + DF_NULL_CHK_B | \ DF_NULL_CHK_OUT0) -#define DF_HAS_RANGE_CHKS (DF_RANGE_CHK_1 | \ - DF_RANGE_CHK_2 | \ - DF_RANGE_CHK_3) +#define DF_HAS_RANGE_CHKS (DF_RANGE_CHK_C) #define DF_HAS_NR_CHKS (DF_HAS_NULL_CHKS | \ DF_HAS_RANGE_CHKS) @@ -132,9 +123,10 @@ enum DataFlowAttributePos { #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) #define DF_NULL_TRANSFER (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N) +#define DF_IS_INVOKE (DF_FORMAT_35C | DF_FORMAT_3RC) + enum OatMethodAttributes { kIsLeaf, // Method is leaf. kHasLoop, // Method contains simple loop. @@ -160,6 +152,7 @@ enum OatMethodAttributes { #define MIR_CALLEE (1 << kMIRCallee) #define MIR_IGNORE_SUSPEND_CHECK (1 << kMIRIgnoreSuspendCheck) #define MIR_DUP (1 << kMIRDup) +#define MIR_MARK (1 << kMIRMark) #define MIR_STORE_NON_TEMPORAL (1 << kMIRStoreNonTemporal) #define BLOCK_NAME_LEN 80 @@ -422,7 +415,12 @@ struct BasicBlock { * remove itself from any predecessor edges, remove itself from any * child's predecessor array. */ - void Hide(CompilationUnit* c_unit); + void Hide(MIRGraph* mir_graph); + + /** + * @brief Kill the unreachable block and all blocks that become unreachable by killing this one. + */ + void KillUnreachable(MIRGraph* mir_graph); /** * @brief Is ssa_reg the last SSA definition of that VR in the block? @@ -710,6 +708,10 @@ class MIRGraph { return &topological_order_loop_head_stack_; } + size_t GetMaxNestedLoops() const { + return max_nested_loops_; + } + bool IsConst(int32_t s_reg) const { return is_constant_v_->IsBitSet(s_reg); } @@ -1015,6 +1017,10 @@ class MIRGraph { return GetFirstSpecialTempVR() + max_available_special_compiler_temps_; } + bool HasTryCatchBlocks() const { + return current_code_item_->tries_size_ != 0; + } + void DumpCheckStats(); MIR* FindMoveResult(BasicBlock* bb, MIR* mir); int SRegToVReg(int ssa_reg) const; @@ -1150,6 +1156,10 @@ class MIRGraph { void InsertPhiNodes(); void DoDFSPreOrderSSARename(BasicBlock* block); + bool DfsOrdersUpToDate() const { + return dfs_orders_up_to_date_; + } + /* * IsDebugBuild sanity check: keep track of the Dex PCs for catch entries so that later on * we can verify that all catch entries have native PC entries. @@ -1246,6 +1256,7 @@ class MIRGraph { ArenaVector<uint32_t> raw_use_counts_; // Not weighted unsigned int num_reachable_blocks_; unsigned int max_num_reachable_blocks_; + bool dfs_orders_up_to_date_; ArenaVector<BasicBlockId> dfs_order_; ArenaVector<BasicBlockId> dfs_post_order_; ArenaVector<BasicBlockId> dom_post_order_traversal_; @@ -1258,6 +1269,7 @@ class MIRGraph { ArenaVector<uint16_t> topological_order_indexes_; // Stack of the loop head indexes and recalculation flags for RepeatingTopologicalSortIterator. ArenaVector<std::pair<uint16_t, bool>> topological_order_loop_head_stack_; + size_t max_nested_loops_; int* i_dom_list_; std::unique_ptr<ScopedArenaAllocator> temp_scoped_alloc_; uint16_t* temp_insn_data_; @@ -1306,7 +1318,9 @@ class MIRGraph { static const uint64_t oat_data_flow_attributes_[kMirOpLast]; ArenaVector<BasicBlock*> gen_suspend_test_list_; // List of blocks containing suspend tests + friend class MirOptimizationTest; friend class ClassInitCheckEliminationTest; + friend class NullCheckEliminationTest; friend class GlobalValueNumberingTest; friend class LocalValueNumberingTest; friend class TopologicalSortOrderTest; diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index 322b737677..96505ab729 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -408,14 +408,14 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { if (bb->block_type == kDead) { return true; } - // Don't do a separate LVN if we did the GVN. - bool use_lvn = bb->use_lvn && (cu_->disable_opt & (1u << kGlobalValueNumbering)) != 0u; + bool use_lvn = bb->use_lvn && (cu_->disable_opt & (1u << kLocalValueNumbering)) == 0u; std::unique_ptr<ScopedArenaAllocator> allocator; std::unique_ptr<GlobalValueNumbering> global_valnum; std::unique_ptr<LocalValueNumbering> local_valnum; if (use_lvn) { allocator.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - global_valnum.reset(new (allocator.get()) GlobalValueNumbering(cu_, allocator.get())); + global_valnum.reset(new (allocator.get()) GlobalValueNumbering(cu_, allocator.get(), + GlobalValueNumbering::kModeLvn)); local_valnum.reset(new (allocator.get()) LocalValueNumbering(global_valnum.get(), bb->id, allocator.get())); } @@ -752,51 +752,101 @@ bool MIRGraph::LayoutBlocks(BasicBlock* bb) { /* Combine any basic blocks terminated by instructions that we now know can't throw */ void 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 == NullBasicBlockId) - || (GetBasicBlock(bb->taken)->block_type != kExceptionHandling) - || (bb->successor_block_list_type != kNotUsed) - || (static_cast<int>(bb->last_mir_insn->dalvikInsn.opcode) != kMirOpCheck)) { - break; - } - - // Test the kMirOpCheck instruction + while ((bb->block_type == kDalvikByteCode) && + (bb->last_mir_insn != nullptr) && + (static_cast<int>(bb->last_mir_insn->dalvikInsn.opcode) == kMirOpCheck)) { MIR* mir = bb->last_mir_insn; - // Grab the attributes from the paired opcode + DCHECK(bb->first_mir_insn != nullptr); + + // Grab the attributes from the paired opcode. MIR* throw_insn = mir->meta.throw_insn; uint64_t df_attributes = GetDataFlowAttributes(throw_insn); - bool can_combine = true; - if (df_attributes & DF_HAS_NULL_CHKS) { - can_combine &= ((throw_insn->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0); + + // Don't combine if the throw_insn can still throw NPE. + if ((df_attributes & DF_HAS_NULL_CHKS) != 0 && + (throw_insn->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0) { + break; } - if (df_attributes & DF_HAS_RANGE_CHKS) { - can_combine &= ((throw_insn->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0); + // Now whitelist specific instructions. + bool ok = false; + if ((df_attributes & DF_IFIELD) != 0) { + // Combine only if fast, otherwise weird things can happen. + const MirIFieldLoweringInfo& field_info = GetIFieldLoweringInfo(throw_insn); + ok = (df_attributes & DF_DA) ? field_info.FastPut() : field_info.FastGet(); + } else if ((df_attributes & DF_SFIELD) != 0) { + // Combine only if fast, otherwise weird things can happen. + const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(throw_insn); + bool fast = ((df_attributes & DF_DA) ? field_info.FastPut() : field_info.FastGet()); + // Don't combine if the SGET/SPUT can call <clinit>(). + bool clinit = !field_info.IsInitialized() && + (throw_insn->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0; + ok = fast && !clinit; + } else if ((df_attributes & DF_HAS_RANGE_CHKS) != 0) { + // Only AGET/APUT have range checks. We have processed the AGET/APUT null check above. + DCHECK_NE(throw_insn->optimization_flags & MIR_IGNORE_NULL_CHECK, 0); + ok = ((throw_insn->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0); + } else if ((throw_insn->dalvikInsn.FlagsOf() & Instruction::kThrow) == 0) { + // We can encounter a non-throwing insn here thanks to inlining or other optimizations. + ok = true; + } else if (throw_insn->dalvikInsn.opcode == Instruction::ARRAY_LENGTH || + throw_insn->dalvikInsn.opcode == Instruction::FILL_ARRAY_DATA || + static_cast<int>(throw_insn->dalvikInsn.opcode) == kMirOpNullCheck) { + // No more checks for these (null check was processed above). + ok = true; } - if (!can_combine) { + if (!ok) { break; } + // OK - got one. Combine BasicBlock* bb_next = GetBasicBlock(bb->fall_through); DCHECK(!bb_next->catch_entry); - DCHECK_EQ(Predecessors(bb_next), 1U); - // Overwrite the kOpCheck insn with the paired opcode + DCHECK_EQ(bb_next->predecessors.size(), 1u); + // Overwrite the kMirOpCheck insn with the paired opcode. DCHECK_EQ(bb_next->first_mir_insn, throw_insn); *bb->last_mir_insn = *throw_insn; + // And grab the rest of the instructions from bb_next. + bb->last_mir_insn = bb_next->last_mir_insn; + throw_insn->next = nullptr; + bb_next->last_mir_insn = throw_insn; + // Mark acquired instructions as belonging to bb. + for (MIR* insn = mir; insn != nullptr; insn = insn->next) { + insn->bb = bb->id; + } + // Before we overwrite successors, remove their predecessor links to bb. + bb_next->ErasePredecessor(bb->id); + if (bb->taken != NullBasicBlockId) { + DCHECK_EQ(bb->successor_block_list_type, kNotUsed); + BasicBlock* bb_taken = GetBasicBlock(bb->taken); + // bb->taken will be overwritten below. + DCHECK_EQ(bb_taken->block_type, kExceptionHandling); + DCHECK_EQ(bb_taken->predecessors.size(), 1u); + DCHECK_EQ(bb_taken->predecessors[0], bb->id); + bb_taken->predecessors.clear(); + bb_taken->block_type = kDead; + DCHECK(bb_taken->data_flow_info == nullptr); + } else { + DCHECK_EQ(bb->successor_block_list_type, kCatch); + for (SuccessorBlockInfo* succ_info : bb->successor_blocks) { + if (succ_info->block != NullBasicBlockId) { + BasicBlock* succ_bb = GetBasicBlock(succ_info->block); + DCHECK(succ_bb->catch_entry); + succ_bb->ErasePredecessor(bb->id); + if (succ_bb->predecessors.empty()) { + succ_bb->KillUnreachable(this); + } + } + } + } // Use the successor info from the next block bb->successor_block_list_type = bb_next->successor_block_list_type; bb->successor_blocks.swap(bb_next->successor_blocks); // Swap instead of copying. + bb_next->successor_block_list_type = kNotUsed; // Use the ending block linkage from the next block bb->fall_through = bb_next->fall_through; - GetBasicBlock(bb->taken)->block_type = kDead; // Kill the unused exception block + bb_next->fall_through = NullBasicBlockId; bb->taken = bb_next->taken; - // Include the rest of the instructions - bb->last_mir_insn = bb_next->last_mir_insn; + bb_next->taken = NullBasicBlockId; /* * If lower-half of pair of blocks to combine contained * a return or a conditional branch or an explicit throw, @@ -805,15 +855,30 @@ void MIRGraph::CombineBlocks(struct BasicBlock* bb) { bb->terminated_by_return = bb_next->terminated_by_return; bb->conditional_branch = bb_next->conditional_branch; bb->explicit_throw = bb_next->explicit_throw; + // Merge the use_lvn flag. + bb->use_lvn |= bb_next->use_lvn; + + // Kill the unused block. + bb_next->data_flow_info = nullptr; /* * 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. + * NOTE: GVN uses bb->data_flow_info->live_in_v which is unaffected by the block merge. */ - // Kill bb_next and remap now-dead id to parent + // Kill bb_next and remap now-dead id to parent. bb_next->block_type = kDead; + bb_next->data_flow_info = nullptr; // Must be null for dead blocks. (Relied on by the GVN.) block_id_map_.Overwrite(bb_next->id, bb->id); + // Update predecessors in children. + ChildBlockIterator iter(bb, this); + for (BasicBlock* child = iter.Next(); child != nullptr; child = iter.Next()) { + child->UpdatePredecessor(bb_next->id, bb->id); + } + + // DFS orders are not up to date anymore. + dfs_orders_up_to_date_ = false; // Now, loop back and see if we can keep going } @@ -827,12 +892,21 @@ bool MIRGraph::EliminateNullChecksGate() { DCHECK(temp_scoped_alloc_.get() == nullptr); temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - temp_bit_vector_size_ = GetNumSSARegs(); + temp_bit_vector_size_ = GetNumOfCodeVRs(); temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector( temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapNullCheck); temp_bit_matrix_ = static_cast<ArenaBitVector**>( temp_scoped_alloc_->Alloc(sizeof(ArenaBitVector*) * GetNumBlocks(), kArenaAllocMisc)); std::fill_n(temp_bit_matrix_, GetNumBlocks(), nullptr); + + // reset MIR_MARK + AllNodesIterator iter(this); + for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { + for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { + mir->optimization_flags &= ~MIR_MARK; + } + } + return true; } @@ -840,98 +914,96 @@ bool MIRGraph::EliminateNullChecksGate() { * Eliminate unnecessary null checks for a basic block. */ bool MIRGraph::EliminateNullChecks(BasicBlock* bb) { - if (bb->data_flow_info == nullptr) return false; + if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) { + // Ignore the kExitBlock as well. + DCHECK(bb->first_mir_insn == nullptr); + return false; + } - ArenaBitVector* ssa_regs_to_check = temp_bit_vector_; + ArenaBitVector* vregs_to_check = temp_bit_vector_; /* * Set initial state. Catch blocks don't need any special treatment. */ if (bb->block_type == kEntryBlock) { - ssa_regs_to_check->ClearAllBits(); + vregs_to_check->ClearAllBits(); // Assume all ins are objects. for (uint16_t in_reg = GetFirstInVR(); in_reg < GetNumOfCodeVRs(); in_reg++) { - ssa_regs_to_check->SetBit(in_reg); + vregs_to_check->SetBit(in_reg); } if ((cu_->access_flags & kAccStatic) == 0) { - // If non-static method, mark "this" as non-null + // If non-static method, mark "this" as non-null. int this_reg = GetFirstInVR(); - ssa_regs_to_check->ClearBit(this_reg); - } - } else if (bb->predecessors.size() == 1) { - BasicBlock* pred_bb = GetBasicBlock(bb->predecessors[0]); - // pred_bb must have already been processed at least once. - DCHECK(temp_bit_matrix_[pred_bb->id] != nullptr); - ssa_regs_to_check->Copy(temp_bit_matrix_[pred_bb->id]); - if (pred_bb->block_type == kDalvikByteCode) { - // Check to see if predecessor had an explicit null-check. - MIR* last_insn = pred_bb->last_mir_insn; - if (last_insn != nullptr) { - Instruction::Code last_opcode = last_insn->dalvikInsn.opcode; - if (last_opcode == Instruction::IF_EQZ) { - if (pred_bb->fall_through == bb->id) { - // 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. - ssa_regs_to_check->ClearBit(last_insn->ssa_rep->uses[0]); - } - } else if (last_opcode == Instruction::IF_NEZ) { - if (pred_bb->taken == bb->id) { - // The taken block following a IF_NEZ, set the vA of the IF_NEZ to show that it can't be - // null. - ssa_regs_to_check->ClearBit(last_insn->ssa_rep->uses[0]); - } - } - } + vregs_to_check->ClearBit(this_reg); } } else { - // Starting state is union of all incoming arcs + DCHECK_EQ(bb->block_type, kDalvikByteCode); + // Starting state is union of all incoming arcs. bool copied_first = false; for (BasicBlockId pred_id : bb->predecessors) { + if (temp_bit_matrix_[pred_id] == nullptr) { + continue; + } BasicBlock* pred_bb = GetBasicBlock(pred_id); DCHECK(pred_bb != nullptr); - DCHECK(pred_bb->data_flow_info != nullptr); - if (temp_bit_matrix_[pred_bb->id] == nullptr) { - continue; + MIR* null_check_insn = nullptr; + if (pred_bb->block_type == kDalvikByteCode) { + // Check to see if predecessor had an explicit null-check. + MIR* last_insn = pred_bb->last_mir_insn; + if (last_insn != nullptr) { + Instruction::Code last_opcode = last_insn->dalvikInsn.opcode; + if ((last_opcode == Instruction::IF_EQZ && pred_bb->fall_through == bb->id) || + (last_opcode == Instruction::IF_NEZ && pred_bb->taken == bb->id)) { + // Remember the null check insn if there's no other predecessor requiring null check. + if (!copied_first || !vregs_to_check->IsBitSet(last_insn->dalvikInsn.vA)) { + null_check_insn = last_insn; + } + } + } } if (!copied_first) { copied_first = true; - ssa_regs_to_check->Copy(temp_bit_matrix_[pred_bb->id]); + vregs_to_check->Copy(temp_bit_matrix_[pred_id]); } else { - ssa_regs_to_check->Union(temp_bit_matrix_[pred_bb->id]); + vregs_to_check->Union(temp_bit_matrix_[pred_id]); + } + if (null_check_insn != nullptr) { + vregs_to_check->ClearBit(null_check_insn->dalvikInsn.vA); } } DCHECK(copied_first); // At least one predecessor must have been processed before this bb. } - // At this point, ssa_regs_to_check shows which sregs have an object definition with + // At this point, vregs_to_check shows which sregs have an object definition with // no intervening uses. // 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; - } - uint64_t df_attributes = GetDataFlowAttributes(mir); + DCHECK_EQ(df_attributes & DF_NULL_TRANSFER_N, 0u); // No Phis yet. + // Might need a null check? if (df_attributes & DF_HAS_NULL_CHKS) { - int src_idx; - if (df_attributes & DF_NULL_CHK_1) { - src_idx = 1; - } else if (df_attributes & DF_NULL_CHK_2) { - src_idx = 2; + int src_vreg; + if (df_attributes & DF_NULL_CHK_OUT0) { + DCHECK_NE(df_attributes & DF_IS_INVOKE, 0u); + src_vreg = mir->dalvikInsn.vC; + } else if (df_attributes & DF_NULL_CHK_B) { + DCHECK_NE(df_attributes & DF_REF_B, 0u); + src_vreg = mir->dalvikInsn.vB; } else { - src_idx = 0; + DCHECK_NE(df_attributes & DF_NULL_CHK_A, 0u); + DCHECK_NE(df_attributes & DF_REF_A, 0u); + src_vreg = mir->dalvikInsn.vA; } - int src_sreg = mir->ssa_rep->uses[src_idx]; - if (!ssa_regs_to_check->IsBitSet(src_sreg)) { + if (!vregs_to_check->IsBitSet(src_vreg)) { // Eliminate the null check. - mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; + mir->optimization_flags |= MIR_MARK; } else { // Do the null check. - // Do not clear MIR_IGNORE_NULL_CHECK flag as it may be set by another optimization - // Mark s_reg as null-checked - ssa_regs_to_check->ClearBit(src_sreg); + mir->optimization_flags &= ~MIR_MARK; + // Mark src_vreg as null-checked. + vregs_to_check->ClearBit(src_vreg); } } @@ -945,66 +1017,41 @@ bool MIRGraph::EliminateNullChecks(BasicBlock* bb) { * Note: we can't tell if a CONST definition might be used as an object, so treat * them all as object definitions. */ - if (((df_attributes & (DF_DA | DF_REF_A)) == (DF_DA | DF_REF_A)) || + if ((df_attributes & (DF_DA | DF_REF_A)) == (DF_DA | DF_REF_A) || (df_attributes & DF_SETS_CONST)) { - ssa_regs_to_check->SetBit(mir->ssa_rep->defs[0]); + vregs_to_check->SetBit(mir->dalvikInsn.vA); } - // Now, remove mark from all object definitions we know are non-null. + // Then, remove mark from all object definitions we know are non-null. if (df_attributes & DF_NON_NULL_DST) { // Mark target of NEW* as non-null - ssa_regs_to_check->ClearBit(mir->ssa_rep->defs[0]); + DCHECK_NE(df_attributes & DF_REF_A, 0u); + vregs_to_check->ClearBit(mir->dalvikInsn.vA); } // 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 - ssa_regs_to_check->ClearBit(next_mir->ssa_rep->defs[0]); + if (UNLIKELY(next_mir == nullptr)) { + // The MethodVerifier makes sure there's no MOVE_RESULT at the catch entry or branch + // target, so the MOVE_RESULT cannot be broken away into another block. + LOG(WARNING) << "Unexpected end of block following new"; + } else if (UNLIKELY(next_mir->dalvikInsn.opcode != Instruction::MOVE_RESULT_OBJECT)) { + LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode; } else { - if (next_mir) { - LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode; - } else if (bb->fall_through != NullBasicBlockId) { - // Look in next basic block - struct BasicBlock* next_bb = GetBasicBlock(bb->fall_through); - for (MIR* tmir = next_bb->first_mir_insn; tmir != NULL; - tmir =tmir->next) { - if (MIR::DecodedInstruction::IsPseudoMirOp(tmir->dalvikInsn.opcode)) { - continue; - } - // First non-pseudo should be MOVE_RESULT_OBJECT - if (tmir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) { - // Mark as null checked - ssa_regs_to_check->ClearBit(tmir->ssa_rep->defs[0]); - } else { - LOG(WARNING) << "Unexpected op after new: " << tmir->dalvikInsn.opcode; - } - break; - } - } + // Mark as null checked. + vregs_to_check->ClearBit(next_mir->dalvikInsn.vA); } } - /* - * Propagate nullcheck state on register copies (including - * Phi pseudo copies. For the latter, nullcheck state is - * the "or" 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 needs_null_check = false; - for (int i = 0; i < operands; i++) { - needs_null_check |= ssa_regs_to_check->IsBitSet(mir->ssa_rep->uses[i]); - } - if (needs_null_check) { - ssa_regs_to_check->SetBit(tgt_sreg); + // Propagate null check state on register copies. + if (df_attributes & DF_NULL_TRANSFER_0) { + DCHECK_EQ(df_attributes | ~(DF_DA | DF_REF_A | DF_UB | DF_REF_B), static_cast<uint64_t>(-1)); + if (vregs_to_check->IsBitSet(mir->dalvikInsn.vB)) { + vregs_to_check->SetBit(mir->dalvikInsn.vA); } else { - ssa_regs_to_check->ClearBit(tgt_sreg); + vregs_to_check->ClearBit(mir->dalvikInsn.vA); } } } @@ -1014,15 +1061,15 @@ bool MIRGraph::EliminateNullChecks(BasicBlock* bb) { ArenaBitVector* old_ending_ssa_regs_to_check = temp_bit_matrix_[bb->id]; if (old_ending_ssa_regs_to_check == nullptr) { DCHECK(temp_scoped_alloc_.get() != nullptr); - nce_changed = ssa_regs_to_check->GetHighestBitSet() != -1; - temp_bit_matrix_[bb->id] = ssa_regs_to_check; - // Create a new ssa_regs_to_check for next BB. + nce_changed = vregs_to_check->GetHighestBitSet() != -1; + temp_bit_matrix_[bb->id] = vregs_to_check; + // Create a new vregs_to_check for next BB. temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector( temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapNullCheck); - } else if (!ssa_regs_to_check->SameBitsSet(old_ending_ssa_regs_to_check)) { + } else if (!vregs_to_check->SameBitsSet(old_ending_ssa_regs_to_check)) { nce_changed = true; - temp_bit_matrix_[bb->id] = ssa_regs_to_check; - temp_bit_vector_ = old_ending_ssa_regs_to_check; // Reuse for ssa_regs_to_check for next BB. + temp_bit_matrix_[bb->id] = vregs_to_check; + temp_bit_vector_ = old_ending_ssa_regs_to_check; // Reuse for vregs_to_check for next BB. } return nce_changed; } @@ -1034,6 +1081,18 @@ void MIRGraph::EliminateNullChecksEnd() { temp_bit_matrix_ = nullptr; DCHECK(temp_scoped_alloc_.get() != nullptr); temp_scoped_alloc_.reset(); + + // converge MIR_MARK with MIR_IGNORE_NULL_CHECK + AllNodesIterator iter(this); + for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { + for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { + constexpr int kMarkToIgnoreNullCheckShift = kMIRMark - kMIRIgnoreNullCheck; + COMPILE_ASSERT(kMarkToIgnoreNullCheckShift > 0, check_valid_shift_right); + uint16_t mirMarkAdjustedToIgnoreNullCheck = + (mir->optimization_flags & MIR_MARK) >> kMarkToIgnoreNullCheckShift; + mir->optimization_flags |= mirMarkAdjustedToIgnoreNullCheck; + } + } } /* @@ -1098,26 +1157,27 @@ bool MIRGraph::EliminateClassInitChecksGate() { // First, find all SGET/SPUTs that may need class initialization checks, record INVOKE_STATICs. AllNodesIterator iter(this); for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - DCHECK(bb->data_flow_info != nullptr); - if (mir->dalvikInsn.opcode >= Instruction::SGET && - mir->dalvikInsn.opcode <= Instruction::SPUT_SHORT) { - const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(mir); - uint16_t index = 0xffffu; - if (!field_info.IsInitialized()) { - DCHECK_LT(class_to_index_map.size(), 0xffffu); - MapEntry entry = { - // Treat unresolved fields as if each had its own class. - field_info.IsResolved() ? field_info.DeclaringDexFile() - : nullptr, - field_info.IsResolved() ? field_info.DeclaringClassIndex() - : field_info.FieldIndex(), - static_cast<uint16_t>(class_to_index_map.size()) - }; - index = class_to_index_map.insert(entry).first->index; + if (bb->block_type == kDalvikByteCode) { + for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { + if (mir->dalvikInsn.opcode >= Instruction::SGET && + mir->dalvikInsn.opcode <= Instruction::SPUT_SHORT) { + const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(mir); + uint16_t index = 0xffffu; + if (!field_info.IsInitialized()) { + DCHECK_LT(class_to_index_map.size(), 0xffffu); + MapEntry entry = { + // Treat unresolved fields as if each had its own class. + field_info.IsResolved() ? field_info.DeclaringDexFile() + : nullptr, + field_info.IsResolved() ? field_info.DeclaringClassIndex() + : field_info.FieldIndex(), + static_cast<uint16_t>(class_to_index_map.size()) + }; + index = class_to_index_map.insert(entry).first->index; + } + // Using offset/2 for index into temp_insn_data_. + temp_insn_data_[mir->offset / 2u] = index; } - // Using offset/2 for index into temp_insn_data_. - temp_insn_data_[mir->offset / 2u] = index; } } } @@ -1146,7 +1206,9 @@ bool MIRGraph::EliminateClassInitChecksGate() { */ bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) { DCHECK_EQ((cu_->disable_opt & (1 << kClassInitCheckElimination)), 0u); - if (bb->data_flow_info == nullptr) { + if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) { + // Ignore the kExitBlock as well. + DCHECK(bb->first_mir_insn == nullptr); return false; } @@ -1161,7 +1223,6 @@ bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) { BasicBlock* pred_bb = GetBasicBlock(bb->predecessors[0]); // pred_bb must have already been processed at least once. DCHECK(pred_bb != nullptr); - DCHECK(pred_bb->data_flow_info != nullptr); DCHECK(temp_bit_matrix_[pred_bb->id] != nullptr); classes_to_check->Copy(temp_bit_matrix_[pred_bb->id]); } else { @@ -1170,7 +1231,6 @@ bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) { for (BasicBlockId pred_id : bb->predecessors) { BasicBlock* pred_bb = GetBasicBlock(pred_id); DCHECK(pred_bb != nullptr); - DCHECK(pred_bb->data_flow_info != nullptr); if (temp_bit_matrix_[pred_bb->id] == nullptr) { continue; } @@ -1237,7 +1297,7 @@ void MIRGraph::EliminateClassInitChecksEnd() { } bool MIRGraph::ApplyGlobalValueNumberingGate() { - if ((cu_->disable_opt & (1u << kGlobalValueNumbering)) != 0u) { + if (GlobalValueNumbering::Skip(cu_)) { return false; } @@ -1245,7 +1305,8 @@ bool MIRGraph::ApplyGlobalValueNumberingGate() { temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); DCHECK(temp_gvn_ == nullptr); temp_gvn_.reset( - new (temp_scoped_alloc_.get()) GlobalValueNumbering(cu_, temp_scoped_alloc_.get())); + new (temp_scoped_alloc_.get()) GlobalValueNumbering(cu_, temp_scoped_alloc_.get(), + GlobalValueNumbering::kModeGvn)); return true; } @@ -1264,19 +1325,23 @@ bool MIRGraph::ApplyGlobalValueNumbering(BasicBlock* bb) { void MIRGraph::ApplyGlobalValueNumberingEnd() { // Perform modifications. if (temp_gvn_->Good()) { - temp_gvn_->AllowModifications(); - PreOrderDfsIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - ScopedArenaAllocator allocator(&cu_->arena_stack); // Reclaim memory after each LVN. - LocalValueNumbering* lvn = temp_gvn_->PrepareBasicBlock(bb, &allocator); - if (lvn != nullptr) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - lvn->GetValueNumber(mir); + if (max_nested_loops_ != 0u) { + temp_gvn_->StartPostProcessing(); + TopologicalSortIterator iter(this); + for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { + ScopedArenaAllocator allocator(&cu_->arena_stack); // Reclaim memory after each LVN. + LocalValueNumbering* lvn = temp_gvn_->PrepareBasicBlock(bb, &allocator); + if (lvn != nullptr) { + for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { + lvn->GetValueNumber(mir); + } + bool change = temp_gvn_->FinishBasicBlock(bb); + DCHECK(!change) << PrettyMethod(cu_->method_idx, *cu_->dex_file); } - bool change = temp_gvn_->FinishBasicBlock(bb); - DCHECK(!change) << PrettyMethod(cu_->method_idx, *cu_->dex_file); } } + // GVN was successful, running the LVN would be useless. + cu_->disable_opt |= (1u << kLocalValueNumbering); } else { LOG(WARNING) << "GVN failed for " << PrettyMethod(cu_->method_idx, *cu_->dex_file); } diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc index 55e547e56f..337d4efda3 100644 --- a/compiler/dex/mir_optimization_test.cc +++ b/compiler/dex/mir_optimization_test.cc @@ -23,15 +23,8 @@ namespace art { -class ClassInitCheckEliminationTest : public testing::Test { +class MirOptimizationTest : public testing::Test { protected: - struct SFieldDef { - uint16_t field_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_class_idx; - uint16_t declaring_field_idx; - }; - struct BBDef { static constexpr size_t kMaxSuccessors = 4; static constexpr size_t kMaxPredecessors = 4; @@ -44,9 +37,12 @@ class ClassInitCheckEliminationTest : public testing::Test { }; struct MIRDef { - Instruction::Code opcode; BasicBlockId bbid; - uint32_t field_or_method_info; + Instruction::Code opcode; + uint32_t field_info; + uint32_t vA; + uint32_t vB; + uint32_t vC; }; #define DEF_SUCC0() \ @@ -72,32 +68,6 @@ class ClassInitCheckEliminationTest : public testing::Test { #define DEF_BB(type, succ, pred) \ { type, succ, pred } -#define DEF_MIR(opcode, bb, field_info) \ - { opcode, bb, field_info } - - void DoPrepareSFields(const SFieldDef* defs, size_t count) { - cu_.mir_graph->sfield_lowering_infos_.clear(); - cu_.mir_graph->sfield_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const SFieldDef* def = &defs[i]; - MirSFieldLoweringInfo field_info(def->field_idx); - if (def->declaring_dex_file != 0u) { - field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file); - field_info.declaring_class_idx_ = def->declaring_class_idx; - field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ = MirSFieldLoweringInfo::kFlagIsStatic; - } - ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved()); - ASSERT_FALSE(field_info.IsInitialized()); - cu_.mir_graph->sfield_lowering_infos_.push_back(field_info); - } - } - - template <size_t count> - void PrepareSFields(const SFieldDef (&defs)[count]) { - DoPrepareSFields(defs, count); - } - void DoPrepareBasicBlocks(const BBDef* defs, size_t count) { cu_.mir_graph->block_id_map_.clear(); cu_.mir_graph->block_list_.clear(); @@ -145,6 +115,63 @@ class ClassInitCheckEliminationTest : public testing::Test { DoPrepareBasicBlocks(defs, count); } + void PrepareSingleBlock() { + static const BBDef bbs[] = { + DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), + DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), + DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(3)), + DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(1)), + }; + PrepareBasicBlocks(bbs); + } + + void PrepareDiamond() { + static const BBDef bbs[] = { + DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), + DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), + DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), + DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)), + DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), + DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), + DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), + }; + PrepareBasicBlocks(bbs); + } + + void PrepareLoop() { + static const BBDef bbs[] = { + DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), + DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), + DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), + DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), + DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)), // "taken" loops to self. + DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), + }; + PrepareBasicBlocks(bbs); + } + + void PrepareCatch() { + static const BBDef bbs[] = { + DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), + DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), + DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), + DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), // The top. + DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // The throwing insn. + DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Catch handler. + DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), // The merged block. + }; + PrepareBasicBlocks(bbs); + BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u); + catch_handler->catch_entry = true; + // Add successor block info to the check block. + BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u); + check_bb->successor_block_list_type = kCatch; + SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*> + (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor)); + successor_block_info->block = catch_handler->id; + check_bb->successor_blocks.push_back(successor_block_info); + } + void DoPrepareMIRs(const MIRDef* defs, size_t count) { mir_count_ = count; mirs_ = reinterpret_cast<MIR*>(cu_.arena.Alloc(sizeof(MIR) * count, kArenaAllocMIR)); @@ -157,9 +184,15 @@ class ClassInitCheckEliminationTest : public testing::Test { BasicBlock* bb = cu_.mir_graph->block_list_[def->bbid]; bb->AppendMIR(mir); if (def->opcode >= Instruction::SGET && def->opcode <= Instruction::SPUT_SHORT) { - ASSERT_LT(def->field_or_method_info, cu_.mir_graph->sfield_lowering_infos_.size()); - mir->meta.sfield_lowering_info = def->field_or_method_info; + ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.size()); + mir->meta.sfield_lowering_info = def->field_info; + } else if (def->opcode >= Instruction::IGET && def->opcode <= Instruction::IPUT_SHORT) { + ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.size()); + mir->meta.ifield_lowering_info = def->field_info; } + mir->dalvikInsn.vA = def->vA; + mir->dalvikInsn.vB = def->vB; + mir->dalvikInsn.vC = def->vC; mir->ssa_rep = nullptr; mir->offset = 2 * i; // All insns need to be at least 2 code units long. mir->optimization_flags = 0u; @@ -179,15 +212,60 @@ class ClassInitCheckEliminationTest : public testing::Test { DoPrepareMIRs(defs, count); } + MirOptimizationTest() + : pool_(), + cu_(&pool_), + mir_count_(0u), + mirs_(nullptr), + code_item_(nullptr) { + cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); + cu_.access_flags = kAccStatic; // Don't let "this" interfere with this test. + } + + ArenaPool pool_; + CompilationUnit cu_; + size_t mir_count_; + MIR* mirs_; + DexFile::CodeItem* code_item_; +}; + +class ClassInitCheckEliminationTest : public MirOptimizationTest { + protected: + struct SFieldDef { + uint16_t field_idx; + uintptr_t declaring_dex_file; + uint16_t declaring_class_idx; + uint16_t declaring_field_idx; + }; + + void DoPrepareSFields(const SFieldDef* defs, size_t count) { + cu_.mir_graph->sfield_lowering_infos_.clear(); + cu_.mir_graph->sfield_lowering_infos_.reserve(count); + for (size_t i = 0u; i != count; ++i) { + const SFieldDef* def = &defs[i]; + MirSFieldLoweringInfo field_info(def->field_idx); + if (def->declaring_dex_file != 0u) { + field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file); + field_info.declaring_class_idx_ = def->declaring_class_idx; + field_info.declaring_field_idx_ = def->declaring_field_idx; + field_info.flags_ = MirSFieldLoweringInfo::kFlagIsStatic; + } + ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved()); + ASSERT_FALSE(field_info.IsInitialized()); + cu_.mir_graph->sfield_lowering_infos_.push_back(field_info); + } + } + + template <size_t count> + void PrepareSFields(const SFieldDef (&defs)[count]) { + DoPrepareSFields(defs, count); + } + void PerformClassInitCheckElimination() { - cu_.mir_graph->SSATransformationStart(); cu_.mir_graph->ComputeDFSOrders(); - cu_.mir_graph->ComputeDominators(); - cu_.mir_graph->ComputeTopologicalSortOrder(); - cu_.mir_graph->SSATransformationEnd(); bool gate_result = cu_.mir_graph->EliminateClassInitChecksGate(); ASSERT_TRUE(gate_result); - LoopRepeatingTopologicalSortIterator iterator(cu_.mir_graph.get()); + RepeatingPreOrderDfsIterator iterator(cu_.mir_graph.get()); bool change = false; for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) { change = cu_.mir_graph->EliminateClassInitChecks(bb); @@ -196,21 +274,64 @@ class ClassInitCheckEliminationTest : public testing::Test { } ClassInitCheckEliminationTest() - : pool_(), - cu_(&pool_), - mir_count_(0u), - mirs_(nullptr), - code_item_(nullptr) { - cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); + : MirOptimizationTest() { } +}; - ArenaPool pool_; - CompilationUnit cu_; - size_t mir_count_; - MIR* mirs_; - DexFile::CodeItem* code_item_; +class NullCheckEliminationTest : public MirOptimizationTest { + protected: + struct IFieldDef { + uint16_t field_idx; + uintptr_t declaring_dex_file; + uint16_t declaring_class_idx; + uint16_t declaring_field_idx; + }; + + void DoPrepareIFields(const IFieldDef* defs, size_t count) { + cu_.mir_graph->ifield_lowering_infos_.clear(); + cu_.mir_graph->ifield_lowering_infos_.reserve(count); + for (size_t i = 0u; i != count; ++i) { + const IFieldDef* def = &defs[i]; + MirIFieldLoweringInfo field_info(def->field_idx); + if (def->declaring_dex_file != 0u) { + field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file); + field_info.declaring_class_idx_ = def->declaring_class_idx; + field_info.declaring_field_idx_ = def->declaring_field_idx; + } + ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved()); + cu_.mir_graph->ifield_lowering_infos_.push_back(field_info); + } + } + + template <size_t count> + void PrepareIFields(const IFieldDef (&defs)[count]) { + DoPrepareIFields(defs, count); + } + + void PerformNullCheckElimination() { + // Make vregs in range [100, 1000) input registers, i.e. requiring a null check. + code_item_->registers_size_ = 1000; + code_item_->ins_size_ = 900; + + cu_.mir_graph->ComputeDFSOrders(); + bool gate_result = cu_.mir_graph->EliminateNullChecksGate(); + ASSERT_TRUE(gate_result); + RepeatingPreOrderDfsIterator iterator(cu_.mir_graph.get()); + bool change = false; + for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) { + change = cu_.mir_graph->EliminateNullChecks(bb); + } + cu_.mir_graph->EliminateNullChecksEnd(); + } + + NullCheckEliminationTest() + : MirOptimizationTest() { + } }; +#define DEF_SGET_SPUT_V0(bb, opcode, field_info) \ + { bb, opcode, field_info, 0u, 0u, 0u } + TEST_F(ClassInitCheckEliminationTest, SingleBlock) { static const SFieldDef sfields[] = { { 0u, 1u, 0u, 0u }, @@ -220,31 +341,25 @@ TEST_F(ClassInitCheckEliminationTest, SingleBlock) { { 4u, 1u, 3u, 4u }, // Same declaring class as sfield[3]. { 5u, 0u, 0u, 0u }, // Unresolved. }; - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(1)), - }; static const MIRDef mirs[] = { - DEF_MIR(Instruction::SPUT, 3u, 5u), // Unresolved. - DEF_MIR(Instruction::SPUT, 3u, 0u), - DEF_MIR(Instruction::SGET, 3u, 1u), - DEF_MIR(Instruction::SGET, 3u, 2u), - DEF_MIR(Instruction::SGET, 3u, 5u), // Unresolved. - DEF_MIR(Instruction::SGET, 3u, 0u), - DEF_MIR(Instruction::SGET, 3u, 1u), - DEF_MIR(Instruction::SGET, 3u, 2u), - DEF_MIR(Instruction::SGET, 3u, 5u), // Unresolved. - DEF_MIR(Instruction::SGET, 3u, 3u), - DEF_MIR(Instruction::SGET, 3u, 4u), + DEF_SGET_SPUT_V0(3u, Instruction::SPUT, 5u), // Unresolved. + DEF_SGET_SPUT_V0(3u, Instruction::SPUT, 0u), + DEF_SGET_SPUT_V0(3u, Instruction::SGET, 1u), + DEF_SGET_SPUT_V0(3u, Instruction::SGET, 2u), + DEF_SGET_SPUT_V0(3u, Instruction::SGET, 5u), // Unresolved. + DEF_SGET_SPUT_V0(3u, Instruction::SGET, 0u), + DEF_SGET_SPUT_V0(3u, Instruction::SGET, 1u), + DEF_SGET_SPUT_V0(3u, Instruction::SGET, 2u), + DEF_SGET_SPUT_V0(3u, Instruction::SGET, 5u), // Unresolved. + DEF_SGET_SPUT_V0(3u, Instruction::SGET, 3u), + DEF_SGET_SPUT_V0(3u, Instruction::SGET, 4u), }; static const bool expected_ignore_clinit_check[] = { false, false, false, false, true, true, true, true, true, false, true }; PrepareSFields(sfields); - PrepareBasicBlocks(bbs); + PrepareSingleBlock(); PrepareMIRs(mirs); PerformClassInitCheckElimination(); ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_); @@ -268,40 +383,31 @@ TEST_F(ClassInitCheckEliminationTest, Diamond) { { 9u, 1u, 8u, 9u }, // Same declaring class as sfield[8]. { 10u, 0u, 0u, 0u }, // Unresolved. }; - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), - }; static const MIRDef mirs[] = { // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_MIR(Instruction::SGET, 3u, 10u), // Unresolved. - DEF_MIR(Instruction::SPUT, 3u, 10u), // Unresolved. - DEF_MIR(Instruction::SPUT, 3u, 0u), - DEF_MIR(Instruction::SGET, 6u, 0u), // Eliminated (block #3 dominates #6). - DEF_MIR(Instruction::SPUT, 4u, 1u), - DEF_MIR(Instruction::SGET, 6u, 1u), // Not eliminated (block #4 doesn't dominate #6). - DEF_MIR(Instruction::SGET, 3u, 2u), - DEF_MIR(Instruction::SGET, 4u, 2u), // Eliminated (block #3 dominates #4). - DEF_MIR(Instruction::SGET, 3u, 3u), - DEF_MIR(Instruction::SGET, 5u, 3u), // Eliminated (block #3 dominates #5). - DEF_MIR(Instruction::SGET, 3u, 4u), - DEF_MIR(Instruction::SGET, 6u, 4u), // Eliminated (block #3 dominates #6). - DEF_MIR(Instruction::SGET, 4u, 5u), - DEF_MIR(Instruction::SGET, 6u, 5u), // Not eliminated (block #4 doesn't dominate #6). - DEF_MIR(Instruction::SGET, 5u, 6u), - DEF_MIR(Instruction::SGET, 6u, 6u), // Not eliminated (block #5 doesn't dominate #6). - DEF_MIR(Instruction::SGET, 4u, 7u), - DEF_MIR(Instruction::SGET, 5u, 7u), - DEF_MIR(Instruction::SGET, 6u, 7u), // Eliminated (initialized in both blocks #3 and #4). - DEF_MIR(Instruction::SGET, 4u, 8u), - DEF_MIR(Instruction::SGET, 5u, 9u), - DEF_MIR(Instruction::SGET, 6u, 8u), // Eliminated (with sfield[9] in block #5). - DEF_MIR(Instruction::SPUT, 6u, 9u), // Eliminated (with sfield[8] in block #4). + DEF_SGET_SPUT_V0(3u, Instruction::SGET, 10u), // Unresolved. + DEF_SGET_SPUT_V0(3u, Instruction::SPUT, 10u), // Unresolved. + DEF_SGET_SPUT_V0(3u, Instruction::SPUT, 0u), + DEF_SGET_SPUT_V0(6u, Instruction::SGET, 0u), // Eliminated (BB #3 dominates #6). + DEF_SGET_SPUT_V0(4u, Instruction::SPUT, 1u), + DEF_SGET_SPUT_V0(6u, Instruction::SGET, 1u), // Not eliminated (BB #4 doesn't dominate #6). + DEF_SGET_SPUT_V0(3u, Instruction::SGET, 2u), + DEF_SGET_SPUT_V0(4u, Instruction::SGET, 2u), // Eliminated (BB #3 dominates #4). + DEF_SGET_SPUT_V0(3u, Instruction::SGET, 3u), + DEF_SGET_SPUT_V0(5u, Instruction::SGET, 3u), // Eliminated (BB #3 dominates #5). + DEF_SGET_SPUT_V0(3u, Instruction::SGET, 4u), + DEF_SGET_SPUT_V0(6u, Instruction::SGET, 4u), // Eliminated (BB #3 dominates #6). + DEF_SGET_SPUT_V0(4u, Instruction::SGET, 5u), + DEF_SGET_SPUT_V0(6u, Instruction::SGET, 5u), // Not eliminated (BB #4 doesn't dominate #6). + DEF_SGET_SPUT_V0(5u, Instruction::SGET, 6u), + DEF_SGET_SPUT_V0(6u, Instruction::SGET, 6u), // Not eliminated (BB #5 doesn't dominate #6). + DEF_SGET_SPUT_V0(4u, Instruction::SGET, 7u), + DEF_SGET_SPUT_V0(5u, Instruction::SGET, 7u), + DEF_SGET_SPUT_V0(6u, Instruction::SGET, 7u), // Eliminated (initialized in both #3 and #4). + DEF_SGET_SPUT_V0(4u, Instruction::SGET, 8u), + DEF_SGET_SPUT_V0(5u, Instruction::SGET, 9u), + DEF_SGET_SPUT_V0(6u, Instruction::SGET, 8u), // Eliminated (with sfield[9] in BB #5). + DEF_SGET_SPUT_V0(6u, Instruction::SPUT, 9u), // Eliminated (with sfield[8] in BB #4). }; static const bool expected_ignore_clinit_check[] = { false, true, // Unresolved: sfield[10], method[2] @@ -317,7 +423,7 @@ TEST_F(ClassInitCheckEliminationTest, Diamond) { }; PrepareSFields(sfields); - PrepareBasicBlocks(bbs); + PrepareDiamond(); PrepareMIRs(mirs); PerformClassInitCheckElimination(); ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_); @@ -332,26 +438,18 @@ TEST_F(ClassInitCheckEliminationTest, Loop) { { 0u, 1u, 0u, 0u }, { 1u, 1u, 1u, 1u }, }; - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)), // "taken" loops to self. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), - }; static const MIRDef mirs[] = { - DEF_MIR(Instruction::SGET, 3u, 0u), - DEF_MIR(Instruction::SGET, 4u, 1u), - DEF_MIR(Instruction::SGET, 5u, 0u), // Eliminated. - DEF_MIR(Instruction::SGET, 5u, 1u), // Eliminated. + DEF_SGET_SPUT_V0(3u, Instruction::SGET, 0u), + DEF_SGET_SPUT_V0(4u, Instruction::SGET, 1u), + DEF_SGET_SPUT_V0(5u, Instruction::SGET, 0u), // Eliminated. + DEF_SGET_SPUT_V0(5u, Instruction::SGET, 1u), // Eliminated. }; static const bool expected_ignore_clinit_check[] = { false, false, true, true }; PrepareSFields(sfields); - PrepareBasicBlocks(bbs); + PrepareLoop(); PrepareMIRs(mirs); PerformClassInitCheckElimination(); ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_); @@ -368,42 +466,24 @@ TEST_F(ClassInitCheckEliminationTest, Catch) { { 2u, 1u, 2u, 2u }, { 3u, 1u, 3u, 3u }, }; - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), // The top. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // The throwing insn. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Catch handler. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), // The merged block. - }; static const MIRDef mirs[] = { - DEF_MIR(Instruction::SGET, 3u, 0u), // Before the exception edge. - DEF_MIR(Instruction::SGET, 3u, 1u), // Before the exception edge. - DEF_MIR(Instruction::SGET, 4u, 2u), // After the exception edge. - DEF_MIR(Instruction::SGET, 4u, 3u), // After the exception edge. - DEF_MIR(Instruction::SGET, 5u, 0u), // In catch handler; class init check eliminated. - DEF_MIR(Instruction::SGET, 5u, 2u), // In catch handler; class init check not eliminated. - DEF_MIR(Instruction::SGET, 6u, 0u), // Class init check eliminated. - DEF_MIR(Instruction::SGET, 6u, 1u), // Class init check eliminated. - DEF_MIR(Instruction::SGET, 6u, 2u), // Class init check eliminated. - DEF_MIR(Instruction::SGET, 6u, 3u), // Class init check not eliminated. + DEF_SGET_SPUT_V0(3u, Instruction::SGET, 0u), // Before the exception edge. + DEF_SGET_SPUT_V0(3u, Instruction::SGET, 1u), // Before the exception edge. + DEF_SGET_SPUT_V0(4u, Instruction::SGET, 2u), // After the exception edge. + DEF_SGET_SPUT_V0(4u, Instruction::SGET, 3u), // After the exception edge. + DEF_SGET_SPUT_V0(5u, Instruction::SGET, 0u), // In catch handler; clinit check eliminated. + DEF_SGET_SPUT_V0(5u, Instruction::SGET, 2u), // In catch handler; clinit check not eliminated. + DEF_SGET_SPUT_V0(6u, Instruction::SGET, 0u), // Class init check eliminated. + DEF_SGET_SPUT_V0(6u, Instruction::SGET, 1u), // Class init check eliminated. + DEF_SGET_SPUT_V0(6u, Instruction::SGET, 2u), // Class init check eliminated. + DEF_SGET_SPUT_V0(6u, Instruction::SGET, 3u), // Class init check not eliminated. }; static const bool expected_ignore_clinit_check[] = { false, false, false, false, true, false, true, true, true, false }; PrepareSFields(sfields); - PrepareBasicBlocks(bbs); - BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u); - catch_handler->catch_entry = true; - // Add successor block info to the check block. - BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u); - check_bb->successor_block_list_type = kCatch; - SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*> - (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor)); - successor_block_info->block = catch_handler->id; - check_bb->successor_blocks.push_back(successor_block_info); + PrepareCatch(); PrepareMIRs(mirs); PerformClassInitCheckElimination(); ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_); @@ -413,4 +493,189 @@ TEST_F(ClassInitCheckEliminationTest, Catch) { } } +#define DEF_IGET_IPUT(bb, opcode, vA, vB, field_info) \ + { bb, opcode, field_info, vA, vB, 0u } +#define DEF_AGET_APUT(bb, opcode, vA, vB, vC) \ + { bb, opcode, 0u, vA, vB, vC } +#define DEF_INVOKE(bb, opcode, vC) \ + { bb, opcode, 0u, 0u, 0u, vC } +#define DEF_OTHER1(bb, opcode, vA) \ + { bb, opcode, 0u, vA, 0u, 0u } +#define DEF_OTHER2(bb, opcode, vA, vB) \ + { bb, opcode, 0u, vA, vB, 0u } + +TEST_F(NullCheckEliminationTest, SingleBlock) { + static const IFieldDef ifields[] = { + { 0u, 1u, 0u, 0u }, + { 1u, 1u, 0u, 1u }, + { 2u, 1u, 0u, 2u }, // Object. + }; + static const MIRDef mirs[] = { + DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 0u, 100u, 2u), + DEF_IGET_IPUT(3u, Instruction::IGET, 1u, 0u, 1u), + DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 2u, 100u, 2u), // Differs from 0u (no LVN here). + DEF_IGET_IPUT(3u, Instruction::IGET, 3u, 2u, 1u), + DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 101u, 0u), + DEF_IGET_IPUT(3u, Instruction::IGET, 5u, 102u, 0u), + DEF_IGET_IPUT(3u, Instruction::IGET, 6u, 103u, 0u), + DEF_IGET_IPUT(3u, Instruction::IGET, 7u, 103u, 1u), + DEF_IGET_IPUT(3u, Instruction::IPUT, 8u, 104u, 0u), + DEF_IGET_IPUT(3u, Instruction::IPUT, 9u, 104u, 1u), + DEF_IGET_IPUT(3u, Instruction::IGET, 10u, 105u, 0u), + DEF_IGET_IPUT(3u, Instruction::IPUT, 11u, 105u, 1u), + DEF_IGET_IPUT(3u, Instruction::IPUT, 12u, 106u, 0u), + DEF_IGET_IPUT(3u, Instruction::IGET, 13u, 106u, 1u), + DEF_INVOKE(3u, Instruction::INVOKE_DIRECT, 107), + DEF_IGET_IPUT(3u, Instruction::IGET, 15u, 107u, 1u), + DEF_IGET_IPUT(3u, Instruction::IGET, 16u, 108u, 0u), + DEF_INVOKE(3u, Instruction::INVOKE_DIRECT, 108), + DEF_AGET_APUT(3u, Instruction::AGET, 18u, 109u, 110u), + DEF_AGET_APUT(3u, Instruction::APUT, 19u, 109u, 111u), + DEF_OTHER2(3u, Instruction::ARRAY_LENGTH, 20u, 112u), + DEF_AGET_APUT(3u, Instruction::AGET, 21u, 112u, 113u), + DEF_OTHER1(3u, Instruction::MONITOR_ENTER, 114u), + DEF_OTHER1(3u, Instruction::MONITOR_EXIT, 114u), + }; + static const bool expected_ignore_null_check[] = { + false, false, true, false /* Not doing LVN. */, + false, true /* Set before running NCE. */, + false, true, // IGET, IGET + false, true, // IPUT, IPUT + false, true, // IGET, IPUT + false, true, // IPUT, IGET + false, true, // INVOKE, IGET + false, true, // IGET, INVOKE + false, true, // AGET, APUT + false, true, // ARRAY_LENGTH, AGET + false, true, // MONITOR_ENTER, MONITOR_EXIT + }; + + PrepareIFields(ifields); + PrepareSingleBlock(); + PrepareMIRs(mirs); + + // Mark IGET 5u as null-checked to test that NCE doesn't clear this flag. + mirs_[5u].optimization_flags |= MIR_IGNORE_NULL_CHECK; + + PerformNullCheckElimination(); + ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); + for (size_t i = 0u; i != arraysize(mirs); ++i) { + EXPECT_EQ(expected_ignore_null_check[i], + (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; + } +} + +TEST_F(NullCheckEliminationTest, Diamond) { + static const IFieldDef ifields[] = { + { 0u, 1u, 0u, 0u }, + { 1u, 1u, 0u, 1u }, + { 2u, 1u, 0u, 2u }, // int[]. + }; + static const MIRDef mirs[] = { + // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. + DEF_IGET_IPUT(3u, Instruction::IPUT, 0u, 100u, 0u), + DEF_IGET_IPUT(6u, Instruction::IGET, 1u, 100u, 1u), // Eliminated (BB #3 dominates #6). + DEF_IGET_IPUT(3u, Instruction::IGET, 2u, 101u, 0u), + DEF_IGET_IPUT(4u, Instruction::IPUT, 3u, 101u, 0u), // Eliminated (BB #3 dominates #4). + DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 102u, 0u), + DEF_IGET_IPUT(5u, Instruction::IPUT, 5u, 102u, 1u), // Eliminated (BB #3 dominates #5). + DEF_IGET_IPUT(4u, Instruction::IPUT, 6u, 103u, 0u), + DEF_IGET_IPUT(6u, Instruction::IPUT, 7u, 103u, 1u), // Not eliminated (going through BB #5). + DEF_IGET_IPUT(5u, Instruction::IGET, 8u, 104u, 1u), + DEF_IGET_IPUT(6u, Instruction::IGET, 9u, 104u, 0u), // Not eliminated (going through BB #4). + DEF_INVOKE(4u, Instruction::INVOKE_DIRECT, 105u), + DEF_IGET_IPUT(5u, Instruction::IGET, 11u, 105u, 1u), + DEF_IGET_IPUT(6u, Instruction::IPUT, 12u, 105u, 0u), // Eliminated. + DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 13u, 106u, 2u), + DEF_OTHER1(3u, Instruction::IF_EQZ, 13u), // Last insn in the BB #3. + DEF_OTHER2(5u, Instruction::NEW_ARRAY, 13u, 107u), + DEF_AGET_APUT(6u, Instruction::AGET, 16u, 13u, 108u), // Eliminated. + }; + static const bool expected_ignore_null_check[] = { + false, true, // BB #3 IPUT, BB #6 IGET + false, true, // BB #3 IGET, BB #4 IPUT + false, true, // BB #3 IGET, BB #5 IPUT + false, false, // BB #4 IPUT, BB #6 IPUT + false, false, // BB #5 IGET, BB #6 IGET + false, false, true, // BB #4 INVOKE, BB #5 IGET, BB #6 IPUT + false, false, // BB #3 IGET_OBJECT & IF_EQZ + false, true, // BB #5 NEW_ARRAY, BB #6 AGET + }; + + PrepareIFields(ifields); + PrepareDiamond(); + PrepareMIRs(mirs); + PerformNullCheckElimination(); + ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); + for (size_t i = 0u; i != arraysize(mirs); ++i) { + EXPECT_EQ(expected_ignore_null_check[i], + (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; + } +} + +TEST_F(NullCheckEliminationTest, Loop) { + static const IFieldDef ifields[] = { + { 0u, 1u, 0u, 0u }, + { 1u, 1u, 1u, 1u }, + }; + static const MIRDef mirs[] = { + DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u), + DEF_IGET_IPUT(4u, Instruction::IGET, 1u, 101u, 0u), + DEF_IGET_IPUT(5u, Instruction::IGET, 2u, 100u, 1u), // Eliminated. + DEF_IGET_IPUT(5u, Instruction::IGET, 3u, 101u, 1u), // Eliminated. + DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 102u, 0u), + DEF_IGET_IPUT(4u, Instruction::IGET, 5u, 102u, 1u), // Not eliminated (MOVE_OBJECT_16). + DEF_OTHER2(4u, Instruction::MOVE_OBJECT_16, 102u, 103u), + }; + static const bool expected_ignore_null_check[] = { + false, false, true, true, + false, false, false, + }; + + PrepareIFields(ifields); + PrepareLoop(); + PrepareMIRs(mirs); + PerformNullCheckElimination(); + ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); + for (size_t i = 0u; i != arraysize(mirs); ++i) { + EXPECT_EQ(expected_ignore_null_check[i], + (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; + } +} + +TEST_F(NullCheckEliminationTest, Catch) { + static const IFieldDef ifields[] = { + { 0u, 1u, 0u, 0u }, + { 1u, 1u, 1u, 1u }, + }; + static const MIRDef mirs[] = { + DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u), // Before the exception edge. + DEF_IGET_IPUT(3u, Instruction::IGET, 1u, 101u, 0u), // Before the exception edge. + DEF_IGET_IPUT(4u, Instruction::IGET, 2u, 102u, 0u), // After the exception edge. + DEF_IGET_IPUT(4u, Instruction::IGET, 3u, 103u, 0u), // After the exception edge. + DEF_IGET_IPUT(5u, Instruction::IGET, 4u, 100u, 1u), // In catch handler; eliminated. + DEF_IGET_IPUT(5u, Instruction::IGET, 5u, 102u, 1u), // In catch handler; not eliminated. + DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 100u, 0u), // Null check eliminated. + DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 101u, 1u), // Null check eliminated. + DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 102u, 0u), // Null check eliminated. + DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 103u, 1u), // Null check not eliminated. + }; + static const bool expected_ignore_null_check[] = { + false, false, false, false, true, false, true, true, true, false + }; + + PrepareIFields(ifields); + PrepareCatch(); + PrepareMIRs(mirs); + PerformNullCheckElimination(); + ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); + for (size_t i = 0u; i != arraysize(mirs); ++i) { + EXPECT_EQ(expected_ignore_null_check[i], + (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; + } +} + +// Undefine MIR_DEF for null check elimination. +#undef MIR_DEF + } // namespace art diff --git a/compiler/dex/pass.h b/compiler/dex/pass.h index c377426ed5..e349eed76d 100644 --- a/compiler/dex/pass.h +++ b/compiler/dex/pass.h @@ -85,6 +85,9 @@ class Pass { // Unused parameter. UNUSED(data); + // Passes that do all their work in Start() or End() should not allow useless node iteration. + DCHECK(false) << "Unsupported default Worker() used for " << GetName(); + // BasicBlock did not change. return false; } diff --git a/compiler/dex/pass_driver_me.h b/compiler/dex/pass_driver_me.h index 537ceb665e..7bfaf820dd 100644 --- a/compiler/dex/pass_driver_me.h +++ b/compiler/dex/pass_driver_me.h @@ -67,9 +67,6 @@ class PassDriverME: public PassDriver<PassDriverType> { case kTopologicalSortTraversal: DoWalkBasicBlocks<TopologicalSortIterator>(&pass_me_data_holder_, me_pass); break; - case kRepeatingTopologicalSortTraversal: - DoWalkBasicBlocks<RepeatingTopologicalSortIterator>(&pass_me_data_holder_, me_pass); - break; case kLoopRepeatingTopologicalSortTraversal: DoWalkBasicBlocks<LoopRepeatingTopologicalSortIterator>(&pass_me_data_holder_, me_pass); break; diff --git a/compiler/dex/pass_driver_me_opts.cc b/compiler/dex/pass_driver_me_opts.cc index cd3ffd4cc8..a2bf8b4aab 100644 --- a/compiler/dex/pass_driver_me_opts.cc +++ b/compiler/dex/pass_driver_me_opts.cc @@ -20,6 +20,7 @@ #include "dataflow_iterator.h" #include "dataflow_iterator-inl.h" #include "pass_driver_me_opts.h" +#include "post_opt_passes.h" namespace art { @@ -35,13 +36,15 @@ template<> const Pass* const PassDriver<PassDriverMEOpts>::g_passes[] = { GetPassInstance<CacheFieldLoweringInfo>(), GetPassInstance<CacheMethodLoweringInfo>(), + GetPassInstance<CalculatePredecessors>(), + GetPassInstance<DFSOrders>(), + GetPassInstance<ClassInitCheckElimination>(), GetPassInstance<SpecialMethodInliner>(), - GetPassInstance<CodeLayout>(), GetPassInstance<NullCheckElimination>(), + GetPassInstance<BBCombine>(), + GetPassInstance<CodeLayout>(), GetPassInstance<TypeInference>(), - GetPassInstance<ClassInitCheckElimination>(), GetPassInstance<GlobalValueNumberingPass>(), - GetPassInstance<BBCombine>(), GetPassInstance<BBOptimizations>(), }; diff --git a/compiler/dex/pass_driver_me_post_opt.cc b/compiler/dex/pass_driver_me_post_opt.cc index 4acab6c6e5..e6238e9f25 100644 --- a/compiler/dex/pass_driver_me_post_opt.cc +++ b/compiler/dex/pass_driver_me_post_opt.cc @@ -33,7 +33,6 @@ template<> const Pass* const PassDriver<PassDriverMEPostOpt>::g_passes[] = { GetPassInstance<InitializeData>(), GetPassInstance<ClearPhiInstructions>(), - GetPassInstance<CalculatePredecessors>(), GetPassInstance<DFSOrders>(), GetPassInstance<BuildDomination>(), GetPassInstance<TopologicalSortOrders>(), diff --git a/compiler/dex/pass_me.h b/compiler/dex/pass_me.h index 8242cb8792..2f3c8b2217 100644 --- a/compiler/dex/pass_me.h +++ b/compiler/dex/pass_me.h @@ -55,7 +55,6 @@ enum DataFlowAnalysisMode { kRepeatingReversePostOrderDFSTraversal, /**< @brief Depth-First-Search / Repeating Reverse Post-Order. */ kPostOrderDOMTraversal, /**< @brief Dominator tree / Post-Order. */ kTopologicalSortTraversal, /**< @brief Topological Order traversal. */ - kRepeatingTopologicalSortTraversal, /**< @brief Repeating Topological Order traversal. */ kLoopRepeatingTopologicalSortTraversal, /**< @brief Loop-repeating Topological Order traversal. */ kNoNodes, /**< @brief Skip BasicBlock traversal. */ }; diff --git a/compiler/dex/post_opt_passes.h b/compiler/dex/post_opt_passes.h index e7805bae68..7b84ba88c5 100644 --- a/compiler/dex/post_opt_passes.h +++ b/compiler/dex/post_opt_passes.h @@ -30,7 +30,7 @@ namespace art { */ class InitializeData : public PassME { public: - InitializeData() : PassME("InitializeData") { + InitializeData() : PassME("InitializeData", kNoNodes) { } void Start(PassDataHolder* data) const { @@ -76,7 +76,7 @@ class ClearPhiInstructions : public PassME { */ class CalculatePredecessors : public PassME { public: - CalculatePredecessors() : PassME("CalculatePredecessors") { + CalculatePredecessors() : PassME("CalculatePredecessors", kNoNodes) { } void Start(PassDataHolder* data) const; @@ -88,7 +88,14 @@ class CalculatePredecessors : public PassME { */ class DFSOrders : public PassME { public: - DFSOrders() : PassME("DFSOrders") { + DFSOrders() : PassME("DFSOrders", kNoNodes) { + } + + bool Gate(const PassDataHolder* data) const { + DCHECK(data != nullptr); + CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit; + DCHECK(c_unit != nullptr); + return !c_unit->mir_graph->DfsOrdersUpToDate(); } void Start(PassDataHolder* data) const { @@ -105,7 +112,7 @@ class DFSOrders : public PassME { */ class BuildDomination : public PassME { public: - BuildDomination() : PassME("BuildDomination") { + BuildDomination() : PassME("BuildDomination", kNoNodes) { } void Start(PassDataHolder* data) const { @@ -133,7 +140,7 @@ class BuildDomination : public PassME { */ class TopologicalSortOrders : public PassME { public: - TopologicalSortOrders() : PassME("TopologicalSortOrders") { + TopologicalSortOrders() : PassME("TopologicalSortOrders", kNoNodes) { } void Start(PassDataHolder* data) const { @@ -150,7 +157,7 @@ class TopologicalSortOrders : public PassME { */ class DefBlockMatrix : public PassME { public: - DefBlockMatrix() : PassME("DefBlockMatrix") { + DefBlockMatrix() : PassME("DefBlockMatrix", kNoNodes) { } void Start(PassDataHolder* data) const { @@ -167,7 +174,7 @@ class DefBlockMatrix : public PassME { */ class CreatePhiNodes : public PassME { public: - CreatePhiNodes() : PassME("CreatePhiNodes") { + CreatePhiNodes() : PassME("CreatePhiNodes", kNoNodes) { } void Start(PassDataHolder* data) const { @@ -185,7 +192,7 @@ class CreatePhiNodes : public PassME { class ClearVisitedFlag : public PassME { public: - ClearVisitedFlag() : PassME("ClearVisitedFlag") { + ClearVisitedFlag() : PassME("ClearVisitedFlag", kNoNodes) { } void Start(PassDataHolder* data) const { @@ -202,7 +209,7 @@ class ClearVisitedFlag : public PassME { */ class SSAConversion : public PassME { public: - SSAConversion() : PassME("SSAConversion") { + SSAConversion() : PassME("SSAConversion", kNoNodes) { } void Start(PassDataHolder* data) const { @@ -241,7 +248,7 @@ class PhiNodeOperands : public PassME { */ class PerformInitRegLocations : public PassME { public: - PerformInitRegLocations() : PassME("PerformInitRegLocation") { + PerformInitRegLocations() : PassME("PerformInitRegLocation", kNoNodes) { } void Start(PassDataHolder* data) const { @@ -286,7 +293,7 @@ class ConstantPropagation : public PassME { */ class FreeData : public PassME { public: - FreeData() : PassME("FreeData") { + FreeData() : PassME("FreeData", kNoNodes) { } void End(PassDataHolder* data) const { diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index e833c9a629..09acf4cf17 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -969,9 +969,9 @@ LIR* ArmMir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_ size = k32; } LIR* load; - if (UNLIKELY(is_volatile == kVolatile && - (size == k64 || size == kDouble) && - !cu_->compiler_driver->GetInstructionSetFeatures().HasLpae())) { + if (is_volatile == kVolatile && (size == k64 || size == kDouble) && + !cu_->compiler_driver->GetInstructionSetFeatures()-> + AsArmInstructionSetFeatures()->HasLpae()) { // Only 64-bit load needs special handling. // If the cpu supports LPAE, aligned LDRD is atomic - fall through to LoadBaseDisp(). DCHECK(!r_dest.IsFloat()); // See RegClassForFieldLoadSave(). @@ -1093,9 +1093,9 @@ LIR* ArmMir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r } LIR* store; - if (UNLIKELY(is_volatile == kVolatile && - (size == k64 || size == kDouble) && - !cu_->compiler_driver->GetInstructionSetFeatures().HasLpae())) { + if (is_volatile == kVolatile && (size == k64 || size == kDouble) && + !cu_->compiler_driver->GetInstructionSetFeatures()-> + AsArmInstructionSetFeatures()->HasLpae()) { // Only 64-bit store needs special handling. // If the cpu supports LPAE, aligned STRD is atomic - fall through to StoreBaseDisp(). // Use STREXD for the atomic store. (Expect displacement > 0, don't optimize for == 0.) diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index 12ca065de4..a33d15fb32 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -1606,7 +1606,8 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, op == kOpDiv); done = true; } else if (cu_->instruction_set == kThumb2) { - if (cu_->GetInstructionSetFeatures().HasDivideInstruction()) { + if (cu_->GetInstructionSetFeatures()->AsArmInstructionSetFeatures()-> + HasDivideInstruction()) { // Use ARM SDIV instruction for division. For remainder we also need to // calculate using a MUL and subtract. rl_src1 = LoadValue(rl_src1, kCoreReg); @@ -1875,7 +1876,8 @@ void Mir2Lir::GenArithOpIntLit(Instruction::Code opcode, RegLocation rl_dest, Re rl_result = GenDivRemLit(rl_dest, rl_src, lit, is_div); done = true; } else if (cu_->instruction_set == kThumb2) { - if (cu_->GetInstructionSetFeatures().HasDivideInstruction()) { + if (cu_->GetInstructionSetFeatures()->AsArmInstructionSetFeatures()-> + HasDivideInstruction()) { // Use ARM SDIV instruction for division. For remainder we also need to // calculate using a MUL and subtract. rl_src = LoadValue(rl_src, kCoreReg); diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index 07034cb8d7..acf5599d5f 100755 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -858,6 +858,11 @@ bool X86Mir2Lir::GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long) { RegLocation rl_dest = InlineTargetWide(info); int res_vreg, src1_vreg, src2_vreg; + if (rl_dest.s_reg_low == INVALID_SREG) { + // Result is unused, the code is dead. Inlining successful, no code generated. + return true; + } + /* * If the result register is the same as the second element, then we * need to be careful. The reason is that the first copy will diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc index 4388041fac..412f85d5dd 100644 --- a/compiler/dex/ssa_transformation.cc +++ b/compiler/dex/ssa_transformation.cc @@ -66,8 +66,9 @@ void MIRGraph::MarkPreOrder(BasicBlock* block) { } void MIRGraph::RecordDFSOrders(BasicBlock* block) { - DCHECK(temp_scoped_alloc_.get() != nullptr); - ScopedArenaVector<BasicBlock*> succ(temp_scoped_alloc_->Adapter()); + ScopedArenaAllocator allocator(&cu_->arena_stack); + ScopedArenaVector<BasicBlock*> succ(allocator.Adapter()); + succ.reserve(GetNumBlocks()); MarkPreOrder(block); succ.push_back(block); while (!succ.empty()) { @@ -107,10 +108,11 @@ void MIRGraph::ComputeDFSOrders() { AllNodesIterator iter(this); for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { if (!bb->visited) { - bb->Hide(cu_); + bb->Hide(this); } } } + dfs_orders_up_to_date_ = true; } /* diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 4d5d253b4a..332556853c 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -243,8 +243,14 @@ inline int CompilerDriver::IsFastInvoke( CHECK(referrer_class->GetDexCache()->GetResolvedMethod(target_method->dex_method_index) == resolved_method) << PrettyMethod(resolved_method); int stats_flags = kFlagMethodResolved; - GetCodeAndMethodForDirectCall(invoke_type, kDirect, false, referrer_class, resolved_method, - &stats_flags, target_method, direct_code, direct_method); + GetCodeAndMethodForDirectCall(/*out*/invoke_type, + kDirect, // Sharp type + false, // The dex cache is guaranteed to be available + referrer_class, resolved_method, + /*out*/&stats_flags, + target_method, + /*out*/direct_code, + /*out*/direct_method); DCHECK_NE(*invoke_type, kSuper) << PrettyMethod(resolved_method); if (*invoke_type == kDirect) { stats_flags |= kFlagsMethodResolvedVirtualMadeDirect; @@ -273,8 +279,14 @@ inline int CompilerDriver::IsFastInvoke( CHECK(called_method != NULL); CHECK(!called_method->IsAbstract()); int stats_flags = kFlagMethodResolved; - GetCodeAndMethodForDirectCall(invoke_type, kDirect, true, referrer_class, called_method, - &stats_flags, target_method, direct_code, direct_method); + GetCodeAndMethodForDirectCall(/*out*/invoke_type, + kDirect, // Sharp type + true, // The dex cache may not be available + referrer_class, called_method, + /*out*/&stats_flags, + target_method, + /*out*/direct_code, + /*out*/direct_method); DCHECK_NE(*invoke_type, kSuper); if (*invoke_type == kDirect) { stats_flags |= kFlagsMethodResolvedPreciseTypeDevirtualization; @@ -289,8 +301,14 @@ inline int CompilerDriver::IsFastInvoke( // Sharpening failed so generate a regular resolved method dispatch. int stats_flags = kFlagMethodResolved; - GetCodeAndMethodForDirectCall(invoke_type, *invoke_type, false, referrer_class, resolved_method, - &stats_flags, target_method, direct_code, direct_method); + GetCodeAndMethodForDirectCall(/*out*/invoke_type, + *invoke_type, // Sharp type + false, // The dex cache is guaranteed to be available + referrer_class, resolved_method, + /*out*/&stats_flags, + target_method, + /*out*/direct_code, + /*out*/direct_method); return stats_flags; } diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index fb648fc532..a60c5bc98e 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -329,10 +329,10 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, DexFileToMethodInlinerMap* method_inliner_map, Compiler::Kind compiler_kind, InstructionSet instruction_set, - InstructionSetFeatures instruction_set_features, + const InstructionSetFeatures* instruction_set_features, bool image, std::set<std::string>* image_classes, size_t thread_count, bool dump_stats, bool dump_passes, CumulativeLogger* timer, - std::string profile_file) + const std::string& profile_file) : profile_present_(false), compiler_options_(compiler_options), verification_results_(verification_results), method_inliner_map_(method_inliner_map), @@ -347,15 +347,11 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, image_(image), image_classes_(image_classes), thread_count_(thread_count), - start_ns_(0), stats_(new AOTCompilationStats), dump_stats_(dump_stats), dump_passes_(dump_passes), timings_logger_(timer), - compiler_library_(nullptr), compiler_context_(nullptr), - compiler_enable_auto_elf_loading_(nullptr), - compiler_get_method_code_addr_(nullptr), support_boot_image_fixup_(instruction_set != kMips), dedupe_code_("dedupe code"), dedupe_src_mapping_table_("dedupe source mapping table"), @@ -920,6 +916,10 @@ bool CompilerDriver::CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_i if (resolved_class == nullptr) { return false; } + if (GetCompilerOptions().GetCompilePic()) { + // Do not allow a direct class pointer to be used when compiling for position-independent + return false; + } *out_is_finalizable = resolved_class->IsFinalizable(); const bool compiling_boot = Runtime::Current()->GetHeap()->IsCompilingBoot(); const bool support_boot_image_fixup = GetSupportBootImageFixup(); @@ -1112,7 +1112,7 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type, bool no_guarantee_of_dex_cache_entry, - mirror::Class* referrer_class, + const mirror::Class* referrer_class, mirror::ArtMethod* method, int* stats_flags, MethodReference* target_method, @@ -1124,7 +1124,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType // invoked, so this can be passed to the out-of-line runtime support code. *direct_code = 0; *direct_method = 0; - bool use_dex_cache = false; + bool use_dex_cache = GetCompilerOptions().GetCompilePic(); // Off by default const bool compiling_boot = Runtime::Current()->GetHeap()->IsCompilingBoot(); // TODO This is somewhat hacky. We should refactor all of this invoke codepath. const bool force_relocations = (compiling_boot || @@ -1139,7 +1139,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType return; } // TODO: support patching on all architectures. - use_dex_cache = force_relocations && !support_boot_image_fixup_; + use_dex_cache = use_dex_cache || (force_relocations && !support_boot_image_fixup_); } bool method_code_in_boot = (method->GetDeclaringClass()->GetClassLoader() == nullptr); if (!use_dex_cache) { @@ -1951,6 +1951,19 @@ void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_fil context.ForAll(0, dex_file.NumClassDefs(), CompilerDriver::CompileClass, thread_count_); } +// Does the runtime for the InstructionSet provide an implementation returned by +// GetQuickGenericJniStub allowing down calls that aren't compiled using a JNI compiler? +static bool InstructionSetHasGenericJniStub(InstructionSet isa) { + switch (isa) { + case kArm: + case kArm64: + case kThumb2: + case kX86: + case kX86_64: return true; + default: return false; + } +} + void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, @@ -1962,13 +1975,14 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t if ((access_flags & kAccNative) != 0) { // Are we interpreting only and have support for generic JNI down calls? if (!compiler_options_->IsCompilationEnabled() && - (instruction_set_ == kX86_64 || instruction_set_ == kArm64)) { + InstructionSetHasGenericJniStub(instruction_set_)) { // Leaving this empty will trigger the generic JNI version } else { compiled_method = compiler_->JniCompile(access_flags, method_idx, dex_file); CHECK(compiled_method != nullptr); } } else if ((access_flags & kAccAbstract) != 0) { + // Abstract methods don't have code. } else { MethodReference method_ref(&dex_file, method_idx); bool compile = verification_results_->IsCandidateForCompilation(method_ref, access_flags); @@ -2002,6 +2016,10 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t ++non_relative_linker_patch_count; } } + bool compile_pic = GetCompilerOptions().GetCompilePic(); // Off by default + // When compiling with PIC, there should be zero non-relative linker patches + CHECK(!compile_pic || non_relative_linker_patch_count == 0u); + MethodReference ref(&dex_file, method_idx); DCHECK(GetCompiledMethod(ref) == nullptr) << PrettyMethod(method_idx, dex_file); { diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index c445683500..0796f4878a 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -88,10 +88,10 @@ class CompilerDriver { DexFileToMethodInlinerMap* method_inliner_map, Compiler::Kind compiler_kind, InstructionSet instruction_set, - InstructionSetFeatures instruction_set_features, + const InstructionSetFeatures* instruction_set_features, bool image, std::set<std::string>* image_classes, size_t thread_count, bool dump_stats, bool dump_passes, - CumulativeLogger* timer, std::string profile_file = ""); + CumulativeLogger* timer, const std::string& profile_file); ~CompilerDriver(); @@ -115,7 +115,7 @@ class CompilerDriver { return instruction_set_; } - InstructionSetFeatures GetInstructionSetFeatures() const { + const InstructionSetFeatures* GetInstructionSetFeatures() const { return instruction_set_features_; } @@ -400,11 +400,12 @@ class CompilerDriver { public: // TODO make private or eliminate. // Compute constant code and method pointers when possible. - void GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type, + void GetCodeAndMethodForDirectCall(/*out*/InvokeType* type, + InvokeType sharp_type, bool no_guarantee_of_dex_cache_entry, - mirror::Class* referrer_class, + const mirror::Class* referrer_class, mirror::ArtMethod* method, - int* stats_flags, + /*out*/int* stats_flags, MethodReference* target_method, uintptr_t* direct_code, uintptr_t* direct_method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -475,7 +476,7 @@ class CompilerDriver { std::unique_ptr<Compiler> compiler_; const InstructionSet instruction_set_; - const InstructionSetFeatures instruction_set_features_; + const InstructionSetFeatures* const instruction_set_features_; // All class references that require mutable ReaderWriterMutex freezing_constructor_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; @@ -502,7 +503,6 @@ class CompilerDriver { std::unique_ptr<std::set<std::string>> image_classes_; size_t thread_count_; - uint64_t start_ns_; class AOTCompilationStats; std::unique_ptr<AOTCompilationStats> stats_; @@ -515,8 +515,6 @@ class CompilerDriver { typedef void (*CompilerCallbackFn)(CompilerDriver& driver); typedef MutexLock* (*CompilerMutexLockFn)(CompilerDriver& driver); - void* compiler_library_; - typedef void (*DexToDexCompilerFn)(CompilerDriver& driver, const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, @@ -532,13 +530,6 @@ class CompilerDriver { // Arena pool used by the compiler. ArenaPool arena_pool_; - typedef void (*CompilerEnableAutoElfLoadingFn)(CompilerDriver& driver); - CompilerEnableAutoElfLoadingFn compiler_enable_auto_elf_loading_; - - typedef const void* (*CompilerGetMethodCodeAddrFn) - (const CompilerDriver& driver, const CompiledMethod* cm, const mirror::ArtMethod* method); - CompilerGetMethodCodeAddrFn compiler_get_method_code_addr_; - bool support_boot_image_fixup_; // DeDuplication data structures, these own the corresponding byte arrays. diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index eb3de975d2..fb7aeb9b06 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -17,9 +17,14 @@ #ifndef ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_ #define ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_ +#include <string> +#include <vector> + +#include "base/macros.h" + namespace art { -class CompilerOptions { +class CompilerOptions FINAL { public: enum CompilerFilter { kVerifyNone, // Skip verification and compile nothing except JNI stubs. @@ -28,7 +33,7 @@ class CompilerOptions { kBalanced, // Try to get the best performance return on compilation investment. kSpeed, // Maximize runtime performance. kEverything, // Force compilation (Note: excludes compilation of class initializers). - kTime // Compile methods, but minimize compilation time. + kTime, // Compile methods, but minimize compilation time. }; // Guide heuristics to determine whether to compile method if profile data not available. @@ -59,11 +64,13 @@ class CompilerOptions { include_debug_symbols_(kDefaultIncludeDebugSymbols), implicit_null_checks_(false), implicit_so_checks_(false), - implicit_suspend_checks_(false) + implicit_suspend_checks_(false), + compile_pic_(false), #ifdef ART_SEA_IR_MODE - , sea_ir_mode_(false) + sea_ir_mode_(false), #endif - {} + verbose_methods_(nullptr) { + } CompilerOptions(CompilerFilter compiler_filter, size_t huge_method_threshold, @@ -77,10 +84,12 @@ class CompilerOptions { bool include_debug_symbols, bool implicit_null_checks, bool implicit_so_checks, - bool implicit_suspend_checks + bool implicit_suspend_checks, + bool compile_pic, #ifdef ART_SEA_IR_MODE - , bool sea_ir_mode + bool sea_ir_mode, #endif + const std::vector<std::string>* verbose_methods ) : // NOLINT(whitespace/parens) compiler_filter_(compiler_filter), huge_method_threshold_(huge_method_threshold), @@ -94,11 +103,13 @@ class CompilerOptions { include_debug_symbols_(include_debug_symbols), implicit_null_checks_(implicit_null_checks), implicit_so_checks_(implicit_so_checks), - implicit_suspend_checks_(implicit_suspend_checks) + implicit_suspend_checks_(implicit_suspend_checks), + compile_pic_(compile_pic), #ifdef ART_SEA_IR_MODE - , sea_ir_mode_(sea_ir_mode) + sea_ir_mode_(sea_ir_mode), #endif - {} + verbose_methods_(verbose_methods) { + } CompilerFilter GetCompilerFilter() const { return compiler_filter_; @@ -165,28 +176,18 @@ class CompilerOptions { return implicit_null_checks_; } - void SetImplicitNullChecks(bool new_val) { - implicit_null_checks_ = new_val; - } - bool GetImplicitStackOverflowChecks() const { return implicit_so_checks_; } - void SetImplicitStackOverflowChecks(bool new_val) { - implicit_so_checks_ = new_val; - } - bool GetImplicitSuspendChecks() const { return implicit_suspend_checks_; } - void SetImplicitSuspendChecks(bool new_val) { - implicit_suspend_checks_ = new_val; - } - #ifdef ART_SEA_IR_MODE - bool GetSeaIrMode(); + bool GetSeaIrMode() const { + return sea_ir_mode_; + } #endif bool GetGenerateGDBInformation() const { @@ -197,24 +198,49 @@ class CompilerOptions { return include_patch_information_; } + // Should the code be compiled as position independent? + bool GetCompilePic() const { + return compile_pic_; + } + + bool HasVerboseMethods() const { + return verbose_methods_ != nullptr && !verbose_methods_->empty(); + } + + bool IsVerboseMethod(const std::string& pretty_method) const { + for (const std::string& cur_method : *verbose_methods_) { + if (pretty_method.find(cur_method) != std::string::npos) { + return true; + } + } + return false; + } + private: CompilerFilter compiler_filter_; - size_t huge_method_threshold_; - size_t large_method_threshold_; - size_t small_method_threshold_; - size_t tiny_method_threshold_; - size_t num_dex_methods_threshold_; - bool generate_gdb_information_; - bool include_patch_information_; + const size_t huge_method_threshold_; + const size_t large_method_threshold_; + const size_t small_method_threshold_; + const size_t tiny_method_threshold_; + const size_t num_dex_methods_threshold_; + const bool generate_gdb_information_; + const bool include_patch_information_; // When using a profile file only the top K% of the profiled samples will be compiled. - double top_k_profile_threshold_; - bool include_debug_symbols_; - bool implicit_null_checks_; - bool implicit_so_checks_; - bool implicit_suspend_checks_; + const double top_k_profile_threshold_; + const bool include_debug_symbols_; + const bool implicit_null_checks_; + const bool implicit_so_checks_; + const bool implicit_suspend_checks_; + const bool compile_pic_; + #ifdef ART_SEA_IR_MODE - bool sea_ir_mode_; + const bool sea_ir_mode_; #endif + + // Vector of methods to have verbose output enabled for. + const std::vector<std::string>* const verbose_methods_; + + DISALLOW_COPY_AND_ASSIGN(CompilerOptions); }; } // namespace art diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc index e479322238..2ffbd10eab 100644 --- a/compiler/elf_writer_test.cc +++ b/compiler/elf_writer_test.cc @@ -17,6 +17,7 @@ #include "elf_file.h" #include "base/stringprintf.h" +#include "base/unix_file/fd_file.h" #include "common_compiler_test.h" #include "oat.h" #include "utils.h" diff --git a/compiler/image_test.cc b/compiler/image_test.cc index cf4259f790..d5d487f03c 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -63,7 +63,7 @@ TEST_F(ImageTest, WriteRead) { ScratchFile oat_file(OS::CreateEmptyFile(oat_filename.c_str())); const uintptr_t requested_image_base = ART_BASE_ADDRESS; - ImageWriter writer(*compiler_driver_, requested_image_base); + std::unique_ptr<ImageWriter> writer(new ImageWriter(*compiler_driver_, requested_image_base)); { { jobject class_loader = NULL; @@ -83,8 +83,8 @@ TEST_F(ImageTest, WriteRead) { t.NewTiming("WriteElf"); SafeMap<std::string, std::string> key_value_store; OatWriter oat_writer(class_linker->GetBootClassPath(), 0, 0, 0, compiler_driver_.get(), - &writer, &timings, &key_value_store); - bool success = writer.PrepareImageAddressSpace() && + writer.get(), &timings, &key_value_store); + bool success = writer->PrepareImageAddressSpace() && compiler_driver_->WriteElf(GetTestAndroidRoot(), !kIsTargetBuild, class_linker->GetBootClassPath(), @@ -99,9 +99,9 @@ TEST_F(ImageTest, WriteRead) { { bool success_image = - writer.Write(image_file.GetFilename(), dup_oat->GetPath(), dup_oat->GetPath()); + writer->Write(image_file.GetFilename(), dup_oat->GetPath(), dup_oat->GetPath()); ASSERT_TRUE(success_image); - bool success_fixup = ElfWriter::Fixup(dup_oat.get(), writer.GetOatDataBegin()); + bool success_fixup = ElfWriter::Fixup(dup_oat.get(), writer->GetOatDataBegin()); ASSERT_TRUE(success_fixup); } @@ -130,14 +130,18 @@ TEST_F(ImageTest, WriteRead) { compiler_driver_.reset(); // Tear down old runtime before making a new one, clearing out misc state. + + // Remove the reservation of the memory for use to load the image. + // Need to do this before we reset the runtime. + UnreserveImageSpace(); + writer.reset(nullptr); + runtime_.reset(); java_lang_dex_file_ = NULL; + MemMap::Init(); std::unique_ptr<const DexFile> dex(LoadExpectSingleDexFile(GetLibCoreDexFileName().c_str())); - // Remove the reservation of the memory for use to load the image. - UnreserveImageSpace(); - RuntimeOptions options; std::string image("-Ximage:"); image.append(image_location.GetFilename()); diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index fd7d350a09..0fea2a7de7 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -73,10 +73,10 @@ class JniCompilerTest : public CommonCompilerTest { } ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig; if (check_generic_jni_) { - method->SetEntryPointFromQuickCompiledCode(class_linker_->GetQuickGenericJniTrampoline()); + method->SetEntryPointFromQuickCompiledCode(class_linker_->GetRuntimeQuickGenericJniStub()); } else { - if (method->GetEntryPointFromQuickCompiledCode() == nullptr || - method->GetEntryPointFromQuickCompiledCode() == class_linker_->GetQuickGenericJniTrampoline()) { + const void* code = method->GetEntryPointFromQuickCompiledCode(); + if (code == nullptr || class_linker_->IsQuickGenericJniStub(code)) { CompileMethod(method); ASSERT_TRUE(method->GetEntryPointFromQuickCompiledCode() != nullptr) << method_name << " " << method_sig; diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc index b95dad261e..29763a2a10 100644 --- a/compiler/jni/quick/arm64/calling_convention_arm64.cc +++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc @@ -21,7 +21,7 @@ namespace art { namespace arm64 { -static const Register kCoreArgumentRegisters[] = { +static const XRegister kXArgumentRegisters[] = { X0, X1, X2, X3, X4, X5, X6, X7 }; @@ -39,11 +39,11 @@ static const SRegister kSArgumentRegisters[] = { // Calling convention ManagedRegister Arm64ManagedRuntimeCallingConvention::InterproceduralScratchRegister() { - return Arm64ManagedRegister::FromCoreRegister(X20); // saved on entry restored on exit + return Arm64ManagedRegister::FromXRegister(X20); // saved on entry restored on exit } ManagedRegister Arm64JniCallingConvention::InterproceduralScratchRegister() { - return Arm64ManagedRegister::FromCoreRegister(X20); // saved on entry restored on exit + return Arm64ManagedRegister::FromXRegister(X20); // saved on entry restored on exit } static ManagedRegister ReturnRegisterForShorty(const char* shorty) { @@ -52,7 +52,7 @@ static ManagedRegister ReturnRegisterForShorty(const char* shorty) { } else if (shorty[0] == 'D') { return Arm64ManagedRegister::FromDRegister(D0); } else if (shorty[0] == 'J') { - return Arm64ManagedRegister::FromCoreRegister(X0); + return Arm64ManagedRegister::FromXRegister(X0); } else if (shorty[0] == 'V') { return Arm64ManagedRegister::NoRegister(); } else { @@ -75,7 +75,7 @@ ManagedRegister Arm64JniCallingConvention::IntReturnRegister() { // Managed runtime calling convention ManagedRegister Arm64ManagedRuntimeCallingConvention::MethodRegister() { - return Arm64ManagedRegister::FromCoreRegister(X0); + return Arm64ManagedRegister::FromXRegister(X0); } bool Arm64ManagedRuntimeCallingConvention::IsCurrentParamInRegister() { @@ -129,7 +129,7 @@ const ManagedRegisterEntrySpills& Arm64ManagedRuntimeCallingConvention::EntrySpi } else { // GP regs. if (gp_reg_index < 8) { if (IsCurrentParamALong() && (!IsCurrentParamAReference())) { - entry_spills_.push_back(Arm64ManagedRegister::FromCoreRegister(kCoreArgumentRegisters[gp_reg_index])); + entry_spills_.push_back(Arm64ManagedRegister::FromXRegister(kXArgumentRegisters[gp_reg_index])); } else { entry_spills_.push_back(Arm64ManagedRegister::FromWRegister(kWArgumentRegisters[gp_reg_index])); } @@ -154,17 +154,17 @@ Arm64JniCallingConvention::Arm64JniCallingConvention(bool is_static, bool is_syn : JniCallingConvention(is_static, is_synchronized, shorty, kFramePointerSize) { // TODO: Ugly hard code... // Should generate these according to the spill mask automatically. - callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X20)); - callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X21)); - callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X22)); - callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X23)); - callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X24)); - callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X25)); - callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X26)); - callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X27)); - callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X28)); - callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X29)); - callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X30)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X20)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X21)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X22)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X23)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X24)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X25)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X26)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X27)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X28)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X29)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X30)); } uint32_t Arm64JniCallingConvention::CoreSpillMask() const { @@ -232,7 +232,7 @@ ManagedRegister Arm64JniCallingConvention::CurrentParamRegister() { int gp_reg = itr_args_ - itr_float_and_doubles_; CHECK_LT(static_cast<unsigned int>(gp_reg), 8u); if (IsCurrentParamALong() || IsCurrentParamAReference() || IsCurrentParamJniEnv()) { - return Arm64ManagedRegister::FromCoreRegister(kCoreArgumentRegisters[gp_reg]); + return Arm64ManagedRegister::FromXRegister(kXArgumentRegisters[gp_reg]); } else { return Arm64ManagedRegister::FromWRegister(kWArgumentRegisters[gp_reg]); } diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index f6795ea28c..3c3aa02502 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -176,12 +176,8 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, // 4. Write out the end of the quick frames. if (is_64_bit_target) { __ StoreStackPointerToThread64(Thread::TopOfManagedStackOffset<8>()); - __ StoreImmediateToThread64(Thread::TopOfManagedStackPcOffset<8>(), 0, - mr_conv->InterproceduralScratchRegister()); } else { __ StoreStackPointerToThread32(Thread::TopOfManagedStackOffset<4>()); - __ StoreImmediateToThread32(Thread::TopOfManagedStackPcOffset<4>(), 0, - mr_conv->InterproceduralScratchRegister()); } // 5. Move frame down to allow space for out going args. diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 3fcc369c5e..847fa0d08f 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -44,11 +44,11 @@ class OatTest : public CommonCompilerTest { compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method->GetDexMethodIndex())); - if (compiled_method == NULL) { - EXPECT_TRUE(oat_method.GetQuickCode() == NULL) << PrettyMethod(method) << " " - << oat_method.GetQuickCode(); - EXPECT_TRUE(oat_method.GetPortableCode() == NULL) << PrettyMethod(method) << " " - << oat_method.GetPortableCode(); + if (compiled_method == nullptr) { + EXPECT_TRUE(oat_method.GetQuickCode() == nullptr) << PrettyMethod(method) << " " + << oat_method.GetQuickCode(); + EXPECT_TRUE(oat_method.GetPortableCode() == nullptr) << PrettyMethod(method) << " " + << oat_method.GetPortableCode(); EXPECT_EQ(oat_method.GetFrameSizeInBytes(), 0U); EXPECT_EQ(oat_method.GetCoreSpillMask(), 0U); EXPECT_EQ(oat_method.GetFpSpillMask(), 0U); @@ -95,7 +95,10 @@ TEST_F(OatTest, WriteRead) { : Compiler::kQuick; InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86; - InstructionSetFeatures insn_features; + std::string error_msg; + std::unique_ptr<const InstructionSetFeatures> insn_features( + InstructionSetFeatures::FromFeatureString(insn_set, "default", &error_msg)); + ASSERT_TRUE(insn_features.get() != nullptr) << error_msg; compiler_options_.reset(new CompilerOptions); verification_results_.reset(new VerificationResults(compiler_options_.get())); method_inliner_map_.reset(new DexFileToMethodInlinerMap); @@ -106,9 +109,9 @@ TEST_F(OatTest, WriteRead) { verification_results_.get(), method_inliner_map_.get(), compiler_kind, insn_set, - insn_features, false, NULL, 2, true, true, - timer_.get())); - jobject class_loader = NULL; + insn_features.get(), false, nullptr, 2, true, true, + timer_.get(), "")); + jobject class_loader = nullptr; if (kCompile) { TimingLogger timings("OatTest::WriteRead", false, false); compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings); @@ -135,9 +138,8 @@ TEST_F(OatTest, WriteRead) { if (kCompile) { // OatWriter strips the code, regenerate to compare compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings); } - std::string error_msg; - std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), NULL, false, - &error_msg)); + std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), nullptr, + false, &error_msg)); ASSERT_TRUE(oat_file.get() != nullptr) << error_msg; const OatHeader& oat_header = oat_file->GetOatHeader(); ASSERT_TRUE(oat_header.IsValid()); @@ -157,7 +159,7 @@ TEST_F(OatTest, WriteRead) { const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); const uint8_t* class_data = dex_file->GetClassData(class_def); size_t num_virtual_methods = 0; - if (class_data != NULL) { + if (class_data != nullptr) { ClassDataItemIterator it(*dex_file, class_data); num_virtual_methods = it.NumVirtualMethods(); } @@ -193,13 +195,16 @@ TEST_F(OatTest, OatHeaderSizeCheck) { } TEST_F(OatTest, OatHeaderIsValid) { - InstructionSet instruction_set = kX86; - InstructionSetFeatures instruction_set_features; + InstructionSet insn_set = kX86; + std::string error_msg; + std::unique_ptr<const InstructionSetFeatures> insn_features( + InstructionSetFeatures::FromFeatureString(insn_set, "default", &error_msg)); + ASSERT_TRUE(insn_features.get() != nullptr) << error_msg; std::vector<const DexFile*> dex_files; uint32_t image_file_location_oat_checksum = 0; uint32_t image_file_location_oat_begin = 0; - std::unique_ptr<OatHeader> oat_header(OatHeader::Create(instruction_set, - instruction_set_features, + std::unique_ptr<OatHeader> oat_header(OatHeader::Create(insn_set, + insn_features.get(), &dex_files, image_file_location_oat_checksum, image_file_location_oat_begin, diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 5bcc65b03b..79cbd0ee21 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -171,6 +171,7 @@ HGraph* HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) { const uint16_t* code_ptr = code_item.insns_; const uint16_t* code_end = code_item.insns_ + code_item.insns_size_in_code_units_; + code_start_ = code_ptr; // Setup the graph with the entry block and exit block. graph_ = new (arena_) HGraph(arena_); @@ -267,6 +268,13 @@ HBasicBlock* HGraphBuilder::FindBlockStartingAt(int32_t index) const { } template<typename T> +void HGraphBuilder::Unop_12x(const Instruction& instruction, Primitive::Type type) { + HInstruction* first = LoadLocal(instruction.VRegB(), type); + current_block_->AddInstruction(new (arena_) T(type, first)); + UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); +} + +template<typename T> void HGraphBuilder::Binop_23x(const Instruction& instruction, Primitive::Type type) { HInstruction* first = LoadLocal(instruction.VRegB(), type); HInstruction* second = LoadLocal(instruction.VRegC(), type); @@ -409,6 +417,7 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, DCHECK_EQ(argument_index, number_of_arguments); current_block_->AddInstruction(invoke); + latest_result_ = invoke; return true; } @@ -496,6 +505,62 @@ void HGraphBuilder::BuildArrayAccess(const Instruction& instruction, } } +void HGraphBuilder::BuildFilledNewArray(uint32_t dex_offset, + uint32_t type_index, + uint32_t number_of_vreg_arguments, + bool is_range, + uint32_t* args, + uint32_t register_index) { + HInstruction* length = GetIntConstant(number_of_vreg_arguments); + HInstruction* object = new (arena_) HNewArray(length, dex_offset, type_index); + current_block_->AddInstruction(object); + + const char* descriptor = dex_file_->StringByTypeIdx(type_index); + DCHECK_EQ(descriptor[0], '[') << descriptor; + char primitive = descriptor[1]; + DCHECK(primitive == 'I' + || primitive == 'L' + || primitive == '[') << descriptor; + bool is_reference_array = (primitive == 'L') || (primitive == '['); + Primitive::Type type = is_reference_array ? Primitive::kPrimNot : Primitive::kPrimInt; + + Temporaries temps(graph_, 1); + temps.Add(object); + for (size_t i = 0; i < number_of_vreg_arguments; ++i) { + HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type); + HInstruction* index = GetIntConstant(i); + current_block_->AddInstruction( + new (arena_) HArraySet(object, index, value, type, dex_offset)); + } + latest_result_ = object; +} + +template <typename T> +void HGraphBuilder::BuildFillArrayData(HInstruction* object, + const T* data, + uint32_t element_count, + Primitive::Type anticipated_type, + uint32_t dex_offset) { + for (uint32_t i = 0; i < element_count; ++i) { + HInstruction* index = GetIntConstant(i); + HInstruction* value = GetIntConstant(data[i]); + current_block_->AddInstruction(new (arena_) HArraySet( + object, index, value, anticipated_type, dex_offset)); + } +} + +void HGraphBuilder::BuildFillWideArrayData(HInstruction* object, + const uint64_t* data, + uint32_t element_count, + uint32_t dex_offset) { + for (uint32_t i = 0; i < element_count; ++i) { + HInstruction* index = GetIntConstant(i); + HInstruction* value = GetLongConstant(data[i]); + current_block_->AddInstruction(new (arena_) HArraySet( + object, index, value, Primitive::kPrimLong, dex_offset)); + } +} + void HGraphBuilder::PotentiallyAddSuspendCheck(int32_t target_offset, uint32_t dex_offset) { if (target_offset <= 0) { // Unconditionnally add a suspend check to backward branches. We can remove @@ -678,6 +743,16 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::NEG_INT: { + Unop_12x<HNeg>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::NOT_INT: { + Unop_12x<HNot>(instruction, Primitive::kPrimInt); + break; + } + case Instruction::ADD_INT: { Binop_23x<HAdd>(instruction, Primitive::kPrimInt); break; @@ -708,11 +783,41 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::SUB_FLOAT: { + Binop_23x<HSub>(instruction, Primitive::kPrimFloat); + break; + } + + case Instruction::SUB_DOUBLE: { + Binop_23x<HSub>(instruction, Primitive::kPrimDouble); + break; + } + case Instruction::ADD_INT_2ADDR: { Binop_12x<HAdd>(instruction, Primitive::kPrimInt); break; } + case Instruction::MUL_INT: { + Binop_23x<HMul>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::MUL_LONG: { + Binop_23x<HMul>(instruction, Primitive::kPrimLong); + break; + } + + case Instruction::MUL_FLOAT: { + Binop_23x<HMul>(instruction, Primitive::kPrimFloat); + break; + } + + case Instruction::MUL_DOUBLE: { + Binop_23x<HMul>(instruction, Primitive::kPrimDouble); + break; + } + case Instruction::ADD_LONG_2ADDR: { Binop_12x<HAdd>(instruction, Primitive::kPrimLong); break; @@ -738,6 +843,36 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::SUB_FLOAT_2ADDR: { + Binop_12x<HSub>(instruction, Primitive::kPrimFloat); + break; + } + + case Instruction::SUB_DOUBLE_2ADDR: { + Binop_12x<HSub>(instruction, Primitive::kPrimDouble); + break; + } + + case Instruction::MUL_INT_2ADDR: { + Binop_12x<HMul>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::MUL_LONG_2ADDR: { + Binop_12x<HMul>(instruction, Primitive::kPrimLong); + break; + } + + case Instruction::MUL_FLOAT_2ADDR: { + Binop_12x<HMul>(instruction, Primitive::kPrimFloat); + break; + } + + case Instruction::MUL_DOUBLE_2ADDR: { + Binop_12x<HMul>(instruction, Primitive::kPrimDouble); + break; + } + case Instruction::ADD_INT_LIT16: { Binop_22s<HAdd>(instruction, false); break; @@ -748,6 +883,11 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::MUL_INT_LIT16: { + Binop_22s<HMul>(instruction, false); + break; + } + case Instruction::ADD_INT_LIT8: { Binop_22b<HAdd>(instruction, false); break; @@ -758,6 +898,11 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::MUL_INT_LIT8: { + Binop_22b<HMul>(instruction, false); + break; + } + case Instruction::NEW_INSTANCE: { current_block_->AddInstruction( new (arena_) HNewInstance(dex_offset, instruction.VRegB_21c())); @@ -765,10 +910,88 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::NEW_ARRAY: { + HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt); + current_block_->AddInstruction( + new (arena_) HNewArray(length, dex_offset, instruction.VRegC_22c())); + UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction()); + break; + } + + case Instruction::FILLED_NEW_ARRAY: { + uint32_t number_of_vreg_arguments = instruction.VRegA_35c(); + uint32_t type_index = instruction.VRegB_35c(); + uint32_t args[5]; + instruction.GetVarArgs(args); + BuildFilledNewArray(dex_offset, type_index, number_of_vreg_arguments, false, args, 0); + break; + } + + case Instruction::FILLED_NEW_ARRAY_RANGE: { + uint32_t number_of_vreg_arguments = instruction.VRegA_3rc(); + uint32_t type_index = instruction.VRegB_3rc(); + uint32_t register_index = instruction.VRegC_3rc(); + BuildFilledNewArray( + dex_offset, type_index, number_of_vreg_arguments, true, nullptr, register_index); + break; + } + + case Instruction::FILL_ARRAY_DATA: { + Temporaries temps(graph_, 1); + HInstruction* array = LoadLocal(instruction.VRegA_31t(), Primitive::kPrimNot); + HNullCheck* null_check = new (arena_) HNullCheck(array, dex_offset); + current_block_->AddInstruction(null_check); + temps.Add(null_check); + + HInstruction* length = new (arena_) HArrayLength(null_check); + current_block_->AddInstruction(length); + + int32_t payload_offset = instruction.VRegB_31t() + dex_offset; + const Instruction::ArrayDataPayload* payload = + reinterpret_cast<const Instruction::ArrayDataPayload*>(code_start_ + payload_offset); + const uint8_t* data = payload->data; + uint32_t element_count = payload->element_count; + + // Implementation of this DEX instruction seems to be that the bounds check is + // done before doing any stores. + HInstruction* last_index = GetIntConstant(payload->element_count - 1); + current_block_->AddInstruction(new (arena_) HBoundsCheck(last_index, length, dex_offset)); + + switch (payload->element_width) { + case 1: + BuildFillArrayData(null_check, data, element_count, Primitive::kPrimByte, dex_offset); + break; + case 2: + BuildFillArrayData(null_check, + reinterpret_cast<const uint16_t*>(data), + element_count, + Primitive::kPrimShort, + dex_offset); + break; + case 4: + BuildFillArrayData(null_check, + reinterpret_cast<const uint32_t*>(data), + element_count, + Primitive::kPrimInt, + dex_offset); + break; + case 8: + BuildFillWideArrayData(null_check, + reinterpret_cast<const uint64_t*>(data), + element_count, + dex_offset); + break; + default: + LOG(FATAL) << "Unknown element width for " << payload->element_width; + } + break; + } + case Instruction::MOVE_RESULT: case Instruction::MOVE_RESULT_WIDE: case Instruction::MOVE_RESULT_OBJECT: - UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); + UpdateLocal(instruction.VRegA(), latest_result_); + latest_result_ = nullptr; break; case Instruction::CMP_LONG: { diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index e68cdb0b1d..c5e02db88c 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -48,7 +48,9 @@ class HGraphBuilder : public ValueObject { dex_file_(dex_file), dex_compilation_unit_(dex_compilation_unit), compiler_driver_(driver), - return_type_(Primitive::GetType(dex_compilation_unit_->GetShorty()[0])) {} + return_type_(Primitive::GetType(dex_compilation_unit_->GetShorty()[0])), + code_start_(nullptr), + latest_result_(nullptr) {} // Only for unit testing. HGraphBuilder(ArenaAllocator* arena, Primitive::Type return_type = Primitive::kPrimInt) @@ -64,7 +66,9 @@ class HGraphBuilder : public ValueObject { dex_file_(nullptr), dex_compilation_unit_(nullptr), compiler_driver_(nullptr), - return_type_(return_type) {} + return_type_(return_type), + code_start_(nullptr), + latest_result_(nullptr) {} HGraph* BuildGraph(const DexFile::CodeItem& code); @@ -95,6 +99,9 @@ class HGraphBuilder : public ValueObject { bool InitializeParameters(uint16_t number_of_parameters); template<typename T> + void Unop_12x(const Instruction& instruction, Primitive::Type type); + + template<typename T> void Binop_23x(const Instruction& instruction, Primitive::Type type); template<typename T> @@ -126,6 +133,31 @@ class HGraphBuilder : public ValueObject { uint32_t* args, uint32_t register_index); + // Builds a new array node and the instructions that fill it. + void BuildFilledNewArray(uint32_t dex_offset, + uint32_t type_index, + uint32_t number_of_vreg_arguments, + bool is_range, + uint32_t* args, + uint32_t register_index); + + // Fills the given object with data as specified in the fill-array-data + // instruction. Currently only used for non-reference and non-floating point + // arrays. + template <typename T> + void BuildFillArrayData(HInstruction* object, + const T* data, + uint32_t element_count, + Primitive::Type anticipated_type, + uint32_t dex_offset); + + // Fills the given object with data as specified in the fill-array-data + // instruction. The data must be for long and double arrays. + void BuildFillWideArrayData(HInstruction* object, + const uint64_t* data, + uint32_t element_count, + uint32_t dex_offset); + ArenaAllocator* const arena_; // A list of the size of the dex code holding block information for @@ -148,6 +180,14 @@ class HGraphBuilder : public ValueObject { CompilerDriver* const compiler_driver_; const Primitive::Type return_type_; + // The pointer in the dex file where the instructions of the code item + // being currently compiled start. + const uint16_t* code_start_; + + // The last invoke or fill-new-array being built. Only to be + // used by move-result instructions. + HInstruction* latest_result_; + DISALLOW_COPY_AND_ASSIGN(HGraphBuilder); }; diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 29dbd8b33d..c4286a401b 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -17,6 +17,7 @@ #include "code_generator.h" #include "code_generator_arm.h" +#include "code_generator_arm64.h" #include "code_generator_x86.h" #include "code_generator_x86_64.h" #include "compiled_method.h" @@ -36,7 +37,7 @@ void CodeGenerator::CompileBaseline(CodeAllocator* allocator, bool is_leaf) { const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks(); DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock()); DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1))); - block_labels_.SetSize(blocks.Size()); + Initialize(); DCHECK_EQ(frame_size_, kUninitializedFrameSize); if (!is_leaf) { @@ -54,7 +55,7 @@ void CodeGenerator::CompileBaseline(CodeAllocator* allocator, bool is_leaf) { HGraphVisitor* instruction_visitor = GetInstructionVisitor(); for (size_t i = 0, e = blocks.Size(); i < e; ++i) { HBasicBlock* block = blocks.Get(i); - Bind(GetLabelOf(block)); + Bind(block); for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* current = it.Current(); current->Accept(location_builder); @@ -76,13 +77,13 @@ void CodeGenerator::CompileOptimized(CodeAllocator* allocator) { const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks(); DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock()); DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1))); - block_labels_.SetSize(blocks.Size()); + Initialize(); GenerateFrameEntry(); HGraphVisitor* instruction_visitor = GetInstructionVisitor(); for (size_t i = 0, e = blocks.Size(); i < e; ++i) { HBasicBlock* block = blocks.Get(i); - Bind(GetLabelOf(block)); + Bind(block); for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* current = it.Current(); current->Accept(instruction_visitor); @@ -273,10 +274,6 @@ bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) con return current->GetBlockId() + 1 == next->GetBlockId(); } -Label* CodeGenerator::GetLabelOf(HBasicBlock* block) const { - return block_labels_.GetRawStorage() + block->GetBlockId(); -} - CodeGenerator* CodeGenerator::Create(ArenaAllocator* allocator, HGraph* graph, InstructionSet instruction_set) { @@ -285,6 +282,9 @@ CodeGenerator* CodeGenerator::Create(ArenaAllocator* allocator, case kThumb2: { return new (allocator) arm::CodeGeneratorARM(graph); } + case kArm64: { + return new (allocator) arm64::CodeGeneratorARM64(graph); + } case kMips: return nullptr; case kX86: { @@ -477,8 +477,7 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc) { case Location::kRegister : { int id = location.reg(); stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, id); - if (current->GetType() == Primitive::kPrimDouble - || current->GetType() == Primitive::kPrimLong) { + if (current->GetType() == Primitive::kPrimLong) { stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, id); ++i; DCHECK_LT(i, environment_size); @@ -486,52 +485,55 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc) { break; } + case Location::kFpuRegister : { + int id = location.reg(); + stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInFpuRegister, id); + if (current->GetType() == Primitive::kPrimDouble) { + stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInFpuRegister, id); + ++i; + DCHECK_LT(i, environment_size); + } + break; + } + default: LOG(FATAL) << "Unexpected kind " << location.GetKind(); } } } -size_t CodeGenerator::GetStackOffsetOfSavedRegister(size_t index) { - return first_register_slot_in_slow_path_ + index * GetWordSize(); -} - void CodeGenerator::SaveLiveRegisters(LocationSummary* locations) { RegisterSet* register_set = locations->GetLiveRegisters(); - uint32_t count = 0; + size_t stack_offset = first_register_slot_in_slow_path_; for (size_t i = 0, e = GetNumberOfCoreRegisters(); i < e; ++i) { if (register_set->ContainsCoreRegister(i)) { - size_t stack_offset = GetStackOffsetOfSavedRegister(count); - ++count; - SaveCoreRegister(Location::StackSlot(stack_offset), i); // If the register holds an object, update the stack mask. if (locations->RegisterContainsObject(i)) { locations->SetStackBit(stack_offset / kVRegSize); } + stack_offset += SaveCoreRegister(stack_offset, i); } } for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) { if (register_set->ContainsFloatingPointRegister(i)) { - LOG(FATAL) << "Unimplemented"; + stack_offset += SaveFloatingPointRegister(stack_offset, i); } } } void CodeGenerator::RestoreLiveRegisters(LocationSummary* locations) { RegisterSet* register_set = locations->GetLiveRegisters(); - uint32_t count = 0; + size_t stack_offset = first_register_slot_in_slow_path_; for (size_t i = 0, e = GetNumberOfCoreRegisters(); i < e; ++i) { if (register_set->ContainsCoreRegister(i)) { - size_t stack_offset = GetStackOffsetOfSavedRegister(count); - ++count; - RestoreCoreRegister(Location::StackSlot(stack_offset), i); + stack_offset += RestoreCoreRegister(stack_offset, i); } } for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) { if (register_set->ContainsFloatingPointRegister(i)) { - LOG(FATAL) << "Unimplemented"; + stack_offset += RestoreFloatingPointRegister(stack_offset, i); } } } diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 4eba791723..220d745561 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -24,13 +24,13 @@ #include "memory_region.h" #include "nodes.h" #include "stack_map_stream.h" -#include "utils/assembler.h" namespace art { static size_t constexpr kVRegSize = 4; static size_t constexpr kUninitializedFrameSize = 0; +class Assembler; class CodeGenerator; class DexCompilationUnit; class SrcMap; @@ -53,18 +53,12 @@ struct PcInfo { class SlowPathCode : public ArenaObject { public: - SlowPathCode() : entry_label_(), exit_label_() {} + SlowPathCode() {} virtual ~SlowPathCode() {} - Label* GetEntryLabel() { return &entry_label_; } - Label* GetExitLabel() { return &exit_label_; } - virtual void EmitNativeCode(CodeGenerator* codegen) = 0; private: - Label entry_label_; - Label exit_label_; - DISALLOW_COPY_AND_ASSIGN(SlowPathCode); }; @@ -80,7 +74,6 @@ class CodeGenerator : public ArenaObject { HGraph* GetGraph() const { return graph_; } - Label* GetLabelOf(HBasicBlock* block) const; bool GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const; size_t GetStackSlotOfParameter(HParameterValue* parameter) const { @@ -90,9 +83,10 @@ class CodeGenerator : public ArenaObject { + parameter->GetIndex() * kVRegSize; } + virtual void Initialize() = 0; virtual void GenerateFrameEntry() = 0; virtual void GenerateFrameExit() = 0; - virtual void Bind(Label* label) = 0; + virtual void Bind(HBasicBlock* block) = 0; virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) = 0; virtual HGraphVisitor* GetLocationBuilder() = 0; virtual HGraphVisitor* GetInstructionVisitor() = 0; @@ -116,8 +110,18 @@ class CodeGenerator : public ArenaObject { virtual void DumpCoreRegister(std::ostream& stream, int reg) const = 0; virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const = 0; virtual InstructionSet GetInstructionSet() const = 0; - virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) = 0; - virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) = 0; + // Saves the register in the stack. Returns the size taken on stack. + virtual size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) = 0; + // Restores the register from the stack. Returns the size taken on stack. + virtual size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) = 0; + virtual size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) { + LOG(FATAL) << "Unimplemented"; + return 0u; + } + virtual size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) { + LOG(FATAL) << "Unimplemented"; + return 0u; + } void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc); @@ -151,6 +155,7 @@ class CodeGenerator : public ArenaObject { void ClearSpillSlotsFromLoopPhisInStackMap(HSuspendCheck* suspend_check) const; bool* GetBlockedCoreRegisters() const { return blocked_core_registers_; } + bool* GetBlockedFloatingPointRegisters() const { return blocked_fpu_registers_; } protected: CodeGenerator(HGraph* graph, @@ -167,7 +172,6 @@ class CodeGenerator : public ArenaObject { number_of_fpu_registers_(number_of_fpu_registers), number_of_register_pairs_(number_of_register_pairs), graph_(graph), - block_labels_(graph->GetArena(), 0), pc_infos_(graph->GetArena(), 32), slow_paths_(graph->GetArena(), 8), is_leaf_(true), @@ -205,8 +209,6 @@ class CodeGenerator : public ArenaObject { HGraph* const graph_; - // Labels for each block that will be compiled. - GrowableArray<Label> block_labels_; GrowableArray<PcInfo> pc_infos_; GrowableArray<SlowPathCode*> slow_paths_; diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 2be5c90ed6..7b00d2f523 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -60,7 +60,21 @@ class InvokeRuntimeCallingConvention : public CallingConvention<Register, DRegis #define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())-> -class NullCheckSlowPathARM : public SlowPathCode { +class SlowPathCodeARM : public SlowPathCode { + public: + SlowPathCodeARM() : entry_label_(), exit_label_() {} + + Label* GetEntryLabel() { return &entry_label_; } + Label* GetExitLabel() { return &exit_label_; } + + private: + Label entry_label_; + Label exit_label_; + + DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM); +}; + +class NullCheckSlowPathARM : public SlowPathCodeARM { public: explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {} @@ -77,7 +91,7 @@ class NullCheckSlowPathARM : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM); }; -class StackOverflowCheckSlowPathARM : public SlowPathCode { +class StackOverflowCheckSlowPathARM : public SlowPathCodeARM { public: StackOverflowCheckSlowPathARM() {} @@ -91,12 +105,13 @@ class StackOverflowCheckSlowPathARM : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM); }; -class SuspendCheckSlowPathARM : public SlowPathCode { +class SuspendCheckSlowPathARM : public SlowPathCodeARM { public: explicit SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor) : instruction_(instruction), successor_(successor) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); __ Bind(GetEntryLabel()); codegen->SaveLiveRegisters(instruction_->GetLocations()); int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pTestSuspend).Int32Value(); @@ -107,7 +122,7 @@ class SuspendCheckSlowPathARM : public SlowPathCode { if (successor_ == nullptr) { __ b(GetReturnLabel()); } else { - __ b(codegen->GetLabelOf(successor_)); + __ b(arm_codegen->GetLabelOf(successor_)); } } @@ -127,7 +142,7 @@ class SuspendCheckSlowPathARM : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM); }; -class BoundsCheckSlowPathARM : public SlowPathCode { +class BoundsCheckSlowPathARM : public SlowPathCodeARM { public: BoundsCheckSlowPathARM(HBoundsCheck* instruction, Location index_location, @@ -137,7 +152,7 @@ class BoundsCheckSlowPathARM : public SlowPathCode { length_location_(length_location) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { - CodeGeneratorARM* arm_codegen = reinterpret_cast<CodeGeneratorARM*>(codegen); + CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); __ Bind(GetEntryLabel()); InvokeRuntimeCallingConvention calling_convention; arm_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_); @@ -195,16 +210,19 @@ void CodeGeneratorARM::DumpFloatingPointRegister(std::ostream& stream, int reg) stream << ArmManagedRegister::FromDRegister(DRegister(reg)); } -void CodeGeneratorARM::SaveCoreRegister(Location stack_location, uint32_t reg_id) { - __ StoreToOffset(kStoreWord, static_cast<Register>(reg_id), SP, stack_location.GetStackIndex()); +size_t CodeGeneratorARM::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { + __ StoreToOffset(kStoreWord, static_cast<Register>(reg_id), SP, stack_index); + return kArmWordSize; } -void CodeGeneratorARM::RestoreCoreRegister(Location stack_location, uint32_t reg_id) { - __ LoadFromOffset(kLoadWord, static_cast<Register>(reg_id), SP, stack_location.GetStackIndex()); +size_t CodeGeneratorARM::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) { + __ LoadFromOffset(kLoadWord, static_cast<Register>(reg_id), SP, stack_index); + return kArmWordSize; } CodeGeneratorARM::CodeGeneratorARM(HGraph* graph) : CodeGenerator(graph, kNumberOfCoreRegisters, kNumberOfDRegisters, kNumberOfRegisterPairs), + block_labels_(graph->GetArena(), 0), location_builder_(graph, this), instruction_visitor_(graph, this), move_resolver_(graph->GetArena(), this), @@ -220,19 +238,12 @@ Location CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type) const { size_t reg = FindFreeEntry(blocked_register_pairs_, kNumberOfRegisterPairs); ArmManagedRegister pair = ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg)); + DCHECK(!blocked_core_registers_[pair.AsRegisterPairLow()]); + DCHECK(!blocked_core_registers_[pair.AsRegisterPairHigh()]); + blocked_core_registers_[pair.AsRegisterPairLow()] = true; blocked_core_registers_[pair.AsRegisterPairHigh()] = true; - // Block all other register pairs that share a register with `pair`. - for (int i = 0; i < kNumberOfRegisterPairs; i++) { - ArmManagedRegister current = - ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); - if (current.AsRegisterPairLow() == pair.AsRegisterPairLow() - || current.AsRegisterPairLow() == pair.AsRegisterPairHigh() - || current.AsRegisterPairHigh() == pair.AsRegisterPairLow() - || current.AsRegisterPairHigh() == pair.AsRegisterPairHigh()) { - blocked_register_pairs_[i] = true; - } - } + UpdateBlockedPairRegisters(); return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh()); } @@ -278,7 +289,6 @@ void CodeGeneratorARM::SetupBlockedRegisters() const { // Reserve R4 for suspend check. blocked_core_registers_[R4] = true; - blocked_register_pairs_[R4_R5] = true; // Reserve thread register. blocked_core_registers_[TR] = true; @@ -302,6 +312,19 @@ void CodeGeneratorARM::SetupBlockedRegisters() const { blocked_fpu_registers_[D13] = true; blocked_fpu_registers_[D14] = true; blocked_fpu_registers_[D15] = true; + + UpdateBlockedPairRegisters(); +} + +void CodeGeneratorARM::UpdateBlockedPairRegisters() const { + for (int i = 0; i < kNumberOfRegisterPairs; i++) { + ArmManagedRegister current = + ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); + if (blocked_core_registers_[current.AsRegisterPairLow()] + || blocked_core_registers_[current.AsRegisterPairHigh()]) { + blocked_register_pairs_[i] = true; + } + } } InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen) @@ -313,7 +336,7 @@ void CodeGeneratorARM::GenerateFrameEntry() { bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm); if (!skip_overflow_check) { if (kExplicitStackOverflowCheck) { - SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM(); + SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM(); AddSlowPath(slow_path); __ LoadFromOffset(kLoadWord, IP, TR, Thread::StackEndOffset<kArmWordSize>().Int32Value()); @@ -339,8 +362,8 @@ void CodeGeneratorARM::GenerateFrameExit() { __ PopList(1 << PC | 1 << R6 | 1 << R7); } -void CodeGeneratorARM::Bind(Label* label) { - __ Bind(label); +void CodeGeneratorARM::Bind(HBasicBlock* block) { + __ Bind(GetLabelOf(block)); } Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const { @@ -527,7 +550,7 @@ void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstr return; } - if (instruction->AsIntConstant() != nullptr) { + if (instruction->IsIntConstant()) { int32_t value = instruction->AsIntConstant()->GetValue(); if (location.IsRegister()) { __ LoadImmediate(location.As<Register>(), value); @@ -536,7 +559,7 @@ void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstr __ LoadImmediate(IP, value); __ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex()); } - } else if (instruction->AsLongConstant() != nullptr) { + } else if (instruction->IsLongConstant()) { int64_t value = instruction->AsLongConstant()->GetValue(); if (location.IsRegisterPair()) { __ LoadImmediate(location.AsRegisterPairLow<Register>(), Low32Bits(value)); @@ -548,7 +571,7 @@ void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstr __ LoadImmediate(IP, High32Bits(value)); __ StoreToOffset(kStoreWord, IP, SP, location.GetHighStackIndex(kArmWordSize)); } - } else if (instruction->AsLoadLocal() != nullptr) { + } else if (instruction->IsLoadLocal()) { uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal()); switch (instruction->GetType()) { case Primitive::kPrimBoolean: @@ -635,42 +658,57 @@ void LocationsBuilderARM::VisitIf(HIf* if_instr) { new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall); HInstruction* cond = if_instr->InputAt(0); if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); + locations->SetInAt(0, Location::RequiresRegister()); } } void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) { HInstruction* cond = if_instr->InputAt(0); - if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { - // Condition has been materialized, compare the output to 0 - DCHECK(if_instr->GetLocations()->InAt(0).IsRegister()); - __ cmp(if_instr->GetLocations()->InAt(0).As<Register>(), - ShifterOperand(0)); - __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), NE); + if (cond->IsIntConstant()) { + // Constant condition, statically compared against 1. + int32_t cond_value = cond->AsIntConstant()->GetValue(); + if (cond_value == 1) { + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), + if_instr->IfTrueSuccessor())) { + __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + } + return; + } else { + DCHECK_EQ(cond_value, 0); + } } else { - // Condition has not been materialized, use its inputs as the comparison and its - // condition as the branch condition. - LocationSummary* locations = cond->GetLocations(); - if (locations->InAt(1).IsRegister()) { - __ cmp(locations->InAt(0).As<Register>(), - ShifterOperand(locations->InAt(1).As<Register>())); + if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { + // Condition has been materialized, compare the output to 0 + DCHECK(if_instr->GetLocations()->InAt(0).IsRegister()); + __ cmp(if_instr->GetLocations()->InAt(0).As<Register>(), + ShifterOperand(0)); + __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), NE); } else { - DCHECK(locations->InAt(1).IsConstant()); - int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue(); - ShifterOperand operand; - if (ShifterOperand::CanHoldArm(value, &operand)) { - __ cmp(locations->InAt(0).As<Register>(), ShifterOperand(value)); + // Condition has not been materialized, use its inputs as the + // comparison and its condition as the branch condition. + LocationSummary* locations = cond->GetLocations(); + if (locations->InAt(1).IsRegister()) { + __ cmp(locations->InAt(0).As<Register>(), + ShifterOperand(locations->InAt(1).As<Register>())); } else { - Register temp = IP; - __ LoadImmediate(temp, value); - __ cmp(locations->InAt(0).As<Register>(), ShifterOperand(temp)); + DCHECK(locations->InAt(1).IsConstant()); + int32_t value = + locations->InAt(1).GetConstant()->AsIntConstant()->GetValue(); + ShifterOperand operand; + if (ShifterOperand::CanHoldArm(value, &operand)) { + __ cmp(locations->InAt(0).As<Register>(), ShifterOperand(value)); + } else { + Register temp = IP; + __ LoadImmediate(temp, value); + __ cmp(locations->InAt(0).As<Register>(), ShifterOperand(temp)); + } } + __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), + ARMCondition(cond->AsCondition()->GetCondition())); } - __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), - ARMCondition(cond->AsCondition()->GetCondition())); } - - if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) { + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), + if_instr->IfFalseSuccessor())) { __ b(codegen_->GetLabelOf(if_instr->IfFalseSuccessor())); } } @@ -679,10 +717,10 @@ void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) { void LocationsBuilderARM::VisitCondition(HCondition* comp) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt(1, Location::RegisterOrConstant(comp->InputAt(1)), Location::kDiesAtEntry); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(comp->InputAt(1))); if (comp->NeedsMaterialization()) { - locations->SetOut(Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } } @@ -810,6 +848,7 @@ void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) { } void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant) { + // Will be generated at use site. } void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) { @@ -822,6 +861,26 @@ void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant) { // Will be generated at use site. } +void LocationsBuilderARM::VisitFloatConstant(HFloatConstant* constant) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorARM::VisitFloatConstant(HFloatConstant* constant) { + // Will be generated at use site. +} + +void LocationsBuilderARM::VisitDoubleConstant(HDoubleConstant* constant) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorARM::VisitDoubleConstant(HDoubleConstant* constant) { + // Will be generated at use site. +} + void LocationsBuilderARM::VisitReturnVoid(HReturnVoid* ret) { ret->SetLocations(nullptr); } @@ -979,16 +1038,57 @@ void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) { codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } +void LocationsBuilderARM::VisitNeg(HNeg* neg) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); + switch (neg->GetResultType()) { + case Primitive::kPrimInt: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType(); + break; + + default: + LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); + } +} + +void InstructionCodeGeneratorARM::VisitNeg(HNeg* neg) { + LocationSummary* locations = neg->GetLocations(); + Location out = locations->Out(); + Location in = locations->InAt(0); + switch (neg->GetResultType()) { + case Primitive::kPrimInt: + DCHECK(in.IsRegister()); + __ rsb(out.As<Register>(), in.As<Register>(), ShifterOperand(0)); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType(); + break; + + default: + LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); + } +} + void LocationsBuilderARM::VisitAdd(HAdd* add) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall); switch (add->GetResultType()) { case Primitive::kPrimInt: case Primitive::kPrimLong: { - bool dies_at_entry = add->GetResultType() != Primitive::kPrimLong; - locations->SetInAt(0, Location::RequiresRegister(), dies_at_entry); - locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)), dies_at_entry); - locations->SetOut(Location::RequiresRegister()); + bool output_overlaps = (add->GetResultType() == Primitive::kPrimLong); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), output_overlaps); break; } @@ -1051,59 +1151,149 @@ void LocationsBuilderARM::VisitSub(HSub* sub) { switch (sub->GetResultType()) { case Primitive::kPrimInt: case Primitive::kPrimLong: { - bool dies_at_entry = sub->GetResultType() != Primitive::kPrimLong; - locations->SetInAt(0, Location::RequiresRegister(), dies_at_entry); - locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)), dies_at_entry); - locations->SetOut(Location::RequiresRegister()); + bool output_overlaps = (sub->GetResultType() == Primitive::kPrimLong); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), output_overlaps); break; } - - case Primitive::kPrimBoolean: - case Primitive::kPrimByte: - case Primitive::kPrimChar: - case Primitive::kPrimShort: - LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister()); break; - + } default: - LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType(); + LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); } } void InstructionCodeGeneratorARM::VisitSub(HSub* sub) { LocationSummary* locations = sub->GetLocations(); + Location out = locations->Out(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); switch (sub->GetResultType()) { case Primitive::kPrimInt: { - if (locations->InAt(1).IsRegister()) { - __ sub(locations->Out().As<Register>(), - locations->InAt(0).As<Register>(), - ShifterOperand(locations->InAt(1).As<Register>())); + if (second.IsRegister()) { + __ sub(out.As<Register>(), first.As<Register>(), ShifterOperand(second.As<Register>())); } else { - __ AddConstant(locations->Out().As<Register>(), - locations->InAt(0).As<Register>(), - -locations->InAt(1).GetConstant()->AsIntConstant()->GetValue()); + __ AddConstant(out.As<Register>(), + first.As<Register>(), + -second.GetConstant()->AsIntConstant()->GetValue()); } break; } - case Primitive::kPrimLong: - __ subs(locations->Out().AsRegisterPairLow<Register>(), - locations->InAt(0).AsRegisterPairLow<Register>(), - ShifterOperand(locations->InAt(1).AsRegisterPairLow<Register>())); - __ sbc(locations->Out().AsRegisterPairHigh<Register>(), - locations->InAt(0).AsRegisterPairHigh<Register>(), - ShifterOperand(locations->InAt(1).AsRegisterPairHigh<Register>())); + case Primitive::kPrimLong: { + __ subs(out.AsRegisterPairLow<Register>(), + first.AsRegisterPairLow<Register>(), + ShifterOperand(second.AsRegisterPairLow<Register>())); + __ sbc(out.AsRegisterPairHigh<Register>(), + first.AsRegisterPairHigh<Register>(), + ShifterOperand(second.AsRegisterPairHigh<Register>())); break; + } - case Primitive::kPrimBoolean: - case Primitive::kPrimByte: - case Primitive::kPrimChar: - case Primitive::kPrimShort: + case Primitive::kPrimFloat: { + __ vsubs(FromDToLowS(out.As<DRegister>()), + FromDToLowS(first.As<DRegister>()), + FromDToLowS(second.As<DRegister>())); + break; + } + + case Primitive::kPrimDouble: { + __ vsubd(out.As<DRegister>(), first.As<DRegister>(), second.As<DRegister>()); + break; + } + + + default: LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); + } +} + +void LocationsBuilderARM::VisitMul(HMul* mul) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall); + switch (mul->GetResultType()) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister()); + break; + } + + default: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + } +} + +void InstructionCodeGeneratorARM::VisitMul(HMul* mul) { + LocationSummary* locations = mul->GetLocations(); + Location out = locations->Out(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + switch (mul->GetResultType()) { + case Primitive::kPrimInt: { + __ mul(out.As<Register>(), first.As<Register>(), second.As<Register>()); + break; + } + case Primitive::kPrimLong: { + Register out_hi = out.AsRegisterPairHigh<Register>(); + Register out_lo = out.AsRegisterPairLow<Register>(); + Register in1_hi = first.AsRegisterPairHigh<Register>(); + Register in1_lo = first.AsRegisterPairLow<Register>(); + Register in2_hi = second.AsRegisterPairHigh<Register>(); + Register in2_lo = second.AsRegisterPairLow<Register>(); + + // Extra checks to protect caused by the existence of R1_R2. + // The algorithm is wrong if out.hi is either in1.lo or in2.lo: + // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2); + DCHECK_NE(out_hi, in1_lo); + DCHECK_NE(out_hi, in2_lo); + + // input: in1 - 64 bits, in2 - 64 bits + // output: out + // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo + // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32] + // parts: out.lo = (in1.lo * in2.lo)[31:0] + + // IP <- in1.lo * in2.hi + __ mul(IP, in1_lo, in2_hi); + // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo + __ mla(out_hi, in1_hi, in2_lo, IP); + // out.lo <- (in1.lo * in2.lo)[31:0]; + __ umull(out_lo, IP, in1_lo, in2_lo); + // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32] + __ add(out_hi, out_hi, ShifterOperand(IP)); + break; + } + + case Primitive::kPrimFloat: { + __ vmuls(FromDToLowS(out.As<DRegister>()), + FromDToLowS(first.As<DRegister>()), + FromDToLowS(second.As<DRegister>())); break; + } + + case Primitive::kPrimDouble: { + __ vmuld(out.As<DRegister>(), first.As<DRegister>(), second.As<DRegister>()); + break; + } default: - LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType(); + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); } } @@ -1129,6 +1319,29 @@ void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) { DCHECK(!codegen_->IsLeafMethod()); } +void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); + InvokeRuntimeCallingConvention calling_convention; + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetOut(Location::RegisterLocation(R0)); + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); +} + +void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) { + InvokeRuntimeCallingConvention calling_convention; + LoadCurrentMethod(calling_convention.GetRegisterAt(1)); + __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex()); + + int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocArrayWithAccessCheck).Int32Value(); + __ LoadFromOffset(kLoadWord, LR, TR, offset); + __ blx(LR); + + codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); + DCHECK(!codegen_->IsLeafMethod()); +} + void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); @@ -1145,25 +1358,41 @@ void InstructionCodeGeneratorARM::VisitParameterValue(HParameterValue* instructi // Nothing to do, the parameter is already at its location. } -void LocationsBuilderARM::VisitNot(HNot* instruction) { +void LocationsBuilderARM::VisitNot(HNot* not_) { LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } -void InstructionCodeGeneratorARM::VisitNot(HNot* instruction) { - LocationSummary* locations = instruction->GetLocations(); - __ eor(locations->Out().As<Register>(), - locations->InAt(0).As<Register>(), ShifterOperand(1)); +void InstructionCodeGeneratorARM::VisitNot(HNot* not_) { + LocationSummary* locations = not_->GetLocations(); + Location out = locations->Out(); + Location in = locations->InAt(0); + switch (not_->InputAt(0)->GetType()) { + case Primitive::kPrimBoolean: + __ eor(out.As<Register>(), in.As<Register>(), ShifterOperand(1)); + break; + + case Primitive::kPrimInt: + __ mvn(out.As<Register>(), ShifterOperand(in.As<Register>())); + break; + + case Primitive::kPrimLong: + LOG(FATAL) << "Not yet implemented type for not operation " << not_->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType(); + } } void LocationsBuilderARM::VisitCompare(HCompare* compare) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt(1, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) { @@ -1219,9 +1448,8 @@ void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); bool is_object_type = instruction->GetFieldType() == Primitive::kPrimNot; - bool dies_at_entry = !is_object_type; - locations->SetInAt(0, Location::RequiresRegister(), dies_at_entry); - locations->SetInAt(1, Location::RequiresRegister(), dies_at_entry); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); // Temporary registers for the write barrier. if (is_object_type) { locations->AddTemp(Location::RequiresRegister()); @@ -1281,8 +1509,8 @@ void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instr void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { @@ -1349,7 +1577,7 @@ void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) { } void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { - SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction); + SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction); codegen_->AddSlowPath(slow_path); LocationSummary* locations = instruction->GetLocations(); @@ -1368,10 +1596,9 @@ void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt( - 1, Location::RegisterOrConstant(instruction->InputAt(1)), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { @@ -1481,10 +1708,9 @@ void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) { locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); } else { - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt( - 1, Location::RegisterOrConstant(instruction->InputAt(1)), Location::kDiesAtEntry); - locations->SetInAt(2, Location::RequiresRegister(), Location::kDiesAtEntry); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetInAt(2, Location::RequiresRegister()); } } @@ -1571,8 +1797,8 @@ void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { @@ -1595,7 +1821,7 @@ void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) { void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) { LocationSummary* locations = instruction->GetLocations(); - SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM( + SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM( instruction, locations->InAt(0), locations->InAt(1)); codegen_->AddSlowPath(slow_path); @@ -1693,7 +1919,7 @@ void ParallelMoveResolverARM::EmitMove(size_t index) { } } else { DCHECK(source.IsConstant()); - DCHECK(source.GetConstant()->AsIntConstant() != nullptr); + DCHECK(source.GetConstant()->IsIntConstant()); int32_t value = source.GetConstant()->AsIntConstant()->GetValue(); if (destination.IsRegister()) { __ LoadImmediate(destination.As<Register>(), value); diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 874db0fd54..1fe8a7eacc 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -140,10 +140,10 @@ class CodeGeneratorARM : public CodeGenerator { virtual void GenerateFrameEntry() OVERRIDE; virtual void GenerateFrameExit() OVERRIDE; - virtual void Bind(Label* label) OVERRIDE; + virtual void Bind(HBasicBlock* block) OVERRIDE; virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE; - virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE; - virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE; + virtual size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; + virtual size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; virtual size_t GetWordSize() const OVERRIDE { return kArmWordSize; @@ -164,6 +164,7 @@ class CodeGeneratorARM : public CodeGenerator { } virtual void SetupBlockedRegisters() const OVERRIDE; + virtual Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE; virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE; @@ -171,6 +172,9 @@ class CodeGeneratorARM : public CodeGenerator { virtual void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE; virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE; + // Blocks all register pairs made out of blocked core registers. + void UpdateBlockedPairRegisters() const; + ParallelMoveResolverARM* GetMoveResolver() { return &move_resolver_; } @@ -187,7 +191,17 @@ class CodeGeneratorARM : public CodeGenerator { // Emit a write barrier. void MarkGCCard(Register temp, Register card, Register object, Register value); + Label* GetLabelOf(HBasicBlock* block) const { + return block_labels_.GetRawStorage() + block->GetBlockId(); + } + + virtual void Initialize() OVERRIDE { + block_labels_.SetSize(GetGraph()->GetBlocks().Size()); + } + private: + // Labels for each block that will be compiled. + GrowableArray<Label> block_labels_; LocationsBuilderARM location_builder_; InstructionCodeGeneratorARM instruction_visitor_; ParallelMoveResolverARM move_resolver_; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc new file mode 100644 index 0000000000..79528ac128 --- /dev/null +++ b/compiler/optimizing/code_generator_arm64.cc @@ -0,0 +1,1205 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "code_generator_arm64.h" + +#include "entrypoints/quick/quick_entrypoints.h" +#include "gc/accounting/card_table.h" +#include "mirror/array-inl.h" +#include "mirror/art_method.h" +#include "mirror/class.h" +#include "thread.h" +#include "utils/arm64/assembler_arm64.h" +#include "utils/assembler.h" +#include "utils/stack_checks.h" + + +using namespace vixl; // NOLINT(build/namespaces) + +#ifdef __ +#error "ARM64 Codegen VIXL macro-assembler macro already defined." +#endif + + +namespace art { + +namespace arm64 { + +static bool IsFPType(Primitive::Type type) { + return type == Primitive::kPrimFloat || type == Primitive::kPrimDouble; +} + +// TODO: clean-up some of the constant definitions. +static constexpr size_t kHeapRefSize = sizeof(mirror::HeapReference<mirror::Object>); +static constexpr int kCurrentMethodStackOffset = 0; + +namespace { +// Convenience helpers to ease conversion to and from VIXL operands. + +int VIXLRegCodeFromART(int code) { + // TODO: static check? + DCHECK_EQ(SP, 31); + DCHECK_EQ(WSP, 31); + DCHECK_EQ(XZR, 32); + DCHECK_EQ(WZR, 32); + if (code == SP) { + return vixl::kSPRegInternalCode; + } + if (code == XZR) { + return vixl::kZeroRegCode; + } + return code; +} + +int ARTRegCodeFromVIXL(int code) { + // TODO: static check? + DCHECK_EQ(SP, 31); + DCHECK_EQ(WSP, 31); + DCHECK_EQ(XZR, 32); + DCHECK_EQ(WZR, 32); + if (code == vixl::kSPRegInternalCode) { + return SP; + } + if (code == vixl::kZeroRegCode) { + return XZR; + } + return code; +} + +Register XRegisterFrom(Location location) { + return Register::XRegFromCode(VIXLRegCodeFromART(location.reg())); +} + +Register WRegisterFrom(Location location) { + return Register::WRegFromCode(VIXLRegCodeFromART(location.reg())); +} + +Register RegisterFrom(Location location, Primitive::Type type) { + DCHECK(type != Primitive::kPrimVoid && !IsFPType(type)); + return type == Primitive::kPrimLong ? XRegisterFrom(location) : WRegisterFrom(location); +} + +Register OutputRegister(HInstruction* instr) { + return RegisterFrom(instr->GetLocations()->Out(), instr->GetType()); +} + +Register InputRegisterAt(HInstruction* instr, int input_index) { + return RegisterFrom(instr->GetLocations()->InAt(input_index), + instr->InputAt(input_index)->GetType()); +} + +int64_t Int64ConstantFrom(Location location) { + HConstant* instr = location.GetConstant(); + return instr->IsIntConstant() ? instr->AsIntConstant()->GetValue() + : instr->AsLongConstant()->GetValue(); +} + +Operand OperandFrom(Location location, Primitive::Type type) { + if (location.IsRegister()) { + return Operand(RegisterFrom(location, type)); + } else { + return Operand(Int64ConstantFrom(location)); + } +} + +Operand InputOperandAt(HInstruction* instr, int input_index) { + return OperandFrom(instr->GetLocations()->InAt(input_index), + instr->InputAt(input_index)->GetType()); +} + +MemOperand StackOperandFrom(Location location) { + return MemOperand(sp, location.GetStackIndex()); +} + +MemOperand HeapOperand(const Register& base, Offset offset) { + // A heap reference must be 32bit, so fit in a W register. + DCHECK(base.IsW()); + return MemOperand(base.X(), offset.SizeValue()); +} + +MemOperand HeapOperandFrom(Location location, Primitive::Type type, Offset offset) { + return HeapOperand(RegisterFrom(location, type), offset); +} + +Location LocationFrom(const Register& reg) { + return Location::RegisterLocation(ARTRegCodeFromVIXL(reg.code())); +} + +} // namespace + +inline Condition ARM64Condition(IfCondition cond) { + switch (cond) { + case kCondEQ: return eq; + case kCondNE: return ne; + case kCondLT: return lt; + case kCondLE: return le; + case kCondGT: return gt; + case kCondGE: return ge; + default: + LOG(FATAL) << "Unknown if condition"; + } + return nv; // Unreachable. +} + +static const Register kRuntimeParameterCoreRegisters[] = { x0, x1, x2, x3, x4, x5, x6, x7 }; +static constexpr size_t kRuntimeParameterCoreRegistersLength = + arraysize(kRuntimeParameterCoreRegisters); +static const FPRegister kRuntimeParameterFpuRegisters[] = { }; +static constexpr size_t kRuntimeParameterFpuRegistersLength = 0; + +class InvokeRuntimeCallingConvention : public CallingConvention<Register, FPRegister> { + public: + static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters); + + InvokeRuntimeCallingConvention() + : CallingConvention(kRuntimeParameterCoreRegisters, + kRuntimeParameterCoreRegistersLength, + kRuntimeParameterFpuRegisters, + kRuntimeParameterFpuRegistersLength) {} + + Location GetReturnLocation(Primitive::Type return_type); + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); +}; + +Location InvokeRuntimeCallingConvention::GetReturnLocation(Primitive::Type return_type) { + DCHECK_NE(return_type, Primitive::kPrimVoid); + if (return_type == Primitive::kPrimFloat || return_type == Primitive::kPrimDouble) { + LOG(FATAL) << "Unimplemented return type " << return_type; + } + return LocationFrom(x0); +} + +#define __ reinterpret_cast<Arm64Assembler*>(codegen->GetAssembler())->vixl_masm_-> + +class SlowPathCodeARM64 : public SlowPathCode { + public: + SlowPathCodeARM64() : entry_label_(), exit_label_() {} + + vixl::Label* GetEntryLabel() { return &entry_label_; } + vixl::Label* GetExitLabel() { return &exit_label_; } + + private: + vixl::Label entry_label_; + vixl::Label exit_label_; + + DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM64); +}; + +class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 { + public: + explicit BoundsCheckSlowPathARM64(HBoundsCheck* instruction, + Location index_location, + Location length_location) + : instruction_(instruction), + index_location_(index_location), + length_location_(length_location) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorARM64* arm64_codegen = reinterpret_cast<CodeGeneratorARM64*>(codegen); + __ Bind(GetEntryLabel()); + InvokeRuntimeCallingConvention calling_convention; + arm64_codegen->MoveHelper(LocationFrom(calling_convention.GetRegisterAt(0)), + index_location_, Primitive::kPrimInt); + arm64_codegen->MoveHelper(LocationFrom(calling_convention.GetRegisterAt(1)), + length_location_, Primitive::kPrimInt); + size_t offset = QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pThrowArrayBounds).SizeValue(); + __ Ldr(lr, MemOperand(tr, offset)); + __ Blr(lr); + codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + } + + private: + HBoundsCheck* const instruction_; + const Location index_location_; + const Location length_location_; + + DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM64); +}; + +class NullCheckSlowPathARM64 : public SlowPathCodeARM64 { + public: + explicit NullCheckSlowPathARM64(HNullCheck* instr) : instruction_(instr) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + __ Bind(GetEntryLabel()); + int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pThrowNullPointer).Int32Value(); + __ Ldr(lr, MemOperand(tr, offset)); + __ Blr(lr); + codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + } + + private: + HNullCheck* const instruction_; + + DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM64); +}; + +class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 { + public: + explicit SuspendCheckSlowPathARM64(HSuspendCheck* instruction, + HBasicBlock* successor) + : instruction_(instruction), successor_(successor) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + size_t offset = QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pTestSuspend).SizeValue(); + __ Bind(GetEntryLabel()); + __ Ldr(lr, MemOperand(tr, offset)); + __ Blr(lr); + codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + __ B(GetReturnLabel()); + } + + vixl::Label* GetReturnLabel() { + DCHECK(successor_ == nullptr); + return &return_label_; + } + + + private: + HSuspendCheck* const instruction_; + // If not null, the block to branch to after the suspend check. + HBasicBlock* const successor_; + + // If `successor_` is null, the label to branch to after the suspend check. + vixl::Label return_label_; + + DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM64); +}; + +#undef __ + +Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) { + Location next_location; + if (type == Primitive::kPrimVoid) { + LOG(FATAL) << "Unreachable type " << type; + } + + if (type == Primitive::kPrimFloat || type == Primitive::kPrimDouble) { + LOG(FATAL) << "Unimplemented type " << type; + } + + if (gp_index_ < calling_convention.GetNumberOfRegisters()) { + next_location = LocationFrom(calling_convention.GetRegisterAt(gp_index_)); + if (type == Primitive::kPrimLong) { + // Double stack slot reserved on the stack. + stack_index_++; + } + } else { // Stack. + if (type == Primitive::kPrimLong) { + next_location = Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index_)); + // Double stack slot reserved on the stack. + stack_index_++; + } else { + next_location = Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index_)); + } + } + // Move to the next register/stack slot. + gp_index_++; + stack_index_++; + return next_location; +} + +CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph) + : CodeGenerator(graph, + kNumberOfAllocatableRegisters, + kNumberOfAllocatableFloatingPointRegisters, + kNumberOfAllocatableRegisterPairs), + block_labels_(nullptr), + location_builder_(graph, this), + instruction_visitor_(graph, this) {} + +#define __ reinterpret_cast<Arm64Assembler*>(GetAssembler())->vixl_masm_-> + +void CodeGeneratorARM64::GenerateFrameEntry() { + // TODO: Add proper support for the stack overflow check. + UseScratchRegisterScope temps(assembler_.vixl_masm_); + Register temp = temps.AcquireX(); + __ Add(temp, sp, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm64))); + __ Ldr(temp, MemOperand(temp, 0)); + RecordPcInfo(nullptr, 0); + + CPURegList preserved_regs = GetFramePreservedRegisters(); + int frame_size = GetFrameSize(); + core_spill_mask_ |= preserved_regs.list(); + + __ Str(w0, MemOperand(sp, -frame_size, PreIndex)); + __ PokeCPURegList(preserved_regs, frame_size - preserved_regs.TotalSizeInBytes()); + + // Stack layout: + // sp[frame_size - 8] : lr. + // ... : other preserved registers. + // sp[frame_size - regs_size]: first preserved register. + // ... : reserved frame space. + // sp[0] : context pointer. +} + +void CodeGeneratorARM64::GenerateFrameExit() { + int frame_size = GetFrameSize(); + CPURegList preserved_regs = GetFramePreservedRegisters(); + __ PeekCPURegList(preserved_regs, frame_size - preserved_regs.TotalSizeInBytes()); + __ Drop(frame_size); +} + +void CodeGeneratorARM64::Bind(HBasicBlock* block) { + __ Bind(GetLabelOf(block)); +} + +void CodeGeneratorARM64::MoveHelper(Location destination, + Location source, + Primitive::Type type) { + if (source.Equals(destination)) { + return; + } + if (destination.IsRegister()) { + Register dst = RegisterFrom(destination, type); + if (source.IsRegister()) { + Register src = RegisterFrom(source, type); + DCHECK(dst.IsSameSizeAndType(src)); + __ Mov(dst, src); + } else { + DCHECK(dst.Is64Bits() || !source.IsDoubleStackSlot()); + __ Ldr(dst, StackOperandFrom(source)); + } + } else { + DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot()); + if (source.IsRegister()) { + __ Str(RegisterFrom(source, type), StackOperandFrom(destination)); + } else { + UseScratchRegisterScope temps(assembler_.vixl_masm_); + Register temp = destination.IsDoubleStackSlot() ? temps.AcquireX() : temps.AcquireW(); + __ Ldr(temp, StackOperandFrom(source)); + __ Str(temp, StackOperandFrom(destination)); + } + } +} + +void CodeGeneratorARM64::Move(HInstruction* instruction, + Location location, + HInstruction* move_for) { + LocationSummary* locations = instruction->GetLocations(); + if (locations != nullptr && locations->Out().Equals(location)) { + return; + } + + Primitive::Type type = instruction->GetType(); + + if (instruction->IsIntConstant() || instruction->IsLongConstant()) { + int64_t value = instruction->IsIntConstant() ? instruction->AsIntConstant()->GetValue() + : instruction->AsLongConstant()->GetValue(); + if (location.IsRegister()) { + Register dst = RegisterFrom(location, type); + DCHECK((instruction->IsIntConstant() && dst.Is32Bits()) || + (instruction->IsLongConstant() && dst.Is64Bits())); + __ Mov(dst, value); + } else { + DCHECK(location.IsStackSlot() || location.IsDoubleStackSlot()); + UseScratchRegisterScope temps(assembler_.vixl_masm_); + Register temp = instruction->IsIntConstant() ? temps.AcquireW() : temps.AcquireX(); + __ Mov(temp, value); + __ Str(temp, StackOperandFrom(location)); + } + + } else if (instruction->IsLoadLocal()) { + uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal()); + switch (type) { + case Primitive::kPrimNot: + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + MoveHelper(location, Location::StackSlot(stack_slot), type); + break; + case Primitive::kPrimLong: + MoveHelper(location, Location::DoubleStackSlot(stack_slot), type); + break; + default: + LOG(FATAL) << "Unimplemented type" << type; + } + + } else { + DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary()); + MoveHelper(location, locations->Out(), type); + } +} + +size_t CodeGeneratorARM64::FrameEntrySpillSize() const { + return GetFramePreservedRegistersSize(); +} + +Location CodeGeneratorARM64::GetStackLocation(HLoadLocal* load) const { + Primitive::Type type = load->GetType(); + switch (type) { + case Primitive::kPrimNot: + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + return Location::StackSlot(GetStackSlot(load->GetLocal())); + case Primitive::kPrimLong: + return Location::DoubleStackSlot(GetStackSlot(load->GetLocal())); + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented type " << type; + break; + case Primitive::kPrimVoid: + default: + LOG(FATAL) << "Unexpected type " << type; + } + LOG(FATAL) << "Unreachable"; + return Location::NoLocation(); +} + +void CodeGeneratorARM64::MarkGCCard(Register object, Register value) { + UseScratchRegisterScope temps(assembler_.vixl_masm_); + Register card = temps.AcquireX(); + Register temp = temps.AcquireX(); + vixl::Label done; + __ Cbz(value, &done); + __ Ldr(card, MemOperand(tr, Thread::CardTableOffset<kArm64WordSize>().Int32Value())); + __ Lsr(temp, object, gc::accounting::CardTable::kCardShift); + __ Strb(card, MemOperand(card, temp)); + __ Bind(&done); +} + +void CodeGeneratorARM64::SetupBlockedRegisters() const { + // Block reserved registers: + // ip0 (VIXL temporary) + // ip1 (VIXL temporary) + // xSuspend (Suspend counter) + // lr + // sp is not part of the allocatable registers, so we don't need to block it. + CPURegList reserved_core_registers = vixl_reserved_core_registers; + reserved_core_registers.Combine(runtime_reserved_core_registers); + // TODO: See if we should instead allow allocating but preserve those if used. + reserved_core_registers.Combine(quick_callee_saved_registers); + while (!reserved_core_registers.IsEmpty()) { + blocked_core_registers_[reserved_core_registers.PopLowestIndex().code()] = true; + } +} + +Location CodeGeneratorARM64::AllocateFreeRegister(Primitive::Type type) const { + if (type == Primitive::kPrimVoid) { + LOG(FATAL) << "Unreachable type " << type; + } + + if (type == Primitive::kPrimFloat || type == Primitive::kPrimDouble) { + LOG(FATAL) << "Unimplemented support for floating-point"; + } + + ssize_t reg = FindFreeEntry(blocked_core_registers_, kNumberOfXRegisters); + DCHECK_NE(reg, -1); + blocked_core_registers_[reg] = true; + + if (IsFPType(type)) { + return Location::FpuRegisterLocation(reg); + } else { + return Location::RegisterLocation(reg); + } +} + +void CodeGeneratorARM64::DumpCoreRegister(std::ostream& stream, int reg) const { + stream << Arm64ManagedRegister::FromXRegister(XRegister(reg)); +} + +void CodeGeneratorARM64::DumpFloatingPointRegister(std::ostream& stream, int reg) const { + stream << Arm64ManagedRegister::FromDRegister(DRegister(reg)); +} + +#undef __ +#define __ assembler_->vixl_masm_-> + +InstructionCodeGeneratorARM64::InstructionCodeGeneratorARM64(HGraph* graph, + CodeGeneratorARM64* codegen) + : HGraphVisitor(graph), + assembler_(codegen->GetAssembler()), + codegen_(codegen) {} + +#define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M) \ + M(ArrayGet) \ + M(ArraySet) \ + M(DoubleConstant) \ + M(FloatConstant) \ + M(Mul) \ + M(Neg) \ + M(NewArray) \ + M(ParallelMove) + +#define UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name) name##UnimplementedInstructionBreakCode + +enum UnimplementedInstructionBreakCode { +#define ENUM_UNIMPLEMENTED_INSTRUCTION(name) UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name), + FOR_EACH_UNIMPLEMENTED_INSTRUCTION(ENUM_UNIMPLEMENTED_INSTRUCTION) +#undef ENUM_UNIMPLEMENTED_INSTRUCTION +}; + +#define DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS(name) \ + void InstructionCodeGeneratorARM64::Visit##name(H##name* instr) { \ + __ Brk(UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name)); \ + } \ + void LocationsBuilderARM64::Visit##name(H##name* instr) { \ + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); \ + locations->SetOut(Location::Any()); \ + } + FOR_EACH_UNIMPLEMENTED_INSTRUCTION(DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS) +#undef DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS + +#undef UNIMPLEMENTED_INSTRUCTION_BREAK_CODE + +void LocationsBuilderARM64::HandleAddSub(HBinaryOperation* instr) { + DCHECK(instr->IsAdd() || instr->IsSub()); + DCHECK_EQ(instr->InputCount(), 2U); + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); + Primitive::Type type = instr->GetResultType(); + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instr->InputAt(1))); + locations->SetOut(Location::RequiresRegister()); + break; + } + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected " << instr->DebugName() << " type " << type; + break; + default: + LOG(FATAL) << "Unimplemented " << instr->DebugName() << " type " << type; + } +} + +void InstructionCodeGeneratorARM64::HandleAddSub(HBinaryOperation* instr) { + DCHECK(instr->IsAdd() || instr->IsSub()); + + Primitive::Type type = instr->GetType(); + Register dst = OutputRegister(instr); + Register lhs = InputRegisterAt(instr, 0); + Operand rhs = InputOperandAt(instr, 1); + + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: + if (instr->IsAdd()) { + __ Add(dst, lhs, rhs); + } else { + __ Sub(dst, lhs, rhs); + } + break; + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected add/sub type " << type; + break; + default: + LOG(FATAL) << "Unimplemented add/sub type " << type; + } +} + +void LocationsBuilderARM64::VisitAdd(HAdd* instruction) { + HandleAddSub(instruction); +} + +void InstructionCodeGeneratorARM64::VisitAdd(HAdd* instruction) { + HandleAddSub(instruction); +} + +void LocationsBuilderARM64::VisitArrayLength(HArrayLength* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorARM64::VisitArrayLength(HArrayLength* instruction) { + __ Ldr(OutputRegister(instruction), + HeapOperand(InputRegisterAt(instruction, 0), mirror::Array::LengthOffset())); +} + +void LocationsBuilderARM64::VisitCompare(HCompare* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorARM64::VisitCompare(HCompare* instruction) { + Primitive::Type in_type = instruction->InputAt(0)->GetType(); + + DCHECK_EQ(in_type, Primitive::kPrimLong); + switch (in_type) { + case Primitive::kPrimLong: { + vixl::Label done; + Register result = OutputRegister(instruction); + Register left = InputRegisterAt(instruction, 0); + Operand right = InputOperandAt(instruction, 1); + __ Subs(result, left, right); + __ B(eq, &done); + __ Mov(result, 1); + __ Cneg(result, result, le); + __ Bind(&done); + break; + } + default: + LOG(FATAL) << "Unimplemented compare type " << in_type; + } +} + +void LocationsBuilderARM64::VisitCondition(HCondition* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + if (instruction->NeedsMaterialization()) { + locations->SetOut(Location::RequiresRegister()); + } +} + +void InstructionCodeGeneratorARM64::VisitCondition(HCondition* instruction) { + if (!instruction->NeedsMaterialization()) { + return; + } + + LocationSummary* locations = instruction->GetLocations(); + Register lhs = InputRegisterAt(instruction, 0); + Operand rhs = InputOperandAt(instruction, 1); + Register res = RegisterFrom(locations->Out(), instruction->GetType()); + Condition cond = ARM64Condition(instruction->GetCondition()); + + __ Cmp(lhs, rhs); + __ Csel(res, vixl::Assembler::AppropriateZeroRegFor(res), Operand(1), InvertCondition(cond)); +} + +#define FOR_EACH_CONDITION_INSTRUCTION(M) \ + M(Equal) \ + M(NotEqual) \ + M(LessThan) \ + M(LessThanOrEqual) \ + M(GreaterThan) \ + M(GreaterThanOrEqual) +#define DEFINE_CONDITION_VISITORS(Name) \ +void LocationsBuilderARM64::Visit##Name(H##Name* comp) { VisitCondition(comp); } \ +void InstructionCodeGeneratorARM64::Visit##Name(H##Name* comp) { VisitCondition(comp); } +FOR_EACH_CONDITION_INSTRUCTION(DEFINE_CONDITION_VISITORS) +#undef FOR_EACH_CONDITION_INSTRUCTION + +void LocationsBuilderARM64::VisitExit(HExit* exit) { + exit->SetLocations(nullptr); +} + +void InstructionCodeGeneratorARM64::VisitExit(HExit* exit) { + if (kIsDebugBuild) { + down_cast<Arm64Assembler*>(GetAssembler())->Comment("Unreachable"); + __ Brk(0); // TODO: Introduce special markers for such code locations. + } +} + +void LocationsBuilderARM64::VisitGoto(HGoto* got) { + got->SetLocations(nullptr); +} + +void InstructionCodeGeneratorARM64::VisitGoto(HGoto* got) { + HBasicBlock* successor = got->GetSuccessor(); + // TODO: Support for suspend checks emission. + if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) { + __ B(codegen_->GetLabelOf(successor)); + } +} + +void LocationsBuilderARM64::VisitIf(HIf* if_instr) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr); + HInstruction* cond = if_instr->InputAt(0); + DCHECK(cond->IsCondition()); + if (cond->AsCondition()->NeedsMaterialization()) { + locations->SetInAt(0, Location::RequiresRegister()); + } +} + +void InstructionCodeGeneratorARM64::VisitIf(HIf* if_instr) { + HInstruction* cond = if_instr->InputAt(0); + DCHECK(cond->IsCondition()); + HCondition* condition = cond->AsCondition(); + vixl::Label* true_target = codegen_->GetLabelOf(if_instr->IfTrueSuccessor()); + vixl::Label* false_target = codegen_->GetLabelOf(if_instr->IfFalseSuccessor()); + + // TODO: Support constant condition input in VisitIf. + + if (condition->NeedsMaterialization()) { + // The condition instruction has been materialized, compare the output to 0. + Location cond_val = if_instr->GetLocations()->InAt(0); + DCHECK(cond_val.IsRegister()); + __ Cbnz(InputRegisterAt(if_instr, 0), true_target); + + } else { + // The condition instruction has not been materialized, use its inputs as + // the comparison and its condition as the branch condition. + Register lhs = InputRegisterAt(condition, 0); + Operand rhs = InputOperandAt(condition, 1); + Condition cond = ARM64Condition(condition->GetCondition()); + if ((cond == eq || cond == ne) && rhs.IsImmediate() && (rhs.immediate() == 0)) { + if (cond == eq) { + __ Cbz(lhs, true_target); + } else { + __ Cbnz(lhs, true_target); + } + } else { + __ Cmp(lhs, rhs); + __ B(cond, true_target); + } + } + + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) { + __ B(false_target); + } +} + +void LocationsBuilderARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { + Primitive::Type res_type = instruction->GetType(); + Register res = OutputRegister(instruction); + Register obj = InputRegisterAt(instruction, 0); + uint32_t offset = instruction->GetFieldOffset().Uint32Value(); + + switch (res_type) { + case Primitive::kPrimBoolean: { + __ Ldrb(res, MemOperand(obj, offset)); + break; + } + case Primitive::kPrimByte: { + __ Ldrsb(res, MemOperand(obj, offset)); + break; + } + case Primitive::kPrimShort: { + __ Ldrsh(res, MemOperand(obj, offset)); + break; + } + case Primitive::kPrimChar: { + __ Ldrh(res, MemOperand(obj, offset)); + break; + } + case Primitive::kPrimInt: + case Primitive::kPrimNot: + case Primitive::kPrimLong: { // TODO: support volatile. + DCHECK(res.IsX() == (res_type == Primitive::kPrimLong)); + __ Ldr(res, MemOperand(obj, offset)); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register res_type " << res_type; + break; + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable res_type " << res_type; + } +} + +void LocationsBuilderARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); +} + +void InstructionCodeGeneratorARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { + Register obj = InputRegisterAt(instruction, 0); + Register value = InputRegisterAt(instruction, 1); + Primitive::Type field_type = instruction->InputAt(1)->GetType(); + uint32_t offset = instruction->GetFieldOffset().Uint32Value(); + + switch (field_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: { + __ Strb(value, MemOperand(obj, offset)); + break; + } + + case Primitive::kPrimShort: + case Primitive::kPrimChar: { + __ Strh(value, MemOperand(obj, offset)); + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: + case Primitive::kPrimLong: { + DCHECK(value.IsX() == (field_type == Primitive::kPrimLong)); + __ Str(value, MemOperand(obj, offset)); + + if (field_type == Primitive::kPrimNot) { + codegen_->MarkGCCard(obj, value); + } + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << field_type; + break; + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << field_type; + } +} + +void LocationsBuilderARM64::VisitIntConstant(HIntConstant* constant) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorARM64::VisitIntConstant(HIntConstant* constant) { + // Will be generated at use site. +} + +void LocationsBuilderARM64::VisitInvokeStatic(HInvokeStatic* invoke) { + HandleInvoke(invoke); +} + +void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { + HandleInvoke(invoke); +} + +void LocationsBuilderARM64::HandleInvoke(HInvoke* invoke) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall); + locations->AddTemp(LocationFrom(x0)); + + InvokeDexCallingConventionVisitor calling_convention_visitor; + for (size_t i = 0; i < invoke->InputCount(); i++) { + HInstruction* input = invoke->InputAt(i); + locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType())); + } + + Primitive::Type return_type = invoke->GetType(); + if (return_type != Primitive::kPrimVoid) { + locations->SetOut(calling_convention_visitor.GetReturnLocation(return_type)); + } +} + +void InstructionCodeGeneratorARM64::VisitInvokeStatic(HInvokeStatic* invoke) { + Register temp = WRegisterFrom(invoke->GetLocations()->GetTemp(0)); + // Make sure that ArtMethod* is passed in W0 as per the calling convention + DCHECK(temp.Is(w0)); + size_t index_in_cache = mirror::Array::DataOffset(kHeapRefSize).SizeValue() + + invoke->GetIndexInDexCache() * kHeapRefSize; + + // TODO: Implement all kinds of calls: + // 1) boot -> boot + // 2) app -> boot + // 3) app -> app + // + // Currently we implement the app -> app logic, which looks up in the resolve cache. + + // temp = method; + __ Ldr(temp, MemOperand(sp, kCurrentMethodStackOffset)); + // temp = temp->dex_cache_resolved_methods_; + __ Ldr(temp, MemOperand(temp.X(), mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue())); + // temp = temp[index_in_cache]; + __ Ldr(temp, MemOperand(temp.X(), index_in_cache)); + // lr = temp->entry_point_from_quick_compiled_code_; + __ Ldr(lr, MemOperand(temp.X(), mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue())); + // lr(); + __ Blr(lr); + + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + DCHECK(!codegen_->IsLeafMethod()); +} + +void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { + LocationSummary* locations = invoke->GetLocations(); + Location receiver = locations->InAt(0); + Register temp = XRegisterFrom(invoke->GetLocations()->GetTemp(0)); + size_t method_offset = mirror::Class::EmbeddedVTableOffset().SizeValue() + + invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry); + Offset class_offset = mirror::Object::ClassOffset(); + Offset entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(); + + // temp = object->GetClass(); + if (receiver.IsStackSlot()) { + __ Ldr(temp.W(), MemOperand(sp, receiver.GetStackIndex())); + __ Ldr(temp.W(), MemOperand(temp, class_offset.SizeValue())); + } else { + DCHECK(receiver.IsRegister()); + __ Ldr(temp.W(), HeapOperandFrom(receiver, Primitive::kPrimNot, + class_offset)); + } + // temp = temp->GetMethodAt(method_offset); + __ Ldr(temp.W(), MemOperand(temp, method_offset)); + // lr = temp->GetEntryPoint(); + __ Ldr(lr, MemOperand(temp, entry_point.SizeValue())); + // lr(); + __ Blr(lr); + DCHECK(!codegen_->IsLeafMethod()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); +} + +void LocationsBuilderARM64::VisitLoadLocal(HLoadLocal* load) { + load->SetLocations(nullptr); +} + +void InstructionCodeGeneratorARM64::VisitLoadLocal(HLoadLocal* load) { + // Nothing to do, this is driven by the code generator. +} + +void LocationsBuilderARM64::VisitLocal(HLocal* local) { + local->SetLocations(nullptr); +} + +void InstructionCodeGeneratorARM64::VisitLocal(HLocal* local) { + DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock()); +} + +void LocationsBuilderARM64::VisitLongConstant(HLongConstant* constant) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorARM64::VisitLongConstant(HLongConstant* constant) { + // Will be generated at use site. +} + +void LocationsBuilderARM64::VisitNewInstance(HNewInstance* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); + InvokeRuntimeCallingConvention calling_convention; + locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0))); + locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1))); + locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); +} + +void InstructionCodeGeneratorARM64::VisitNewInstance(HNewInstance* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt); + DCHECK(type_index.Is(w0)); + Register current_method = RegisterFrom(locations->GetTemp(1), Primitive::kPrimNot); + DCHECK(current_method.Is(w1)); + __ Ldr(current_method, MemOperand(sp, kCurrentMethodStackOffset)); + __ Mov(type_index, instruction->GetTypeIndex()); + __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocObjectWithAccessCheck).Int32Value())); + __ Blr(lr); + codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); + DCHECK(!codegen_->IsLeafMethod()); +} + +void LocationsBuilderARM64::VisitNot(HNot* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorARM64::VisitNot(HNot* instruction) { + switch (instruction->InputAt(0)->GetType()) { + case Primitive::kPrimBoolean: + __ Eor(OutputRegister(instruction), InputRegisterAt(instruction, 0), Operand(1)); + break; + + case Primitive::kPrimInt: + __ Mvn(OutputRegister(instruction), InputOperandAt(instruction, 0)); + break; + + case Primitive::kPrimLong: + LOG(FATAL) << "Not yet implemented type for not operation " << instruction->GetResultType(); + break; + + default: + LOG(FATAL) << "Unexpected type for not operation " << instruction->GetResultType(); + } +} + +void LocationsBuilderARM64::VisitNullCheck(HNullCheck* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + if (instruction->HasUses()) { + locations->SetOut(Location::SameAsFirstInput()); + } +} + +void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) { + SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM64(instruction); + codegen_->AddSlowPath(slow_path); + + LocationSummary* locations = instruction->GetLocations(); + Location obj = locations->InAt(0); + if (obj.IsRegister()) { + __ Cbz(RegisterFrom(obj, instruction->InputAt(0)->GetType()), slow_path->GetEntryLabel()); + } else { + DCHECK(obj.IsConstant()) << obj; + DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0); + __ B(slow_path->GetEntryLabel()); + } +} + +void LocationsBuilderARM64::VisitParameterValue(HParameterValue* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + Location location = parameter_visitor_.GetNextLocation(instruction->GetType()); + if (location.IsStackSlot()) { + location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); + } else if (location.IsDoubleStackSlot()) { + location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); + } + locations->SetOut(location); +} + +void InstructionCodeGeneratorARM64::VisitParameterValue(HParameterValue* instruction) { + // Nothing to do, the parameter is already at its location. +} + +void LocationsBuilderARM64::VisitPhi(HPhi* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) { + locations->SetInAt(i, Location::Any()); + } + locations->SetOut(Location::Any()); +} + +void InstructionCodeGeneratorARM64::VisitPhi(HPhi* instruction) { + LOG(FATAL) << "Unreachable"; +} + +void LocationsBuilderARM64::VisitReturn(HReturn* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + Primitive::Type return_type = instruction->InputAt(0)->GetType(); + + if (return_type == Primitive::kPrimFloat || return_type == Primitive::kPrimDouble) { + LOG(FATAL) << "Unimplemented return type " << return_type; + } + + locations->SetInAt(0, LocationFrom(x0)); +} + +void InstructionCodeGeneratorARM64::VisitReturn(HReturn* instruction) { + if (kIsDebugBuild) { + Primitive::Type type = instruction->InputAt(0)->GetType(); + switch (type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: + DCHECK(InputRegisterAt(instruction, 0).Is(w0)); + break; + + case Primitive::kPrimLong: + DCHECK(InputRegisterAt(instruction, 0).Is(x0)); + break; + + default: + LOG(FATAL) << "Unimplemented return type " << type; + } + } + codegen_->GenerateFrameExit(); + __ Br(lr); +} + +void LocationsBuilderARM64::VisitReturnVoid(HReturnVoid* instruction) { + instruction->SetLocations(nullptr); +} + +void InstructionCodeGeneratorARM64::VisitReturnVoid(HReturnVoid* instruction) { + codegen_->GenerateFrameExit(); + __ Br(lr); +} + +void LocationsBuilderARM64::VisitStoreLocal(HStoreLocal* store) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store); + Primitive::Type field_type = store->InputAt(1)->GetType(); + switch (field_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: + locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal()))); + break; + + case Primitive::kPrimLong: + locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal()))); + break; + + default: + LOG(FATAL) << "Unimplemented local type " << field_type; + } +} + +void InstructionCodeGeneratorARM64::VisitStoreLocal(HStoreLocal* store) { +} + +void LocationsBuilderARM64::VisitSub(HSub* instruction) { + HandleAddSub(instruction); +} + +void InstructionCodeGeneratorARM64::VisitSub(HSub* instruction) { + HandleAddSub(instruction); +} + +void LocationsBuilderARM64::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + if (instruction->HasUses()) { + locations->SetOut(Location::SameAsFirstInput()); + } +} + +void InstructionCodeGeneratorARM64::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = instruction->GetLocations(); + BoundsCheckSlowPathARM64* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM64( + instruction, locations->InAt(0), locations->InAt(1)); + codegen_->AddSlowPath(slow_path); + + __ Cmp(InputRegisterAt(instruction, 0), InputOperandAt(instruction, 1)); + __ B(slow_path->GetEntryLabel(), hs); +} + +void LocationsBuilderARM64::VisitSuspendCheck(HSuspendCheck* instruction) { + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); +} + +void InstructionCodeGeneratorARM64::VisitSuspendCheck(HSuspendCheck* instruction) { + // TODO: Improve support for suspend checks. + SuspendCheckSlowPathARM64* slow_path = + new (GetGraph()->GetArena()) SuspendCheckSlowPathARM64(instruction, nullptr); + codegen_->AddSlowPath(slow_path); + + __ Subs(wSuspend, wSuspend, 1); + __ B(slow_path->GetEntryLabel(), le); + __ Bind(slow_path->GetReturnLabel()); +} + +void LocationsBuilderARM64::VisitTemporary(HTemporary* temp) { + temp->SetLocations(nullptr); +} + +void InstructionCodeGeneratorARM64::VisitTemporary(HTemporary* temp) { + // Nothing to do, this is driven by the code generator. +} + +} // namespace arm64 +} // namespace art diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h new file mode 100644 index 0000000000..a4003ffea5 --- /dev/null +++ b/compiler/optimizing/code_generator_arm64.h @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_ +#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_ + +#include "code_generator.h" +#include "nodes.h" +#include "parallel_move_resolver.h" +#include "utils/arm64/assembler_arm64.h" +#include "a64/disasm-a64.h" +#include "a64/macro-assembler-a64.h" +#include "arch/arm64/quick_method_frame_info_arm64.h" + +namespace art { +namespace arm64 { + +class CodeGeneratorARM64; + +static constexpr size_t kArm64WordSize = 8; +static const vixl::Register kParameterCoreRegisters[] = { + vixl::x1, vixl::x2, vixl::x3, vixl::x4, vixl::x5, vixl::x6, vixl::x7 +}; +static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters); +static const vixl::FPRegister kParameterFPRegisters[] = { + vixl::d0, vixl::d1, vixl::d2, vixl::d3, vixl::d4, vixl::d5, vixl::d6, vixl::d7 +}; +static constexpr size_t kParameterFPRegistersLength = arraysize(kParameterFPRegisters); + +const vixl::Register tr = vixl::x18; // Thread Register +const vixl::Register wSuspend = vixl::w19; // Suspend Register +const vixl::Register xSuspend = vixl::x19; + +const vixl::CPURegList vixl_reserved_core_registers(vixl::ip0, vixl::ip1); +const vixl::CPURegList runtime_reserved_core_registers(tr, xSuspend, vixl::lr); +const vixl::CPURegList quick_callee_saved_registers(vixl::CPURegister::kRegister, + vixl::kXRegSize, + kArm64CalleeSaveRefSpills); + +class InvokeDexCallingConvention : public CallingConvention<vixl::Register, vixl::FPRegister> { + public: + InvokeDexCallingConvention() + : CallingConvention(kParameterCoreRegisters, + kParameterCoreRegistersLength, + kParameterFPRegisters, + kParameterFPRegistersLength) {} + + Location GetReturnLocation(Primitive::Type return_type) { + DCHECK_NE(return_type, Primitive::kPrimVoid); + if (return_type == Primitive::kPrimFloat || return_type == Primitive::kPrimDouble) { + LOG(FATAL) << "Unimplemented return type " << return_type; + } + return Location::RegisterLocation(X0); + } + + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConvention); +}; + +class InvokeDexCallingConventionVisitor { + public: + InvokeDexCallingConventionVisitor() : gp_index_(0), stack_index_(0) {} + + Location GetNextLocation(Primitive::Type type); + Location GetReturnLocation(Primitive::Type return_type) { + return calling_convention.GetReturnLocation(return_type); + } + + private: + InvokeDexCallingConvention calling_convention; + // The current index for core registers. + uint32_t gp_index_; + // The current stack index. + uint32_t stack_index_; + + DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitor); +}; + +class InstructionCodeGeneratorARM64 : public HGraphVisitor { + public: + InstructionCodeGeneratorARM64(HGraph* graph, CodeGeneratorARM64* codegen); + +#define DECLARE_VISIT_INSTRUCTION(name, super) \ + virtual void Visit##name(H##name* instr); + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) +#undef DECLARE_VISIT_INSTRUCTION + + void LoadCurrentMethod(XRegister reg); + + Arm64Assembler* GetAssembler() const { return assembler_; } + + private: + void HandleAddSub(HBinaryOperation* instr); + + Arm64Assembler* const assembler_; + CodeGeneratorARM64* const codegen_; + + DISALLOW_COPY_AND_ASSIGN(InstructionCodeGeneratorARM64); +}; + +class LocationsBuilderARM64 : public HGraphVisitor { + public: + explicit LocationsBuilderARM64(HGraph* graph, CodeGeneratorARM64* codegen) + : HGraphVisitor(graph), codegen_(codegen) {} + +#define DECLARE_VISIT_INSTRUCTION(name, super) \ + virtual void Visit##name(H##name* instr); + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) +#undef DECLARE_VISIT_INSTRUCTION + + private: + void HandleAddSub(HBinaryOperation* instr); + void HandleInvoke(HInvoke* instr); + + CodeGeneratorARM64* const codegen_; + InvokeDexCallingConventionVisitor parameter_visitor_; + + DISALLOW_COPY_AND_ASSIGN(LocationsBuilderARM64); +}; + +class CodeGeneratorARM64 : public CodeGenerator { + public: + explicit CodeGeneratorARM64(HGraph* graph); + virtual ~CodeGeneratorARM64() { } + + virtual void GenerateFrameEntry() OVERRIDE; + virtual void GenerateFrameExit() OVERRIDE; + + static const vixl::CPURegList& GetFramePreservedRegisters() { + static const vixl::CPURegList frame_preserved_regs = + vixl::CPURegList(vixl::CPURegister::kRegister, vixl::kXRegSize, vixl::lr.Bit()); + return frame_preserved_regs; + } + static int GetFramePreservedRegistersSize() { + return GetFramePreservedRegisters().TotalSizeInBytes(); + } + + virtual void Bind(HBasicBlock* block) OVERRIDE; + + vixl::Label* GetLabelOf(HBasicBlock* block) const { + return block_labels_ + block->GetBlockId(); + } + + virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE; + + virtual size_t GetWordSize() const OVERRIDE { + return kArm64WordSize; + } + + virtual size_t FrameEntrySpillSize() const OVERRIDE; + + virtual HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; } + virtual HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; } + virtual Arm64Assembler* GetAssembler() OVERRIDE { return &assembler_; } + + // Emit a write barrier. + void MarkGCCard(vixl::Register object, vixl::Register value); + + // Register allocation. + + virtual void SetupBlockedRegisters() const OVERRIDE; + // AllocateFreeRegister() is only used when allocating registers locally + // during CompileBaseline(). + virtual Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE; + + virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE; + + virtual size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE { + UNIMPLEMENTED(INFO) << "TODO: SaveCoreRegister"; + return 0; + } + + virtual size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE { + UNIMPLEMENTED(INFO) << "TODO: RestoreCoreRegister"; + return 0; + } + + // The number of registers that can be allocated. The register allocator may + // decide to reserve and not use a few of them. + // We do not consider registers sp, xzr, wzr. They are either not allocatable + // (xzr, wzr), or make for poor allocatable registers (sp alignment + // requirements, etc.). This also facilitates our task as all other registers + // can easily be mapped via to or from their type and index or code. + static const int kNumberOfAllocatableCoreRegisters = vixl::kNumberOfRegisters - 1; + static const int kNumberOfAllocatableFloatingPointRegisters = vixl::kNumberOfFPRegisters; + static const int kNumberOfAllocatableRegisters = + kNumberOfAllocatableCoreRegisters + kNumberOfAllocatableFloatingPointRegisters; + static constexpr int kNumberOfAllocatableRegisterPairs = 0; + + virtual void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE; + virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE; + + virtual InstructionSet GetInstructionSet() const OVERRIDE { + return InstructionSet::kArm64; + } + + void MoveHelper(Location destination, Location source, Primitive::Type type); + + virtual void Initialize() OVERRIDE { + HGraph* graph = GetGraph(); + int length = graph->GetBlocks().Size(); + block_labels_ = graph->GetArena()->AllocArray<vixl::Label>(length); + for (int i = 0; i < length; ++i) { + new(block_labels_ + i) vixl::Label(); + } + } + + private: + // Labels for each block that will be compiled. + vixl::Label* block_labels_; + + LocationsBuilderARM64 location_builder_; + InstructionCodeGeneratorARM64 instruction_visitor_; + Arm64Assembler assembler_; + + DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM64); +}; + +} // namespace arm64 +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_ diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 73143d6ac9..61f0750c5c 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -56,7 +56,21 @@ class InvokeRuntimeCallingConvention : public CallingConvention<Register, XmmReg #define __ reinterpret_cast<X86Assembler*>(codegen->GetAssembler())-> -class NullCheckSlowPathX86 : public SlowPathCode { +class SlowPathCodeX86 : public SlowPathCode { + public: + SlowPathCodeX86() : entry_label_(), exit_label_() {} + + Label* GetEntryLabel() { return &entry_label_; } + Label* GetExitLabel() { return &exit_label_; } + + private: + Label entry_label_; + Label exit_label_; + + DISALLOW_COPY_AND_ASSIGN(SlowPathCodeX86); +}; + +class NullCheckSlowPathX86 : public SlowPathCodeX86 { public: explicit NullCheckSlowPathX86(HNullCheck* instruction) : instruction_(instruction) {} @@ -71,7 +85,7 @@ class NullCheckSlowPathX86 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86); }; -class StackOverflowCheckSlowPathX86 : public SlowPathCode { +class StackOverflowCheckSlowPathX86 : public SlowPathCodeX86 { public: StackOverflowCheckSlowPathX86() {} @@ -86,7 +100,7 @@ class StackOverflowCheckSlowPathX86 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86); }; -class BoundsCheckSlowPathX86 : public SlowPathCode { +class BoundsCheckSlowPathX86 : public SlowPathCodeX86 { public: BoundsCheckSlowPathX86(HBoundsCheck* instruction, Location index_location, @@ -94,7 +108,7 @@ class BoundsCheckSlowPathX86 : public SlowPathCode { : instruction_(instruction), index_location_(index_location), length_location_(length_location) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { - CodeGeneratorX86* x86_codegen = reinterpret_cast<CodeGeneratorX86*>(codegen); + CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen); __ Bind(GetEntryLabel()); InvokeRuntimeCallingConvention calling_convention; x86_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_); @@ -111,12 +125,13 @@ class BoundsCheckSlowPathX86 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86); }; -class SuspendCheckSlowPathX86 : public SlowPathCode { +class SuspendCheckSlowPathX86 : public SlowPathCodeX86 { public: explicit SuspendCheckSlowPathX86(HSuspendCheck* instruction, HBasicBlock* successor) : instruction_(instruction), successor_(successor) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen); __ Bind(GetEntryLabel()); codegen->SaveLiveRegisters(instruction_->GetLocations()); __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pTestSuspend))); @@ -125,7 +140,7 @@ class SuspendCheckSlowPathX86 : public SlowPathCode { if (successor_ == nullptr) { __ jmp(GetReturnLabel()); } else { - __ jmp(codegen->GetLabelOf(successor_)); + __ jmp(x86_codegen->GetLabelOf(successor_)); } } @@ -167,16 +182,19 @@ void CodeGeneratorX86::DumpFloatingPointRegister(std::ostream& stream, int reg) stream << X86ManagedRegister::FromXmmRegister(XmmRegister(reg)); } -void CodeGeneratorX86::SaveCoreRegister(Location stack_location, uint32_t reg_id) { - __ movl(Address(ESP, stack_location.GetStackIndex()), static_cast<Register>(reg_id)); +size_t CodeGeneratorX86::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { + __ movl(Address(ESP, stack_index), static_cast<Register>(reg_id)); + return kX86WordSize; } -void CodeGeneratorX86::RestoreCoreRegister(Location stack_location, uint32_t reg_id) { - __ movl(static_cast<Register>(reg_id), Address(ESP, stack_location.GetStackIndex())); +size_t CodeGeneratorX86::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) { + __ movl(static_cast<Register>(reg_id), Address(ESP, stack_index)); + return kX86WordSize; } CodeGeneratorX86::CodeGeneratorX86(HGraph* graph) : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfXmmRegisters, kNumberOfRegisterPairs), + block_labels_(graph->GetArena(), 0), location_builder_(graph, this), instruction_visitor_(graph, this), move_resolver_(graph->GetArena(), this) {} @@ -191,19 +209,11 @@ Location CodeGeneratorX86::AllocateFreeRegister(Primitive::Type type) const { size_t reg = FindFreeEntry(blocked_register_pairs_, kNumberOfRegisterPairs); X86ManagedRegister pair = X86ManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg)); + DCHECK(!blocked_core_registers_[pair.AsRegisterPairLow()]); + DCHECK(!blocked_core_registers_[pair.AsRegisterPairHigh()]); blocked_core_registers_[pair.AsRegisterPairLow()] = true; blocked_core_registers_[pair.AsRegisterPairHigh()] = true; - // Block all other register pairs that share a register with `pair`. - for (int i = 0; i < kNumberOfRegisterPairs; i++) { - X86ManagedRegister current = - X86ManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); - if (current.AsRegisterPairLow() == pair.AsRegisterPairLow() - || current.AsRegisterPairLow() == pair.AsRegisterPairHigh() - || current.AsRegisterPairHigh() == pair.AsRegisterPairLow() - || current.AsRegisterPairHigh() == pair.AsRegisterPairHigh()) { - blocked_register_pairs_[i] = true; - } - } + UpdateBlockedPairRegisters(); return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh()); } @@ -250,10 +260,19 @@ void CodeGeneratorX86::SetupBlockedRegisters() const { blocked_core_registers_[EBP] = true; blocked_core_registers_[ESI] = true; blocked_core_registers_[EDI] = true; - blocked_register_pairs_[EAX_EDI] = true; - blocked_register_pairs_[EDX_EDI] = true; - blocked_register_pairs_[ECX_EDI] = true; - blocked_register_pairs_[EBX_EDI] = true; + + UpdateBlockedPairRegisters(); +} + +void CodeGeneratorX86::UpdateBlockedPairRegisters() const { + for (int i = 0; i < kNumberOfRegisterPairs; i++) { + X86ManagedRegister current = + X86ManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); + if (blocked_core_registers_[current.AsRegisterPairLow()] + || blocked_core_registers_[current.AsRegisterPairHigh()]) { + blocked_register_pairs_[i] = true; + } + } } InstructionCodeGeneratorX86::InstructionCodeGeneratorX86(HGraph* graph, CodeGeneratorX86* codegen) @@ -276,7 +295,7 @@ void CodeGeneratorX86::GenerateFrameEntry() { __ subl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize)); if (!skip_overflow_check && kExplicitStackOverflowCheck) { - SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86(); + SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86(); AddSlowPath(slow_path); __ fs()->cmpl(ESP, Address::Absolute(Thread::StackEndOffset<kX86WordSize>())); @@ -290,8 +309,8 @@ void CodeGeneratorX86::GenerateFrameExit() { __ addl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize)); } -void CodeGeneratorX86::Bind(Label* label) { - __ Bind(label); +void CodeGeneratorX86::Bind(HBasicBlock* block) { + __ Bind(GetLabelOf(block)); } void InstructionCodeGeneratorX86::LoadCurrentMethod(Register reg) { @@ -468,23 +487,29 @@ void CodeGeneratorX86::Move64(Location destination, Location source) { } void CodeGeneratorX86::Move(HInstruction* instruction, Location location, HInstruction* move_for) { - if (instruction->AsIntConstant() != nullptr) { + if (instruction->IsIntConstant()) { Immediate imm(instruction->AsIntConstant()->GetValue()); if (location.IsRegister()) { __ movl(location.As<Register>(), imm); - } else { + } else if (location.IsStackSlot()) { __ movl(Address(ESP, location.GetStackIndex()), imm); + } else { + DCHECK(location.IsConstant()); + DCHECK_EQ(location.GetConstant(), instruction); } - } else if (instruction->AsLongConstant() != nullptr) { + } else if (instruction->IsLongConstant()) { int64_t value = instruction->AsLongConstant()->GetValue(); if (location.IsRegister()) { __ movl(location.AsRegisterPairLow<Register>(), Immediate(Low32Bits(value))); __ movl(location.AsRegisterPairHigh<Register>(), Immediate(High32Bits(value))); - } else { + } else if (location.IsDoubleStackSlot()) { __ movl(Address(ESP, location.GetStackIndex()), Immediate(Low32Bits(value))); __ movl(Address(ESP, location.GetHighStackIndex(kX86WordSize)), Immediate(High32Bits(value))); + } else { + DCHECK(location.IsConstant()); + DCHECK_EQ(location.GetConstant(), instruction); } - } else if (instruction->AsLoadLocal() != nullptr) { + } else if (instruction->IsLoadLocal()) { int slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal()); switch (instruction->GetType()) { case Primitive::kPrimBoolean: @@ -571,48 +596,66 @@ void LocationsBuilderX86::VisitIf(HIf* if_instr) { new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall); HInstruction* cond = if_instr->InputAt(0); if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { - locations->SetInAt(0, Location::Any(), Location::kDiesAtEntry); + locations->SetInAt(0, Location::Any()); } } void InstructionCodeGeneratorX86::VisitIf(HIf* if_instr) { HInstruction* cond = if_instr->InputAt(0); - bool materialized = !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization(); - // Moves do not affect the eflags register, so if the condition is evaluated - // just before the if, we don't need to evaluate it again. - bool eflags_set = cond->IsCondition() - && cond->AsCondition()->IsBeforeWhenDisregardMoves(if_instr); - if (materialized) { - if (!eflags_set) { - // Materialized condition, compare against 0. - Location lhs = if_instr->GetLocations()->InAt(0); - if (lhs.IsRegister()) { - __ cmpl(lhs.As<Register>(), Immediate(0)); - } else { - __ cmpl(Address(ESP, lhs.GetStackIndex()), Immediate(0)); + if (cond->IsIntConstant()) { + // Constant condition, statically compared against 1. + int32_t cond_value = cond->AsIntConstant()->GetValue(); + if (cond_value == 1) { + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), + if_instr->IfTrueSuccessor())) { + __ jmp(codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); } - __ j(kNotEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + return; } else { - __ j(X86Condition(cond->AsCondition()->GetCondition()), - codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + DCHECK_EQ(cond_value, 0); } } else { - Location lhs = cond->GetLocations()->InAt(0); - Location rhs = cond->GetLocations()->InAt(1); - // LHS is guaranteed to be in a register (see LocationsBuilderX86::VisitCondition). - if (rhs.IsRegister()) { - __ cmpl(lhs.As<Register>(), rhs.As<Register>()); - } else if (rhs.IsConstant()) { - HIntConstant* instruction = rhs.GetConstant()->AsIntConstant(); - Immediate imm(instruction->AsIntConstant()->GetValue()); - __ cmpl(lhs.As<Register>(), imm); + bool materialized = + !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization(); + // Moves do not affect the eflags register, so if the condition is + // evaluated just before the if, we don't need to evaluate it + // again. + bool eflags_set = cond->IsCondition() + && cond->AsCondition()->IsBeforeWhenDisregardMoves(if_instr); + if (materialized) { + if (!eflags_set) { + // Materialized condition, compare against 0. + Location lhs = if_instr->GetLocations()->InAt(0); + if (lhs.IsRegister()) { + __ cmpl(lhs.As<Register>(), Immediate(0)); + } else { + __ cmpl(Address(ESP, lhs.GetStackIndex()), Immediate(0)); + } + __ j(kNotEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + } else { + __ j(X86Condition(cond->AsCondition()->GetCondition()), + codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + } } else { - __ cmpl(lhs.As<Register>(), Address(ESP, rhs.GetStackIndex())); + Location lhs = cond->GetLocations()->InAt(0); + Location rhs = cond->GetLocations()->InAt(1); + // LHS is guaranteed to be in a register (see + // LocationsBuilderX86::VisitCondition). + if (rhs.IsRegister()) { + __ cmpl(lhs.As<Register>(), rhs.As<Register>()); + } else if (rhs.IsConstant()) { + HIntConstant* instruction = rhs.GetConstant()->AsIntConstant(); + Immediate imm(instruction->AsIntConstant()->GetValue()); + __ cmpl(lhs.As<Register>(), imm); + } else { + __ cmpl(lhs.As<Register>(), Address(ESP, rhs.GetStackIndex())); + } + __ j(X86Condition(cond->AsCondition()->GetCondition()), + codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); } - __ j(X86Condition(cond->AsCondition()->GetCondition()), - codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); } - if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) { + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), + if_instr->IfFalseSuccessor())) { __ jmp(codegen_->GetLabelOf(if_instr->IfFalseSuccessor())); } } @@ -664,8 +707,8 @@ void InstructionCodeGeneratorX86::VisitStoreLocal(HStoreLocal* store) { void LocationsBuilderX86::VisitCondition(HCondition* comp) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt(1, Location::Any(), Location::kDiesAtEntry); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::Any()); if (comp->NeedsMaterialization()) { locations->SetOut(Location::RequiresRegister()); } @@ -747,6 +790,7 @@ void LocationsBuilderX86::VisitIntConstant(HIntConstant* constant) { } void InstructionCodeGeneratorX86::VisitIntConstant(HIntConstant* constant) { + // Will be generated at use site. } void LocationsBuilderX86::VisitLongConstant(HLongConstant* constant) { @@ -759,6 +803,26 @@ void InstructionCodeGeneratorX86::VisitLongConstant(HLongConstant* constant) { // Will be generated at use site. } +void LocationsBuilderX86::VisitFloatConstant(HFloatConstant* constant) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorX86::VisitFloatConstant(HFloatConstant* constant) { + // Will be generated at use site. +} + +void LocationsBuilderX86::VisitDoubleConstant(HDoubleConstant* constant) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorX86::VisitDoubleConstant(HDoubleConstant* constant) { + // Will be generated at use site. +} + void LocationsBuilderX86::VisitReturnVoid(HReturnVoid* ret) { ret->SetLocations(nullptr); } @@ -921,6 +985,47 @@ void InstructionCodeGeneratorX86::VisitInvokeVirtual(HInvokeVirtual* invoke) { codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } +void LocationsBuilderX86::VisitNeg(HNeg* neg) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); + switch (neg->GetResultType()) { + case Primitive::kPrimInt: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType(); + break; + + default: + LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); + } +} + +void InstructionCodeGeneratorX86::VisitNeg(HNeg* neg) { + LocationSummary* locations = neg->GetLocations(); + Location out = locations->Out(); + Location in = locations->InAt(0); + switch (neg->GetResultType()) { + case Primitive::kPrimInt: + DCHECK(in.IsRegister()); + __ negl(out.As<Register>()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType(); + break; + + default: + LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); + } +} + void LocationsBuilderX86::VisitAdd(HAdd* add) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall); @@ -951,16 +1056,13 @@ void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) { LocationSummary* locations = add->GetLocations(); Location first = locations->InAt(0); Location second = locations->InAt(1); - + DCHECK(first.Equals(locations->Out())); switch (add->GetResultType()) { case Primitive::kPrimInt: { - DCHECK_EQ(first.As<Register>(), locations->Out().As<Register>()); if (second.IsRegister()) { __ addl(first.As<Register>(), second.As<Register>()); } else if (second.IsConstant()) { - HConstant* instruction = second.GetConstant(); - Immediate imm(instruction->AsIntConstant()->GetValue()); - __ addl(first.As<Register>(), imm); + __ addl(first.As<Register>(), Immediate(second.GetConstant()->AsIntConstant()->GetValue())); } else { __ addl(first.As<Register>(), Address(ESP, second.GetStackIndex())); } @@ -968,10 +1070,6 @@ void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) { } case Primitive::kPrimLong: { - DCHECK_EQ(first.AsRegisterPairLow<Register>(), - locations->Out().AsRegisterPairLow<Register>()); - DCHECK_EQ(first.AsRegisterPairHigh<Register>(), - locations->Out().AsRegisterPairHigh<Register>()); if (second.IsRegister()) { __ addl(first.AsRegisterPairLow<Register>(), second.AsRegisterPairLow<Register>()); __ adcl(first.AsRegisterPairHigh<Register>(), second.AsRegisterPairHigh<Register>()); @@ -1017,16 +1115,16 @@ void LocationsBuilderX86::VisitSub(HSub* sub) { locations->SetOut(Location::SameAsFirstInput()); break; } - - case Primitive::kPrimBoolean: - case Primitive::kPrimByte: - case Primitive::kPrimChar: - case Primitive::kPrimShort: - LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); break; + } default: - LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType(); + LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); } } @@ -1034,52 +1132,153 @@ void InstructionCodeGeneratorX86::VisitSub(HSub* sub) { LocationSummary* locations = sub->GetLocations(); Location first = locations->InAt(0); Location second = locations->InAt(1); + DCHECK(first.Equals(locations->Out())); switch (sub->GetResultType()) { case Primitive::kPrimInt: { - DCHECK_EQ(first.As<Register>(), - locations->Out().As<Register>()); if (second.IsRegister()) { - __ subl(first.As<Register>(), - second.As<Register>()); + __ subl(first.As<Register>(), second.As<Register>()); } else if (second.IsConstant()) { - HConstant* instruction = second.GetConstant(); - Immediate imm(instruction->AsIntConstant()->GetValue()); - __ subl(first.As<Register>(), imm); + __ subl(first.As<Register>(), Immediate(second.GetConstant()->AsIntConstant()->GetValue())); } else { - __ subl(first.As<Register>(), - Address(ESP, second.GetStackIndex())); + __ subl(first.As<Register>(), Address(ESP, second.GetStackIndex())); } break; } case Primitive::kPrimLong: { - DCHECK_EQ(first.AsRegisterPairLow<Register>(), - locations->Out().AsRegisterPairLow<Register>()); - DCHECK_EQ(first.AsRegisterPairHigh<Register>(), - locations->Out().AsRegisterPairHigh<Register>()); if (second.IsRegister()) { - __ subl(first.AsRegisterPairLow<Register>(), - second.AsRegisterPairLow<Register>()); - __ sbbl(first.AsRegisterPairHigh<Register>(), - second.AsRegisterPairHigh<Register>()); + __ subl(first.AsRegisterPairLow<Register>(), second.AsRegisterPairLow<Register>()); + __ sbbl(first.AsRegisterPairHigh<Register>(), second.AsRegisterPairHigh<Register>()); } else { - __ subl(first.AsRegisterPairLow<Register>(), - Address(ESP, second.GetStackIndex())); + __ subl(first.AsRegisterPairLow<Register>(), Address(ESP, second.GetStackIndex())); __ sbbl(first.AsRegisterPairHigh<Register>(), Address(ESP, second.GetHighStackIndex(kX86WordSize))); } break; } - case Primitive::kPrimBoolean: - case Primitive::kPrimByte: - case Primitive::kPrimChar: - case Primitive::kPrimShort: + case Primitive::kPrimFloat: { + __ subss(first.As<XmmRegister>(), second.As<XmmRegister>()); + break; + } + + case Primitive::kPrimDouble: { + __ subsd(first.As<XmmRegister>(), second.As<XmmRegister>()); + break; + } + + default: LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); + } +} + +void LocationsBuilderX86::VisitMul(HMul* mul) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall); + switch (mul->GetResultType()) { + case Primitive::kPrimInt: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::Any()); + locations->SetOut(Location::SameAsFirstInput()); + break; + case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RequiresRegister()); + // TODO: Currently this handles only stack operands: + // - we don't have enough registers because we currently use Quick ABI. + // - by the time we have a working register allocator we will probably change the ABI + // and fix the above. + // - we don't have a way yet to request operands on stack but the base line compiler + // will leave the operands on the stack with Any(). + locations->SetInAt(1, Location::Any()); + locations->SetOut(Location::SameAsFirstInput()); + // Needed for imul on 32bits with 64bits output. + locations->AddTemp(Location::RegisterLocation(EAX)); + locations->AddTemp(Location::RegisterLocation(EDX)); + break; + } + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + } + + default: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + } +} + +void InstructionCodeGeneratorX86::VisitMul(HMul* mul) { + LocationSummary* locations = mul->GetLocations(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + DCHECK(first.Equals(locations->Out())); + + switch (mul->GetResultType()) { + case Primitive::kPrimInt: { + if (second.IsRegister()) { + __ imull(first.As<Register>(), second.As<Register>()); + } else if (second.IsConstant()) { + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue()); + __ imull(first.As<Register>(), imm); + } else { + DCHECK(second.IsStackSlot()); + __ imull(first.As<Register>(), Address(ESP, second.GetStackIndex())); + } + break; + } + + case Primitive::kPrimLong: { + DCHECK(second.IsDoubleStackSlot()); + + Register in1_hi = first.AsRegisterPairHigh<Register>(); + Register in1_lo = first.AsRegisterPairLow<Register>(); + Address in2_hi(ESP, second.GetHighStackIndex(kX86WordSize)); + Address in2_lo(ESP, second.GetStackIndex()); + Register eax = locations->GetTemp(0).As<Register>(); + Register edx = locations->GetTemp(1).As<Register>(); + + DCHECK_EQ(EAX, eax); + DCHECK_EQ(EDX, edx); + + // input: in1 - 64 bits, in2 - 64 bits + // output: in1 + // formula: in1.hi : in1.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo + // parts: in1.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32] + // parts: in1.lo = (in1.lo * in2.lo)[31:0] + + __ movl(eax, in2_hi); + // eax <- in1.lo * in2.hi + __ imull(eax, in1_lo); + // in1.hi <- in1.hi * in2.lo + __ imull(in1_hi, in2_lo); + // in1.hi <- in1.lo * in2.hi + in1.hi * in2.lo + __ addl(in1_hi, eax); + // move in1_lo to eax to prepare for double precision + __ movl(eax, in1_lo); + // edx:eax <- in1.lo * in2.lo + __ mull(in2_lo); + // in1.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32] + __ addl(in1_hi, edx); + // in1.lo <- (in1.lo * in2.lo)[31:0]; + __ movl(in1_lo, eax); + + break; + } + + case Primitive::kPrimFloat: { + __ mulss(first.As<XmmRegister>(), second.As<XmmRegister>()); + break; + } + + case Primitive::kPrimDouble: { + __ mulsd(first.As<XmmRegister>(), second.As<XmmRegister>()); break; + } default: - LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType(); + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); } } @@ -1104,6 +1303,28 @@ void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) { DCHECK(!codegen_->IsLeafMethod()); } +void LocationsBuilderX86::VisitNewArray(HNewArray* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); + locations->SetOut(Location::RegisterLocation(EAX)); + InvokeRuntimeCallingConvention calling_convention; + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); +} + +void InstructionCodeGeneratorX86::VisitNewArray(HNewArray* instruction) { + InvokeRuntimeCallingConvention calling_convention; + LoadCurrentMethod(calling_convention.GetRegisterAt(1)); + __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex())); + + __ fs()->call( + Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocArrayWithAccessCheck))); + + codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); + DCHECK(!codegen_->IsLeafMethod()); +} + void LocationsBuilderX86::VisitParameterValue(HParameterValue* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); @@ -1119,26 +1340,42 @@ void LocationsBuilderX86::VisitParameterValue(HParameterValue* instruction) { void InstructionCodeGeneratorX86::VisitParameterValue(HParameterValue* instruction) { } -void LocationsBuilderX86::VisitNot(HNot* instruction) { +void LocationsBuilderX86::VisitNot(HNot* not_) { LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); locations->SetOut(Location::SameAsFirstInput()); } -void InstructionCodeGeneratorX86::VisitNot(HNot* instruction) { - LocationSummary* locations = instruction->GetLocations(); +void InstructionCodeGeneratorX86::VisitNot(HNot* not_) { + LocationSummary* locations = not_->GetLocations(); + DCHECK_EQ(locations->InAt(0).As<Register>(), locations->Out().As<Register>()); Location out = locations->Out(); DCHECK_EQ(locations->InAt(0).As<Register>(), out.As<Register>()); - __ xorl(out.As<Register>(), Immediate(1)); + switch (not_->InputAt(0)->GetType()) { + case Primitive::kPrimBoolean: + __ xorl(out.As<Register>(), Immediate(1)); + break; + + case Primitive::kPrimInt: + __ notl(out.As<Register>()); + break; + + case Primitive::kPrimLong: + LOG(FATAL) << "Not yet implemented type for not operation " << not_->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType(); + } } void LocationsBuilderX86::VisitCompare(HCompare* compare) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt(1, Location::Any(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::Any()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) { @@ -1207,12 +1444,11 @@ void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) || (field_type == Primitive::kPrimByte); // The register allocator does not support multiple // inputs that die at entry with one in a specific register. - bool dies_at_entry = !is_object_type && !is_byte_type; if (is_byte_type) { // Ensure the value is in a byte register. - locations->SetInAt(1, Location::RegisterLocation(EAX), dies_at_entry); + locations->SetInAt(1, Location::RegisterLocation(EAX)); } else { - locations->SetInAt(1, Location::RequiresRegister(), dies_at_entry); + locations->SetInAt(1, Location::RequiresRegister()); } // Temporary registers for the write barrier. if (is_object_type) { @@ -1288,8 +1524,8 @@ void CodeGeneratorX86::MarkGCCard(Register temp, Register card, Register object, void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { @@ -1356,7 +1592,7 @@ void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) { } void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) { - SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction); + SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction); codegen_->AddSlowPath(slow_path); LocationSummary* locations = instruction->GetLocations(); @@ -1378,10 +1614,9 @@ void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) { void LocationsBuilderX86::VisitArrayGet(HArrayGet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt( - 1, Location::RegisterOrConstant(instruction->InputAt(1)), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { @@ -1494,16 +1729,13 @@ void LocationsBuilderX86::VisitArraySet(HArraySet* instruction) { // We need the inputs to be different than the output in case of long operation. // In case of a byte operation, the register allocator does not support multiple // inputs that die at entry with one in a specific register. - bool dies_at_entry = value_type != Primitive::kPrimLong && !is_byte_type; - locations->SetInAt(0, Location::RequiresRegister(), dies_at_entry); - locations->SetInAt( - 1, Location::RegisterOrConstant(instruction->InputAt(1)), dies_at_entry); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); if (is_byte_type) { // Ensure the value is in a byte register. - locations->SetInAt(2, Location::ByteRegisterOrConstant( - EAX, instruction->InputAt(2)), dies_at_entry); + locations->SetInAt(2, Location::ByteRegisterOrConstant(EAX, instruction->InputAt(2))); } else { - locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2)), dies_at_entry); + locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2))); } } } @@ -1633,8 +1865,8 @@ void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) { void LocationsBuilderX86::VisitArrayLength(HArrayLength* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); instruction->SetLocations(locations); } @@ -1658,7 +1890,7 @@ void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) { void InstructionCodeGeneratorX86::VisitBoundsCheck(HBoundsCheck* instruction) { LocationSummary* locations = instruction->GetLocations(); - SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86( + SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86( instruction, locations->InAt(0), locations->InAt(1)); codegen_->AddSlowPath(slow_path); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index a1a72a2bd7..fff91d179a 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -142,10 +142,10 @@ class CodeGeneratorX86 : public CodeGenerator { virtual void GenerateFrameEntry() OVERRIDE; virtual void GenerateFrameExit() OVERRIDE; - virtual void Bind(Label* label) OVERRIDE; + virtual void Bind(HBasicBlock* block) OVERRIDE; virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE; - virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE; - virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE; + virtual size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; + virtual size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; virtual size_t GetWordSize() const OVERRIDE { return kX86WordSize; @@ -166,6 +166,7 @@ class CodeGeneratorX86 : public CodeGenerator { } virtual void SetupBlockedRegisters() const OVERRIDE; + virtual Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE; virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE; @@ -173,6 +174,9 @@ class CodeGeneratorX86 : public CodeGenerator { virtual void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE; virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE; + // Blocks all register pairs made out of blocked core registers. + void UpdateBlockedPairRegisters() const; + ParallelMoveResolverX86* GetMoveResolver() { return &move_resolver_; } @@ -189,7 +193,17 @@ class CodeGeneratorX86 : public CodeGenerator { // Emit a write barrier. void MarkGCCard(Register temp, Register card, Register object, Register value); + Label* GetLabelOf(HBasicBlock* block) const { + return block_labels_.GetRawStorage() + block->GetBlockId(); + } + + virtual void Initialize() OVERRIDE { + block_labels_.SetSize(GetGraph()->GetBlocks().Size()); + } + private: + // Labels for each block that will be compiled. + GrowableArray<Label> block_labels_; LocationsBuilderX86 location_builder_; InstructionCodeGeneratorX86 instruction_visitor_; ParallelMoveResolverX86 move_resolver_; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 21b21f39d4..4a05b89892 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -60,7 +60,21 @@ class InvokeRuntimeCallingConvention : public CallingConvention<Register, FloatR #define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())-> -class NullCheckSlowPathX86_64 : public SlowPathCode { +class SlowPathCodeX86_64 : public SlowPathCode { + public: + SlowPathCodeX86_64() : entry_label_(), exit_label_() {} + + Label* GetEntryLabel() { return &entry_label_; } + Label* GetExitLabel() { return &exit_label_; } + + private: + Label entry_label_; + Label exit_label_; + + DISALLOW_COPY_AND_ASSIGN(SlowPathCodeX86_64); +}; + +class NullCheckSlowPathX86_64 : public SlowPathCodeX86_64 { public: explicit NullCheckSlowPathX86_64(HNullCheck* instruction) : instruction_(instruction) {} @@ -76,7 +90,7 @@ class NullCheckSlowPathX86_64 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86_64); }; -class StackOverflowCheckSlowPathX86_64 : public SlowPathCode { +class StackOverflowCheckSlowPathX86_64 : public SlowPathCodeX86_64 { public: StackOverflowCheckSlowPathX86_64() {} @@ -92,12 +106,13 @@ class StackOverflowCheckSlowPathX86_64 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86_64); }; -class SuspendCheckSlowPathX86_64 : public SlowPathCode { +class SuspendCheckSlowPathX86_64 : public SlowPathCodeX86_64 { public: explicit SuspendCheckSlowPathX86_64(HSuspendCheck* instruction, HBasicBlock* successor) : instruction_(instruction), successor_(successor) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); __ Bind(GetEntryLabel()); codegen->SaveLiveRegisters(instruction_->GetLocations()); __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pTestSuspend), true)); @@ -106,7 +121,7 @@ class SuspendCheckSlowPathX86_64 : public SlowPathCode { if (successor_ == nullptr) { __ jmp(GetReturnLabel()); } else { - __ jmp(codegen->GetLabelOf(successor_)); + __ jmp(x64_codegen->GetLabelOf(successor_)); } } @@ -123,7 +138,7 @@ class SuspendCheckSlowPathX86_64 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathX86_64); }; -class BoundsCheckSlowPathX86_64 : public SlowPathCode { +class BoundsCheckSlowPathX86_64 : public SlowPathCodeX86_64 { public: BoundsCheckSlowPathX86_64(HBoundsCheck* instruction, Location index_location, @@ -133,7 +148,7 @@ class BoundsCheckSlowPathX86_64 : public SlowPathCode { length_location_(length_location) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { - CodeGeneratorX86_64* x64_codegen = reinterpret_cast<CodeGeneratorX86_64*>(codegen); + CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); __ Bind(GetEntryLabel()); InvokeRuntimeCallingConvention calling_convention; x64_codegen->Move(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_); @@ -176,16 +191,29 @@ void CodeGeneratorX86_64::DumpFloatingPointRegister(std::ostream& stream, int re stream << X86_64ManagedRegister::FromXmmRegister(FloatRegister(reg)); } -void CodeGeneratorX86_64::SaveCoreRegister(Location stack_location, uint32_t reg_id) { - __ movq(Address(CpuRegister(RSP), stack_location.GetStackIndex()), CpuRegister(reg_id)); +size_t CodeGeneratorX86_64::SaveCoreRegister(size_t stack_index, uint32_t reg_id) { + __ movq(Address(CpuRegister(RSP), stack_index), CpuRegister(reg_id)); + return kX86_64WordSize; +} + +size_t CodeGeneratorX86_64::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) { + __ movq(CpuRegister(reg_id), Address(CpuRegister(RSP), stack_index)); + return kX86_64WordSize; } -void CodeGeneratorX86_64::RestoreCoreRegister(Location stack_location, uint32_t reg_id) { - __ movq(CpuRegister(reg_id), Address(CpuRegister(RSP), stack_location.GetStackIndex())); +size_t CodeGeneratorX86_64::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) { + __ movsd(Address(CpuRegister(RSP), stack_index), XmmRegister(reg_id)); + return kX86_64WordSize; +} + +size_t CodeGeneratorX86_64::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) { + __ movsd(XmmRegister(reg_id), Address(CpuRegister(RSP), stack_index)); + return kX86_64WordSize; } CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph) : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfFloatRegisters, 0), + block_labels_(graph->GetArena(), 0), location_builder_(graph, this), instruction_visitor_(graph, this), move_resolver_(graph->GetArena(), this) {} @@ -266,7 +294,7 @@ void CodeGeneratorX86_64::GenerateFrameEntry() { Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize)); if (!skip_overflow_check && kExplicitStackOverflowCheck) { - SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86_64(); + SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86_64(); AddSlowPath(slow_path); __ gs()->cmpq(CpuRegister(RSP), @@ -282,8 +310,8 @@ void CodeGeneratorX86_64::GenerateFrameExit() { Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize)); } -void CodeGeneratorX86_64::Bind(Label* label) { - __ Bind(label); +void CodeGeneratorX86_64::Bind(HBasicBlock* block) { + __ Bind(GetLabelOf(block)); } void InstructionCodeGeneratorX86_64::LoadCurrentMethod(CpuRegister reg) { @@ -375,22 +403,28 @@ void CodeGeneratorX86_64::Move(Location destination, Location source) { void CodeGeneratorX86_64::Move(HInstruction* instruction, Location location, HInstruction* move_for) { - if (instruction->AsIntConstant() != nullptr) { + if (instruction->IsIntConstant()) { Immediate imm(instruction->AsIntConstant()->GetValue()); if (location.IsRegister()) { __ movl(location.As<CpuRegister>(), imm); - } else { + } else if (location.IsStackSlot()) { __ movl(Address(CpuRegister(RSP), location.GetStackIndex()), imm); + } else { + DCHECK(location.IsConstant()); + DCHECK_EQ(location.GetConstant(), instruction); } - } else if (instruction->AsLongConstant() != nullptr) { + } else if (instruction->IsLongConstant()) { int64_t value = instruction->AsLongConstant()->GetValue(); if (location.IsRegister()) { __ movq(location.As<CpuRegister>(), Immediate(value)); - } else { + } else if (location.IsDoubleStackSlot()) { __ movq(CpuRegister(TMP), Immediate(value)); __ movq(Address(CpuRegister(RSP), location.GetStackIndex()), CpuRegister(TMP)); + } else { + DCHECK(location.IsConstant()); + DCHECK_EQ(location.GetConstant(), instruction); } - } else if (instruction->AsLoadLocal() != nullptr) { + } else if (instruction->IsLoadLocal()) { switch (instruction->GetType()) { case Primitive::kPrimBoolean: case Primitive::kPrimByte: @@ -473,46 +507,65 @@ void LocationsBuilderX86_64::VisitIf(HIf* if_instr) { new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall); HInstruction* cond = if_instr->InputAt(0); if (!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization()) { - locations->SetInAt(0, Location::Any(), Location::kDiesAtEntry); + locations->SetInAt(0, Location::Any()); } } void InstructionCodeGeneratorX86_64::VisitIf(HIf* if_instr) { HInstruction* cond = if_instr->InputAt(0); - bool materialized = !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization(); - // Moves do not affect the eflags register, so if the condition is evaluated - // just before the if, we don't need to evaluate it again. - bool eflags_set = cond->IsCondition() - && cond->AsCondition()->IsBeforeWhenDisregardMoves(if_instr); - if (materialized) { - if (!eflags_set) { - // Materialized condition, compare against 0. - Location lhs = if_instr->GetLocations()->InAt(0); - if (lhs.IsRegister()) { - __ cmpl(lhs.As<CpuRegister>(), Immediate(0)); - } else { - __ cmpl(Address(CpuRegister(RSP), lhs.GetStackIndex()), Immediate(0)); + if (cond->IsIntConstant()) { + // Constant condition, statically compared against 1. + int32_t cond_value = cond->AsIntConstant()->GetValue(); + if (cond_value == 1) { + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), + if_instr->IfTrueSuccessor())) { + __ jmp(codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); } - __ j(kNotEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + return; } else { - __ j(X86_64Condition(cond->AsCondition()->GetCondition()), - codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + DCHECK_EQ(cond_value, 0); } } else { - Location lhs = cond->GetLocations()->InAt(0); - Location rhs = cond->GetLocations()->InAt(1); - if (rhs.IsRegister()) { - __ cmpl(lhs.As<CpuRegister>(), rhs.As<CpuRegister>()); - } else if (rhs.IsConstant()) { - __ cmpl(lhs.As<CpuRegister>(), - Immediate(rhs.GetConstant()->AsIntConstant()->GetValue())); + bool materialized = + !cond->IsCondition() || cond->AsCondition()->NeedsMaterialization(); + // Moves do not affect the eflags register, so if the condition is + // evaluated just before the if, we don't need to evaluate it + // again. + bool eflags_set = cond->IsCondition() + && cond->AsCondition()->IsBeforeWhenDisregardMoves(if_instr); + if (materialized) { + if (!eflags_set) { + // Materialized condition, compare against 0. + Location lhs = if_instr->GetLocations()->InAt(0); + if (lhs.IsRegister()) { + __ cmpl(lhs.As<CpuRegister>(), Immediate(0)); + } else { + __ cmpl(Address(CpuRegister(RSP), lhs.GetStackIndex()), + Immediate(0)); + } + __ j(kNotEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + } else { + __ j(X86_64Condition(cond->AsCondition()->GetCondition()), + codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); + } } else { - __ cmpl(lhs.As<CpuRegister>(), Address(CpuRegister(RSP), rhs.GetStackIndex())); + Location lhs = cond->GetLocations()->InAt(0); + Location rhs = cond->GetLocations()->InAt(1); + if (rhs.IsRegister()) { + __ cmpl(lhs.As<CpuRegister>(), rhs.As<CpuRegister>()); + } else if (rhs.IsConstant()) { + __ cmpl(lhs.As<CpuRegister>(), + Immediate(rhs.GetConstant()->AsIntConstant()->GetValue())); + } else { + __ cmpl(lhs.As<CpuRegister>(), + Address(CpuRegister(RSP), rhs.GetStackIndex())); + } + __ j(X86_64Condition(cond->AsCondition()->GetCondition()), + codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); } - __ j(X86_64Condition(cond->AsCondition()->GetCondition()), - codegen_->GetLabelOf(if_instr->IfTrueSuccessor())); } - if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) { + if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), + if_instr->IfFalseSuccessor())) { __ jmp(codegen_->GetLabelOf(if_instr->IfFalseSuccessor())); } } @@ -563,8 +616,8 @@ void InstructionCodeGeneratorX86_64::VisitStoreLocal(HStoreLocal* store) { void LocationsBuilderX86_64::VisitCondition(HCondition* comp) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt(1, Location::Any(), Location::kDiesAtEntry); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::Any()); if (comp->NeedsMaterialization()) { locations->SetOut(Location::RequiresRegister()); } @@ -641,9 +694,9 @@ void InstructionCodeGeneratorX86_64::VisitGreaterThanOrEqual(HGreaterThanOrEqual void LocationsBuilderX86_64::VisitCompare(HCompare* compare) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetInAt(1, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorX86_64::VisitCompare(HCompare* compare) { @@ -679,6 +732,7 @@ void LocationsBuilderX86_64::VisitIntConstant(HIntConstant* constant) { } void InstructionCodeGeneratorX86_64::VisitIntConstant(HIntConstant* constant) { + // Will be generated at use site. } void LocationsBuilderX86_64::VisitLongConstant(HLongConstant* constant) { @@ -688,6 +742,27 @@ void LocationsBuilderX86_64::VisitLongConstant(HLongConstant* constant) { } void InstructionCodeGeneratorX86_64::VisitLongConstant(HLongConstant* constant) { + // Will be generated at use site. +} + +void LocationsBuilderX86_64::VisitFloatConstant(HFloatConstant* constant) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorX86_64::VisitFloatConstant(HFloatConstant* constant) { + // Will be generated at use site. +} + +void LocationsBuilderX86_64::VisitDoubleConstant(HDoubleConstant* constant) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); + locations->SetOut(Location::ConstantLocation(constant)); +} + +void InstructionCodeGeneratorX86_64::VisitDoubleConstant(HDoubleConstant* constant) { + // Will be generated at use site. } void LocationsBuilderX86_64::VisitReturnVoid(HReturnVoid* ret) { @@ -896,6 +971,47 @@ void InstructionCodeGeneratorX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } +void LocationsBuilderX86_64::VisitNeg(HNeg* neg) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); + switch (neg->GetResultType()) { + case Primitive::kPrimInt: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType(); + break; + + default: + LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); + } +} + +void InstructionCodeGeneratorX86_64::VisitNeg(HNeg* neg) { + LocationSummary* locations = neg->GetLocations(); + Location out = locations->Out(); + Location in = locations->InAt(0); + switch (neg->GetResultType()) { + case Primitive::kPrimInt: + DCHECK(in.IsRegister()); + __ negl(out.As<CpuRegister>()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType(); + break; + + default: + LOG(FATAL) << "Unexpected neg type " << neg->GetResultType(); + } +} + void LocationsBuilderX86_64::VisitAdd(HAdd* add) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall); @@ -917,7 +1033,7 @@ void LocationsBuilderX86_64::VisitAdd(HAdd* add) { case Primitive::kPrimDouble: case Primitive::kPrimFloat: { locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetInAt(1, Location::Any()); + locations->SetInAt(1, Location::RequiresFpuRegister()); locations->SetOut(Location::SameAsFirstInput()); break; } @@ -931,19 +1047,17 @@ void InstructionCodeGeneratorX86_64::VisitAdd(HAdd* add) { LocationSummary* locations = add->GetLocations(); Location first = locations->InAt(0); Location second = locations->InAt(1); - DCHECK(first.Equals(locations->Out())); + switch (add->GetResultType()) { case Primitive::kPrimInt: { if (second.IsRegister()) { __ addl(first.As<CpuRegister>(), second.As<CpuRegister>()); } else if (second.IsConstant()) { - HConstant* instruction = second.GetConstant(); - Immediate imm(instruction->AsIntConstant()->GetValue()); + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue()); __ addl(first.As<CpuRegister>(), imm); } else { - __ addl(first.As<CpuRegister>(), - Address(CpuRegister(RSP), second.GetStackIndex())); + __ addl(first.As<CpuRegister>(), Address(CpuRegister(RSP), second.GetStackIndex())); } break; } @@ -954,21 +1068,12 @@ void InstructionCodeGeneratorX86_64::VisitAdd(HAdd* add) { } case Primitive::kPrimFloat: { - if (second.IsFpuRegister()) { - __ addss(first.As<XmmRegister>(), second.As<XmmRegister>()); - } else { - __ addss(first.As<XmmRegister>(), - Address(CpuRegister(RSP), second.GetStackIndex())); - } + __ addss(first.As<XmmRegister>(), second.As<XmmRegister>()); break; } case Primitive::kPrimDouble: { - if (second.IsFpuRegister()) { - __ addsd(first.As<XmmRegister>(), second.As<XmmRegister>()); - } else { - __ addsd(first.As<XmmRegister>(), Address(CpuRegister(RSP), second.GetStackIndex())); - } + __ addsd(first.As<XmmRegister>(), second.As<XmmRegister>()); break; } @@ -993,53 +1098,119 @@ void LocationsBuilderX86_64::VisitSub(HSub* sub) { locations->SetOut(Location::SameAsFirstInput()); break; } - - case Primitive::kPrimBoolean: - case Primitive::kPrimByte: - case Primitive::kPrimChar: - case Primitive::kPrimShort: - LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); break; - + } default: - LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType(); + LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); } } void InstructionCodeGeneratorX86_64::VisitSub(HSub* sub) { LocationSummary* locations = sub->GetLocations(); - DCHECK_EQ(locations->InAt(0).As<CpuRegister>().AsRegister(), - locations->Out().As<CpuRegister>().AsRegister()); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + DCHECK(first.Equals(locations->Out())); switch (sub->GetResultType()) { case Primitive::kPrimInt: { - if (locations->InAt(1).IsRegister()) { - __ subl(locations->InAt(0).As<CpuRegister>(), - locations->InAt(1).As<CpuRegister>()); - } else if (locations->InAt(1).IsConstant()) { - HConstant* instruction = locations->InAt(1).GetConstant(); - Immediate imm(instruction->AsIntConstant()->GetValue()); - __ subl(locations->InAt(0).As<CpuRegister>(), imm); + if (second.IsRegister()) { + __ subl(first.As<CpuRegister>(), second.As<CpuRegister>()); + } else if (second.IsConstant()) { + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue()); + __ subl(first.As<CpuRegister>(), imm); } else { - __ subl(locations->InAt(0).As<CpuRegister>(), - Address(CpuRegister(RSP), locations->InAt(1).GetStackIndex())); + __ subl(first.As<CpuRegister>(), Address(CpuRegister(RSP), second.GetStackIndex())); } break; } case Primitive::kPrimLong: { - __ subq(locations->InAt(0).As<CpuRegister>(), - locations->InAt(1).As<CpuRegister>()); + __ subq(first.As<CpuRegister>(), second.As<CpuRegister>()); break; } - case Primitive::kPrimBoolean: - case Primitive::kPrimByte: - case Primitive::kPrimChar: - case Primitive::kPrimShort: + case Primitive::kPrimFloat: { + __ subss(first.As<XmmRegister>(), second.As<XmmRegister>()); + break; + } + + case Primitive::kPrimDouble: { + __ subsd(first.As<XmmRegister>(), second.As<XmmRegister>()); + break; + } + + default: LOG(FATAL) << "Unexpected sub type " << sub->GetResultType(); + } +} + +void LocationsBuilderX86_64::VisitMul(HMul* mul) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall); + switch (mul->GetResultType()) { + case Primitive::kPrimInt: { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::Any()); + locations->SetOut(Location::SameAsFirstInput()); + break; + } + case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); break; + } + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + } default: - LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType(); + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + } +} + +void InstructionCodeGeneratorX86_64::VisitMul(HMul* mul) { + LocationSummary* locations = mul->GetLocations(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + DCHECK(first.Equals(locations->Out())); + switch (mul->GetResultType()) { + case Primitive::kPrimInt: { + if (second.IsRegister()) { + __ imull(first.As<CpuRegister>(), second.As<CpuRegister>()); + } else if (second.IsConstant()) { + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue()); + __ imull(first.As<CpuRegister>(), imm); + } else { + DCHECK(second.IsStackSlot()); + __ imull(first.As<CpuRegister>(), Address(CpuRegister(RSP), second.GetStackIndex())); + } + break; + } + case Primitive::kPrimLong: { + __ imulq(first.As<CpuRegister>(), second.As<CpuRegister>()); + break; + } + + case Primitive::kPrimFloat: { + __ mulss(first.As<XmmRegister>(), second.As<XmmRegister>()); + break; + } + + case Primitive::kPrimDouble: { + __ mulsd(first.As<XmmRegister>(), second.As<XmmRegister>()); + break; + } + + default: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); } } @@ -1064,6 +1235,28 @@ void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction) codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); } +void LocationsBuilderX86_64::VisitNewArray(HNewArray* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); + InvokeRuntimeCallingConvention calling_convention; + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetOut(Location::RegisterLocation(RAX)); + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); +} + +void InstructionCodeGeneratorX86_64::VisitNewArray(HNewArray* instruction) { + InvokeRuntimeCallingConvention calling_convention; + LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1))); + __ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex())); + + __ gs()->call(Address::Absolute( + QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocArrayWithAccessCheck), true)); + + DCHECK(!codegen_->IsLeafMethod()); + codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); +} + void LocationsBuilderX86_64::VisitParameterValue(HParameterValue* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); @@ -1080,18 +1273,34 @@ void InstructionCodeGeneratorX86_64::VisitParameterValue(HParameterValue* instru // Nothing to do, the parameter is already at its location. } -void LocationsBuilderX86_64::VisitNot(HNot* instruction) { +void LocationsBuilderX86_64::VisitNot(HNot* not_) { LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); locations->SetOut(Location::SameAsFirstInput()); } -void InstructionCodeGeneratorX86_64::VisitNot(HNot* instruction) { - LocationSummary* locations = instruction->GetLocations(); +void InstructionCodeGeneratorX86_64::VisitNot(HNot* not_) { + LocationSummary* locations = not_->GetLocations(); DCHECK_EQ(locations->InAt(0).As<CpuRegister>().AsRegister(), locations->Out().As<CpuRegister>().AsRegister()); - __ xorq(locations->Out().As<CpuRegister>(), Immediate(1)); + Location out = locations->Out(); + switch (not_->InputAt(0)->GetType()) { + case Primitive::kPrimBoolean: + __ xorq(out.As<CpuRegister>(), Immediate(1)); + break; + + case Primitive::kPrimInt: + __ notl(out.As<CpuRegister>()); + break; + + case Primitive::kPrimLong: + LOG(FATAL) << "Not yet implemented type for not operation " << not_->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType(); + } } void LocationsBuilderX86_64::VisitPhi(HPhi* instruction) { @@ -1112,9 +1321,8 @@ void LocationsBuilderX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instructio new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); Primitive::Type field_type = instruction->GetFieldType(); bool is_object_type = field_type == Primitive::kPrimNot; - bool dies_at_entry = !is_object_type; - locations->SetInAt(0, Location::RequiresRegister(), dies_at_entry); - locations->SetInAt(1, Location::RequiresRegister(), dies_at_entry); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); if (is_object_type) { // Temporary registers for the write barrier. locations->AddTemp(Location::RequiresRegister()); @@ -1171,8 +1379,8 @@ void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* in void LocationsBuilderX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { @@ -1233,7 +1441,7 @@ void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) { } void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) { - SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction); + SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction); codegen_->AddSlowPath(slow_path); LocationSummary* locations = instruction->GetLocations(); @@ -1255,10 +1463,10 @@ void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) { void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); + locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt( - 1, Location::RegisterOrConstant(instruction->InputAt(1)), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + 1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { @@ -1341,10 +1549,30 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { break; } - case Primitive::kPrimFloat: - case Primitive::kPrimDouble: - LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); - UNREACHABLE(); + case Primitive::kPrimFloat: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); + XmmRegister out = locations->Out().As<XmmRegister>(); + if (index.IsConstant()) { + __ movss(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset)); + } else { + __ movss(out, Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset)); + } + break; + } + + case Primitive::kPrimDouble: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); + XmmRegister out = locations->Out().As<XmmRegister>(); + if (index.IsConstant()) { + __ movsd(out, Address(obj, + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset)); + } else { + __ movsd(out, Address(obj, index.As<CpuRegister>(), TIMES_8, data_offset)); + } + break; + } + case Primitive::kPrimVoid: LOG(FATAL) << "Unreachable type " << instruction->GetType(); UNREACHABLE(); @@ -1362,14 +1590,16 @@ void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) { locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); } else { - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); + locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt( - 1, Location::RegisterOrConstant(instruction->InputAt(1)), Location::kDiesAtEntry); - locations->SetInAt(2, Location::RequiresRegister(), Location::kDiesAtEntry); + 1, Location::RegisterOrConstant(instruction->InputAt(1))); + locations->SetInAt(2, Location::RequiresRegister()); if (value_type == Primitive::kPrimLong) { - locations->SetInAt(2, Location::RequiresRegister(), Location::kDiesAtEntry); + locations->SetInAt(2, Location::RequiresRegister()); + } else if (value_type == Primitive::kPrimFloat || value_type == Primitive::kPrimDouble) { + locations->SetInAt(2, Location::RequiresFpuRegister()); } else { - locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2)), Location::kDiesAtEntry); + locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2))); } } } @@ -1440,6 +1670,7 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { __ movl(Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset), value.As<CpuRegister>()); } else { + DCHECK(value.IsConstant()) << value; __ movl(Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset), Immediate(value.GetConstant()->AsIntConstant()->GetValue())); } @@ -1468,10 +1699,34 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { break; } - case Primitive::kPrimFloat: - case Primitive::kPrimDouble: - LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); - UNREACHABLE(); + case Primitive::kPrimFloat: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + DCHECK(value.IsFpuRegister()); + __ movss(Address(obj, offset), value.As<XmmRegister>()); + } else { + DCHECK(value.IsFpuRegister()); + __ movss(Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset), + value.As<XmmRegister>()); + } + break; + } + + case Primitive::kPrimDouble: { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + DCHECK(value.IsFpuRegister()); + __ movsd(Address(obj, offset), value.As<XmmRegister>()); + } else { + DCHECK(value.IsFpuRegister()); + __ movsd(Address(obj, index.As<CpuRegister>(), TIMES_8, data_offset), + value.As<XmmRegister>()); + } + break; + } + case Primitive::kPrimVoid: LOG(FATAL) << "Unreachable type " << instruction->GetType(); UNREACHABLE(); @@ -1481,8 +1736,8 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { void LocationsBuilderX86_64::VisitArrayLength(HArrayLength* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); - locations->SetOut(Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } void InstructionCodeGeneratorX86_64::VisitArrayLength(HArrayLength* instruction) { @@ -1505,7 +1760,7 @@ void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) { void InstructionCodeGeneratorX86_64::VisitBoundsCheck(HBoundsCheck* instruction) { LocationSummary* locations = instruction->GetLocations(); - SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64( + SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64( instruction, locations->InAt(0), locations->InAt(1)); codegen_->AddSlowPath(slow_path); @@ -1605,6 +1860,9 @@ void ParallelMoveResolverX86_64::EmitMove(size_t index) { if (destination.IsRegister()) { __ movl(destination.As<CpuRegister>(), Address(CpuRegister(RSP), source.GetStackIndex())); + } else if (destination.IsFpuRegister()) { + __ movss(destination.As<XmmRegister>(), + Address(CpuRegister(RSP), source.GetStackIndex())); } else { DCHECK(destination.IsStackSlot()); __ movl(CpuRegister(TMP), Address(CpuRegister(RSP), source.GetStackIndex())); @@ -1614,8 +1872,10 @@ void ParallelMoveResolverX86_64::EmitMove(size_t index) { if (destination.IsRegister()) { __ movq(destination.As<CpuRegister>(), Address(CpuRegister(RSP), source.GetStackIndex())); + } else if (destination.IsFpuRegister()) { + __ movsd(destination.As<XmmRegister>(), Address(CpuRegister(RSP), source.GetStackIndex())); } else { - DCHECK(destination.IsDoubleStackSlot()); + DCHECK(destination.IsDoubleStackSlot()) << destination; __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), source.GetStackIndex())); __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP)); } @@ -1626,6 +1886,7 @@ void ParallelMoveResolverX86_64::EmitMove(size_t index) { if (destination.IsRegister()) { __ movl(destination.As<CpuRegister>(), imm); } else { + DCHECK(destination.IsStackSlot()) << destination; __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), imm); } } else if (constant->IsLongConstant()) { @@ -1633,14 +1894,42 @@ void ParallelMoveResolverX86_64::EmitMove(size_t index) { if (destination.IsRegister()) { __ movq(destination.As<CpuRegister>(), Immediate(value)); } else { + DCHECK(destination.IsDoubleStackSlot()) << destination; __ movq(CpuRegister(TMP), Immediate(value)); __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP)); } + } else if (constant->IsFloatConstant()) { + Immediate imm(bit_cast<float, int32_t>(constant->AsFloatConstant()->GetValue())); + if (destination.IsFpuRegister()) { + __ movl(CpuRegister(TMP), imm); + __ movd(destination.As<XmmRegister>(), CpuRegister(TMP)); + } else { + DCHECK(destination.IsStackSlot()) << destination; + __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), imm); + } + } else { + DCHECK(constant->IsDoubleConstant()) << constant->DebugName(); + Immediate imm(bit_cast<double, int64_t>(constant->AsDoubleConstant()->GetValue())); + if (destination.IsFpuRegister()) { + __ movq(CpuRegister(TMP), imm); + __ movd(destination.As<XmmRegister>(), CpuRegister(TMP)); + } else { + DCHECK(destination.IsDoubleStackSlot()) << destination; + __ movq(CpuRegister(TMP), imm); + __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP)); + } + } + } else if (source.IsFpuRegister()) { + if (destination.IsFpuRegister()) { + __ movaps(destination.As<XmmRegister>(), source.As<XmmRegister>()); + } else if (destination.IsStackSlot()) { + __ movss(Address(CpuRegister(RSP), destination.GetStackIndex()), + source.As<XmmRegister>()); } else { - LOG(FATAL) << "Unimplemented constant type"; + DCHECK(destination.IsDoubleStackSlot()); + __ movsd(Address(CpuRegister(RSP), destination.GetStackIndex()), + source.As<XmmRegister>()); } - } else { - LOG(FATAL) << "Unimplemented"; } } @@ -1682,6 +1971,18 @@ void ParallelMoveResolverX86_64::Exchange64(int mem1, int mem2) { CpuRegister(ensure_scratch.GetRegister())); } +void ParallelMoveResolverX86_64::Exchange32(XmmRegister reg, int mem) { + __ movl(CpuRegister(TMP), Address(CpuRegister(RSP), mem)); + __ movss(Address(CpuRegister(RSP), mem), reg); + __ movd(reg, CpuRegister(TMP)); +} + +void ParallelMoveResolverX86_64::Exchange64(XmmRegister reg, int mem) { + __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), mem)); + __ movsd(Address(CpuRegister(RSP), mem), reg); + __ movd(reg, CpuRegister(TMP)); +} + void ParallelMoveResolverX86_64::EmitSwap(size_t index) { MoveOperands* move = moves_.Get(index); Location source = move->GetSource(); @@ -1701,8 +2002,20 @@ void ParallelMoveResolverX86_64::EmitSwap(size_t index) { Exchange64(destination.As<CpuRegister>(), source.GetStackIndex()); } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) { Exchange64(destination.GetStackIndex(), source.GetStackIndex()); + } else if (source.IsFpuRegister() && destination.IsFpuRegister()) { + __ movd(CpuRegister(TMP), source.As<XmmRegister>()); + __ movaps(source.As<XmmRegister>(), destination.As<XmmRegister>()); + __ movd(destination.As<XmmRegister>(), CpuRegister(TMP)); + } else if (source.IsFpuRegister() && destination.IsStackSlot()) { + Exchange32(source.As<XmmRegister>(), destination.GetStackIndex()); + } else if (source.IsStackSlot() && destination.IsFpuRegister()) { + Exchange32(destination.As<XmmRegister>(), source.GetStackIndex()); + } else if (source.IsFpuRegister() && destination.IsDoubleStackSlot()) { + Exchange64(source.As<XmmRegister>(), destination.GetStackIndex()); + } else if (source.IsDoubleStackSlot() && destination.IsFpuRegister()) { + Exchange64(destination.As<XmmRegister>(), source.GetStackIndex()); } else { - LOG(FATAL) << "Unimplemented"; + LOG(FATAL) << "Unimplemented swap between " << source << " and " << destination; } } diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 288f3f61f9..e04a8d8ab9 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -80,8 +80,10 @@ class ParallelMoveResolverX86_64 : public ParallelMoveResolver { private: void Exchange32(CpuRegister reg, int mem); + void Exchange32(XmmRegister reg, int mem); void Exchange32(int mem1, int mem2); void Exchange64(CpuRegister reg, int mem); + void Exchange64(XmmRegister reg, int mem); void Exchange64(int mem1, int mem2); CodeGeneratorX86_64* const codegen_; @@ -144,10 +146,12 @@ class CodeGeneratorX86_64 : public CodeGenerator { virtual void GenerateFrameEntry() OVERRIDE; virtual void GenerateFrameExit() OVERRIDE; - virtual void Bind(Label* label) OVERRIDE; + virtual void Bind(HBasicBlock* block) OVERRIDE; virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE; - virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE; - virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE; + virtual size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; + virtual size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; + virtual size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; + virtual size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE; virtual size_t GetWordSize() const OVERRIDE { return kX86_64WordSize; @@ -188,7 +192,17 @@ class CodeGeneratorX86_64 : public CodeGenerator { // Helper method to move a value between two locations. void Move(Location destination, Location source); + Label* GetLabelOf(HBasicBlock* block) const { + return block_labels_.GetRawStorage() + block->GetBlockId(); + } + + virtual void Initialize() OVERRIDE { + block_labels_.SetSize(GetGraph()->GetBlocks().Size()); + } + private: + // Labels for each block that will be compiled. + GrowableArray<Label> block_labels_; LocationsBuilderX86_64 location_builder_; InstructionCodeGeneratorX86_64 instruction_visitor_; ParallelMoveResolverX86_64 move_resolver_; diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc index 3037f1c2e8..03951e29dd 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -16,8 +16,10 @@ #include <functional> +#include "base/macros.h" #include "builder.h" #include "code_generator_arm.h" +#include "code_generator_arm64.h" #include "code_generator_x86.h" #include "code_generator_x86_64.h" #include "common_compiler_test.h" @@ -93,6 +95,12 @@ static void RunCodeBaseline(HGraph* graph, bool has_result, int32_t expected) { if (kRuntimeISA == kX86_64) { Run(allocator, codegenX86_64, has_result, expected); } + + arm64::CodeGeneratorARM64 codegenARM64(graph); + codegenARM64.CompileBaseline(&allocator, true); + if (kRuntimeISA == kArm64) { + Run(allocator, codegenARM64, has_result, expected); + } } static void RunCodeOptimized(CodeGenerator* codegen, @@ -134,9 +142,9 @@ static void TestCode(const uint16_t* data, bool has_result = false, int32_t expe HGraphBuilder builder(&arena); const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); HGraph* graph = builder.BuildGraph(*item); + ASSERT_NE(graph, nullptr); // Remove suspend checks, they cannot be executed in this context. RemoveSuspendChecks(graph); - ASSERT_NE(graph, nullptr); RunCodeBaseline(graph, has_result, expected); } @@ -260,6 +268,31 @@ TEST(CodegenTest, ReturnIf2) { TestCode(data, true, 0); } +// Exercise bit-wise (one's complement) not-int instruction. +#define NOT_INT_TEST(TEST_NAME, INPUT, EXPECTED_OUTPUT) \ +TEST(CodegenTest, TEST_NAME) { \ + const int32_t input = INPUT; \ + const uint16_t input_lo = input & 0x0000FFFF; \ + const uint16_t input_hi = input >> 16; \ + const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( \ + Instruction::CONST | 0 << 8, input_lo, input_hi, \ + Instruction::NOT_INT | 1 << 8 | 0 << 12 , \ + Instruction::RETURN | 1 << 8); \ + \ + TestCode(data, true, EXPECTED_OUTPUT); \ +} + +NOT_INT_TEST(ReturnNotIntMinus2, -2, 1) +NOT_INT_TEST(ReturnNotIntMinus1, -1, 0) +NOT_INT_TEST(ReturnNotInt0, 0, -1) +NOT_INT_TEST(ReturnNotInt1, 1, -2) +NOT_INT_TEST(ReturnNotIntINT_MIN, -2147483648, 2147483647) // (2^31) - 1 +NOT_INT_TEST(ReturnNotIntINT_MINPlus1, -2147483647, 2147483646) // (2^31) - 2 +NOT_INT_TEST(ReturnNotIntINT_MAXMinus1, 2147483646, -2147483647) // -(2^31) - 1 +NOT_INT_TEST(ReturnNotIntINT_MAX, 2147483647, -2147483648) // -(2^31) + +#undef NOT_INT_TEST + TEST(CodegenTest, ReturnAdd1) { const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 3 << 12 | 0, @@ -349,4 +382,173 @@ TEST(CodegenTest, NonMaterializedCondition) { RunCodeOptimized(graph, hook_before_codegen, true, 0); } +#define MUL_TEST(TYPE, TEST_NAME) \ + TEST(CodegenTest, Return ## TEST_NAME) { \ + const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( \ + Instruction::CONST_4 | 3 << 12 | 0, \ + Instruction::CONST_4 | 4 << 12 | 1 << 8, \ + Instruction::MUL_ ## TYPE, 1 << 8 | 0, \ + Instruction::RETURN); \ + \ + TestCode(data, true, 12); \ + } \ + \ + TEST(CodegenTest, Return ## TEST_NAME ## 2addr) { \ + const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( \ + Instruction::CONST_4 | 3 << 12 | 0, \ + Instruction::CONST_4 | 4 << 12 | 1 << 8, \ + Instruction::MUL_ ## TYPE ## _2ADDR | 1 << 12, \ + Instruction::RETURN); \ + \ + TestCode(data, true, 12); \ + } + +#if !defined(__aarch64__) +MUL_TEST(INT, MulInt); +MUL_TEST(LONG, MulLong); +#endif + +#if defined(__aarch64__) +TEST(CodegenTest, DISABLED_ReturnMulIntLit8) { +#else +TEST(CodegenTest, ReturnMulIntLit8) { +#endif + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 4 << 12 | 0 << 8, + Instruction::MUL_INT_LIT8, 3 << 8 | 0, + Instruction::RETURN); + + TestCode(data, true, 12); +} + +#if defined(__aarch64__) +TEST(CodegenTest, DISABLED_ReturnMulIntLit16) { +#else +TEST(CodegenTest, ReturnMulIntLit16) { +#endif + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 4 << 12 | 0 << 8, + Instruction::MUL_INT_LIT16, 3, + Instruction::RETURN); + + TestCode(data, true, 12); +} + +TEST(CodegenTest, MaterializedCondition1) { + // Check that condition are materialized correctly. A materialized condition + // should yield `1` if it evaluated to true, and `0` otherwise. + // We force the materialization of comparisons for different combinations of + // inputs and check the results. + + int lhs[] = {1, 2, -1, 2, 0xabc}; + int rhs[] = {2, 1, 2, -1, 0xabc}; + + for (size_t i = 0; i < arraysize(lhs); i++) { + ArenaPool pool; + ArenaAllocator allocator(&pool); + HGraph* graph = new (&allocator) HGraph(&allocator); + + HBasicBlock* entry_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(entry_block); + graph->SetEntryBlock(entry_block); + entry_block->AddInstruction(new (&allocator) HGoto()); + HBasicBlock* code_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(code_block); + HBasicBlock* exit_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(exit_block); + exit_block->AddInstruction(new (&allocator) HExit()); + + entry_block->AddSuccessor(code_block); + code_block->AddSuccessor(exit_block); + graph->SetExitBlock(exit_block); + + HIntConstant cst_lhs(lhs[i]); + code_block->AddInstruction(&cst_lhs); + HIntConstant cst_rhs(rhs[i]); + code_block->AddInstruction(&cst_rhs); + HLessThan cmp_lt(&cst_lhs, &cst_rhs); + code_block->AddInstruction(&cmp_lt); + HReturn ret(&cmp_lt); + code_block->AddInstruction(&ret); + + auto hook_before_codegen = [](HGraph* graph) { + HBasicBlock* block = graph->GetEntryBlock()->GetSuccessors().Get(0); + HParallelMove* move = new (graph->GetArena()) HParallelMove(graph->GetArena()); + block->InsertInstructionBefore(move, block->GetLastInstruction()); + }; + + RunCodeOptimized(graph, hook_before_codegen, true, lhs[i] < rhs[i]); + } +} + +TEST(CodegenTest, MaterializedCondition2) { + // Check that HIf correctly interprets a materialized condition. + // We force the materialization of comparisons for different combinations of + // inputs. An HIf takes the materialized combination as input and returns a + // value that we verify. + + int lhs[] = {1, 2, -1, 2, 0xabc}; + int rhs[] = {2, 1, 2, -1, 0xabc}; + + + for (size_t i = 0; i < arraysize(lhs); i++) { + ArenaPool pool; + ArenaAllocator allocator(&pool); + HGraph* graph = new (&allocator) HGraph(&allocator); + + HBasicBlock* entry_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(entry_block); + graph->SetEntryBlock(entry_block); + entry_block->AddInstruction(new (&allocator) HGoto()); + + HBasicBlock* if_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(if_block); + HBasicBlock* if_true_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(if_true_block); + HBasicBlock* if_false_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(if_false_block); + HBasicBlock* exit_block = new (&allocator) HBasicBlock(graph); + graph->AddBlock(exit_block); + exit_block->AddInstruction(new (&allocator) HExit()); + + graph->SetEntryBlock(entry_block); + entry_block->AddSuccessor(if_block); + if_block->AddSuccessor(if_true_block); + if_block->AddSuccessor(if_false_block); + if_true_block->AddSuccessor(exit_block); + if_false_block->AddSuccessor(exit_block); + graph->SetExitBlock(exit_block); + + HIntConstant cst_lhs(lhs[i]); + if_block->AddInstruction(&cst_lhs); + HIntConstant cst_rhs(rhs[i]); + if_block->AddInstruction(&cst_rhs); + HLessThan cmp_lt(&cst_lhs, &cst_rhs); + if_block->AddInstruction(&cmp_lt); + // We insert a temporary to separate the HIf from the HLessThan and force + // the materialization of the condition. + HTemporary force_materialization(0); + if_block->AddInstruction(&force_materialization); + HIf if_lt(&cmp_lt); + if_block->AddInstruction(&if_lt); + + HIntConstant cst_lt(1); + if_true_block->AddInstruction(&cst_lt); + HReturn ret_lt(&cst_lt); + if_true_block->AddInstruction(&ret_lt); + HIntConstant cst_ge(0); + if_false_block->AddInstruction(&cst_ge); + HReturn ret_ge(&cst_ge); + if_false_block->AddInstruction(&ret_ge); + + auto hook_before_codegen = [](HGraph* graph) { + HBasicBlock* block = graph->GetEntryBlock()->GetSuccessors().Get(0); + HParallelMove* move = new (graph->GetArena()) HParallelMove(graph->GetArena()); + block->InsertInstructionBefore(move, block->GetLastInstruction()); + }; + + RunCodeOptimized(graph, hook_before_codegen, true, lhs[i] < rhs[i]); + } +} + } // namespace art diff --git a/compiler/optimizing/constant_propagation.cc b/compiler/optimizing/constant_folding.cc index d675164fa4..10a7e46299 100644 --- a/compiler/optimizing/constant_propagation.cc +++ b/compiler/optimizing/constant_folding.cc @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "constant_propagation.h" +#include "constant_folding.h" namespace art { -void ConstantPropagation::Run() { +void HConstantFolding::Run() { // Process basic blocks in reverse post-order in the dominator tree, // so that an instruction turned into a constant, used as input of // another instruction, may possibly be used to turn that second @@ -31,11 +31,19 @@ void ConstantPropagation::Run() { for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* inst = it.Current(); - // Constant folding: replace `c <- a op b' with a compile-time - // evaluation of `a op b' if `a' and `b' are constant. if (inst->IsBinaryOperation()) { + // Constant folding: replace `op(a, b)' with a constant at + // compile time if `a' and `b' are both constants. HConstant* constant = - inst->AsBinaryOperation()->TryStaticEvaluation(graph_->GetArena()); + inst->AsBinaryOperation()->TryStaticEvaluation(); + if (constant != nullptr) { + inst->GetBlock()->ReplaceAndRemoveInstructionWith(inst, constant); + } + } else if (inst->IsUnaryOperation()) { + // Constant folding: replace `op(a)' with a constant at compile + // time if `a' is a constant. + HConstant* constant = + inst->AsUnaryOperation()->TryStaticEvaluation(); if (constant != nullptr) { inst->GetBlock()->ReplaceAndRemoveInstructionWith(inst, constant); } diff --git a/compiler/optimizing/constant_folding.h b/compiler/optimizing/constant_folding.h new file mode 100644 index 0000000000..d2acfa6973 --- /dev/null +++ b/compiler/optimizing/constant_folding.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_CONSTANT_FOLDING_H_ +#define ART_COMPILER_OPTIMIZING_CONSTANT_FOLDING_H_ + +#include "nodes.h" +#include "optimization.h" + +namespace art { + +/** + * Optimization pass performing a simple constant-expression + * evaluation on the SSA form. + * + * This class is named art::HConstantFolding to avoid name + * clashes with the art::ConstantPropagation class defined in + * compiler/dex/post_opt_passes.h. + */ +class HConstantFolding : public HOptimization { + public: + HConstantFolding(HGraph* graph, const HGraphVisualizer& visualizer) + : HOptimization(graph, true, kConstantFoldingPassName, visualizer) {} + + virtual void Run() OVERRIDE; + + static constexpr const char* kConstantFoldingPassName = "constant_folding"; + + private: + DISALLOW_COPY_AND_ASSIGN(HConstantFolding); +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_CONSTANT_FOLDING_H_ diff --git a/compiler/optimizing/constant_propagation_test.cc b/compiler/optimizing/constant_folding_test.cc index 342777a49c..09bf2c8d7d 100644 --- a/compiler/optimizing/constant_propagation_test.cc +++ b/compiler/optimizing/constant_folding_test.cc @@ -16,11 +16,12 @@ #include <functional> -#include "constant_propagation.h" +#include "code_generator_x86.h" +#include "constant_folding.h" #include "dead_code_elimination.h" -#include "pretty_printer.h" #include "graph_checker.h" #include "optimizing_unit_test.h" +#include "pretty_printer.h" #include "gtest/gtest.h" @@ -28,9 +29,9 @@ namespace art { static void TestCode(const uint16_t* data, const std::string& expected_before, - const std::string& expected_after_cp, + const std::string& expected_after_cf, const std::string& expected_after_dce, - std::function<void(HGraph*)> check_after_cp, + std::function<void(HGraph*)> check_after_cf, Primitive::Type return_type = Primitive::kPrimInt) { ArenaPool pool; ArenaAllocator allocator(&pool); @@ -45,29 +46,87 @@ static void TestCode(const uint16_t* data, std::string actual_before = printer_before.str(); ASSERT_EQ(expected_before, actual_before); - ConstantPropagation(graph).Run(); + x86::CodeGeneratorX86 codegen(graph); + HGraphVisualizer visualizer(nullptr, graph, codegen, ""); + HConstantFolding(graph, visualizer).Run(); + SSAChecker ssa_checker(&allocator, graph); + ssa_checker.Run(); + ASSERT_TRUE(ssa_checker.IsValid()); - StringPrettyPrinter printer_after_cp(graph); - printer_after_cp.VisitInsertionOrder(); - std::string actual_after_cp = printer_after_cp.str(); - ASSERT_EQ(expected_after_cp, actual_after_cp); + StringPrettyPrinter printer_after_cf(graph); + printer_after_cf.VisitInsertionOrder(); + std::string actual_after_cf = printer_after_cf.str(); + ASSERT_EQ(expected_after_cf, actual_after_cf); - check_after_cp(graph); + check_after_cf(graph); - DeadCodeElimination(graph).Run(); + HDeadCodeElimination(graph, visualizer).Run(); + ssa_checker.Run(); + ASSERT_TRUE(ssa_checker.IsValid()); StringPrettyPrinter printer_after_dce(graph); printer_after_dce.VisitInsertionOrder(); std::string actual_after_dce = printer_after_dce.str(); ASSERT_EQ(expected_after_dce, actual_after_dce); - - SSAChecker ssa_checker(&allocator, graph); - ssa_checker.VisitInsertionOrder(); - ASSERT_TRUE(ssa_checker.IsValid()); } /** + * Tiny three-register program exercising int constant folding on negation. + * + * 16-bit + * offset + * ------ + * v0 <- 1 0. const/4 v0, #+1 + * v1 <- -v0 1. neg-int v0, v1 + * return v1 2. return v1 + */ +TEST(ConstantFolding, IntConstantFoldingNegation) { + const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + Instruction::CONST_4 | 0 << 8 | 1 << 12, + Instruction::NEG_INT | 1 << 8 | 0 << 12, + Instruction::RETURN | 1 << 8); + + std::string expected_before = + "BasicBlock 0, succ: 1\n" + " 2: IntConstant [5]\n" + " 10: SuspendCheck\n" + " 11: Goto 1\n" + "BasicBlock 1, pred: 0, succ: 2\n" + " 5: Neg(2) [8]\n" + " 8: Return(5)\n" + "BasicBlock 2, pred: 1\n" + " 9: Exit\n"; + + // Expected difference after constant folding. + diff_t expected_cf_diff = { + { " 2: IntConstant [5]\n", " 2: IntConstant\n" }, + { " 5: Neg(2) [8]\n", " 12: IntConstant [8]\n" }, + { " 8: Return(5)\n", " 8: Return(12)\n" } + }; + std::string expected_after_cf = Patch(expected_before, expected_cf_diff); + + // Check the value of the computed constant. + auto check_after_cf = [](HGraph* graph) { + HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); + ASSERT_TRUE(inst->IsIntConstant()); + ASSERT_EQ(inst->AsIntConstant()->GetValue(), -1); + }; + + // Expected difference after dead code elimination. + diff_t expected_dce_diff = { + { " 2: IntConstant\n", removed }, + }; + std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); + + TestCode(data, + expected_before, + expected_after_cf, + expected_after_dce, + check_after_cf); +} + +/** * Tiny three-register program exercising int constant folding on addition. * * 16-bit @@ -78,7 +137,7 @@ static void TestCode(const uint16_t* data, * v2 <- v0 + v1 2. add-int v2, v0, v1 * return v2 4. return v2 */ -TEST(ConstantPropagation, IntConstantFoldingOnAddition1) { +TEST(ConstantFolding, IntConstantFoldingOnAddition1) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 1 << 12, Instruction::CONST_4 | 1 << 8 | 2 << 12, @@ -97,17 +156,17 @@ TEST(ConstantPropagation, IntConstantFoldingOnAddition1) { "BasicBlock 2, pred: 1\n" " 13: Exit\n"; - // Expected difference after constant propagation. - diff_t expected_cp_diff = { + // Expected difference after constant folding. + diff_t expected_cf_diff = { { " 3: IntConstant [9]\n", " 3: IntConstant\n" }, { " 5: IntConstant [9]\n", " 5: IntConstant\n" }, { " 9: Add(3, 5) [12]\n", " 16: IntConstant [12]\n" }, { " 12: Return(9)\n", " 12: Return(16)\n" } }; - std::string expected_after_cp = Patch(expected_before, expected_cp_diff); + std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the value of the computed constant. - auto check_after_cp = [](HGraph* graph) { + auto check_after_cf = [](HGraph* graph) { HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); ASSERT_TRUE(inst->IsIntConstant()); ASSERT_EQ(inst->AsIntConstant()->GetValue(), 3); @@ -118,13 +177,13 @@ TEST(ConstantPropagation, IntConstantFoldingOnAddition1) { { " 3: IntConstant\n", removed }, { " 5: IntConstant\n", removed } }; - std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff); + std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); TestCode(data, expected_before, - expected_after_cp, + expected_after_cf, expected_after_dce, - check_after_cp); + check_after_cf); } /** @@ -142,7 +201,7 @@ TEST(ConstantPropagation, IntConstantFoldingOnAddition1) { * v2 <- v0 + v1 6. add-int v2, v0, v1 * return v2 8. return v2 */ -TEST(ConstantPropagation, IntConstantFoldingOnAddition2) { +TEST(ConstantFolding, IntConstantFoldingOnAddition2) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 1 << 12, Instruction::CONST_4 | 1 << 8 | 2 << 12, @@ -169,8 +228,8 @@ TEST(ConstantPropagation, IntConstantFoldingOnAddition2) { "BasicBlock 2, pred: 1\n" " 25: Exit\n"; - // Expected difference after constant propagation. - diff_t expected_cp_diff = { + // Expected difference after constant folding. + diff_t expected_cf_diff = { { " 3: IntConstant [9]\n", " 3: IntConstant\n" }, { " 5: IntConstant [9]\n", " 5: IntConstant\n" }, { " 11: IntConstant [17]\n", " 11: IntConstant\n" }, @@ -180,10 +239,10 @@ TEST(ConstantPropagation, IntConstantFoldingOnAddition2) { { " 21: Add(9, 17) [24]\n", " 30: IntConstant [24]\n" }, { " 24: Return(21)\n", " 24: Return(30)\n" } }; - std::string expected_after_cp = Patch(expected_before, expected_cp_diff); + std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the values of the computed constants. - auto check_after_cp = [](HGraph* graph) { + auto check_after_cf = [](HGraph* graph) { HInstruction* inst1 = graph->GetBlock(1)->GetFirstInstruction(); ASSERT_TRUE(inst1->IsIntConstant()); ASSERT_EQ(inst1->AsIntConstant()->GetValue(), 3); @@ -204,13 +263,13 @@ TEST(ConstantPropagation, IntConstantFoldingOnAddition2) { { " 28: IntConstant\n", removed }, { " 29: IntConstant\n", removed } }; - std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff); + std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); TestCode(data, expected_before, - expected_after_cp, + expected_after_cf, expected_after_dce, - check_after_cp); + check_after_cf); } /** @@ -224,7 +283,7 @@ TEST(ConstantPropagation, IntConstantFoldingOnAddition2) { * v2 <- v0 - v1 2. sub-int v2, v0, v1 * return v2 4. return v2 */ -TEST(ConstantPropagation, IntConstantFoldingOnSubtraction) { +TEST(ConstantFolding, IntConstantFoldingOnSubtraction) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 3 << 12, Instruction::CONST_4 | 1 << 8 | 2 << 12, @@ -243,17 +302,17 @@ TEST(ConstantPropagation, IntConstantFoldingOnSubtraction) { "BasicBlock 2, pred: 1\n" " 13: Exit\n"; - // Expected difference after constant propagation. - diff_t expected_cp_diff = { + // Expected difference after constant folding. + diff_t expected_cf_diff = { { " 3: IntConstant [9]\n", " 3: IntConstant\n" }, { " 5: IntConstant [9]\n", " 5: IntConstant\n" }, { " 9: Sub(3, 5) [12]\n", " 16: IntConstant [12]\n" }, { " 12: Return(9)\n", " 12: Return(16)\n" } }; - std::string expected_after_cp = Patch(expected_before, expected_cp_diff); + std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the value of the computed constant. - auto check_after_cp = [](HGraph* graph) { + auto check_after_cf = [](HGraph* graph) { HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); ASSERT_TRUE(inst->IsIntConstant()); ASSERT_EQ(inst->AsIntConstant()->GetValue(), 1); @@ -264,13 +323,13 @@ TEST(ConstantPropagation, IntConstantFoldingOnSubtraction) { { " 3: IntConstant\n", removed }, { " 5: IntConstant\n", removed } }; - std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff); + std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); TestCode(data, expected_before, - expected_after_cp, + expected_after_cf, expected_after_dce, - check_after_cp); + check_after_cf); } #define SIX_REGISTERS_CODE_ITEM(...) \ @@ -289,7 +348,7 @@ TEST(ConstantPropagation, IntConstantFoldingOnSubtraction) { * (v0, v1) + (v1, v2) 4. add-long v4, v0, v2 * return (v4, v5) 6. return-wide v4 */ -TEST(ConstantPropagation, LongConstantFoldingOnAddition) { +TEST(ConstantFolding, LongConstantFoldingOnAddition) { const uint16_t data[] = SIX_REGISTERS_CODE_ITEM( Instruction::CONST_WIDE_16 | 0 << 8, 1, Instruction::CONST_WIDE_16 | 2 << 8, 2, @@ -308,17 +367,17 @@ TEST(ConstantPropagation, LongConstantFoldingOnAddition) { "BasicBlock 2, pred: 1\n" " 16: Exit\n"; - // Expected difference after constant propagation. - diff_t expected_cp_diff = { + // Expected difference after constant folding. + diff_t expected_cf_diff = { { " 6: LongConstant [12]\n", " 6: LongConstant\n" }, { " 8: LongConstant [12]\n", " 8: LongConstant\n" }, { " 12: Add(6, 8) [15]\n", " 19: LongConstant [15]\n" }, { " 15: Return(12)\n", " 15: Return(19)\n" } }; - std::string expected_after_cp = Patch(expected_before, expected_cp_diff); + std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the value of the computed constant. - auto check_after_cp = [](HGraph* graph) { + auto check_after_cf = [](HGraph* graph) { HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); ASSERT_TRUE(inst->IsLongConstant()); ASSERT_EQ(inst->AsLongConstant()->GetValue(), 3); @@ -329,13 +388,13 @@ TEST(ConstantPropagation, LongConstantFoldingOnAddition) { { " 6: LongConstant\n", removed }, { " 8: LongConstant\n", removed } }; - std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff); + std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); TestCode(data, expected_before, - expected_after_cp, + expected_after_cf, expected_after_dce, - check_after_cp, + check_after_cf, Primitive::kPrimLong); } @@ -352,7 +411,7 @@ TEST(ConstantPropagation, LongConstantFoldingOnAddition) { * (v0, v1) - (v1, v2) 4. sub-long v4, v0, v2 * return (v4, v5) 6. return-wide v4 */ -TEST(ConstantPropagation, LongConstantFoldingOnSubtraction) { +TEST(ConstantFolding, LongConstantFoldingOnSubtraction) { const uint16_t data[] = SIX_REGISTERS_CODE_ITEM( Instruction::CONST_WIDE_16 | 0 << 8, 3, Instruction::CONST_WIDE_16 | 2 << 8, 2, @@ -371,17 +430,17 @@ TEST(ConstantPropagation, LongConstantFoldingOnSubtraction) { "BasicBlock 2, pred: 1\n" " 16: Exit\n"; - // Expected difference after constant propagation. - diff_t expected_cp_diff = { + // Expected difference after constant folding. + diff_t expected_cf_diff = { { " 6: LongConstant [12]\n", " 6: LongConstant\n" }, { " 8: LongConstant [12]\n", " 8: LongConstant\n" }, { " 12: Sub(6, 8) [15]\n", " 19: LongConstant [15]\n" }, { " 15: Return(12)\n", " 15: Return(19)\n" } }; - std::string expected_after_cp = Patch(expected_before, expected_cp_diff); + std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the value of the computed constant. - auto check_after_cp = [](HGraph* graph) { + auto check_after_cf = [](HGraph* graph) { HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); ASSERT_TRUE(inst->IsLongConstant()); ASSERT_EQ(inst->AsLongConstant()->GetValue(), 1); @@ -392,13 +451,13 @@ TEST(ConstantPropagation, LongConstantFoldingOnSubtraction) { { " 6: LongConstant\n", removed }, { " 8: LongConstant\n", removed } }; - std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff); + std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); TestCode(data, expected_before, - expected_after_cp, + expected_after_cf, expected_after_dce, - check_after_cp, + check_after_cf, Primitive::kPrimLong); } @@ -424,7 +483,7 @@ TEST(ConstantPropagation, LongConstantFoldingOnSubtraction) { * L3: v2 <- v1 + 4 11. add-int/lit16 v2, v1, #+4 * return v2 13. return v2 */ -TEST(ConstantPropagation, IntConstantFoldingAndJumps) { +TEST(ConstantFolding, IntConstantFoldingAndJumps) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 0 << 12, Instruction::CONST_4 | 1 << 8 | 1 << 12, @@ -462,8 +521,8 @@ TEST(ConstantPropagation, IntConstantFoldingAndJumps) { "BasicBlock 5, pred: 4\n" " 29: Exit\n"; - // Expected difference after constant propagation. - diff_t expected_cp_diff = { + // Expected difference after constant folding. + diff_t expected_cf_diff = { { " 3: IntConstant [9]\n", " 3: IntConstant\n" }, { " 5: IntConstant [9]\n", " 5: IntConstant []\n" }, { " 13: IntConstant [14]\n", " 13: IntConstant\n" }, @@ -475,10 +534,10 @@ TEST(ConstantPropagation, IntConstantFoldingAndJumps) { { " 25: Add(14, 24) [28]\n", " 35: IntConstant [28]\n" }, { " 28: Return(25)\n", " 28: Return(35)\n"} }; - std::string expected_after_cp = Patch(expected_before, expected_cp_diff); + std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the values of the computed constants. - auto check_after_cp = [](HGraph* graph) { + auto check_after_cf = [](HGraph* graph) { HInstruction* inst1 = graph->GetBlock(1)->GetFirstInstruction(); ASSERT_TRUE(inst1->IsIntConstant()); ASSERT_EQ(inst1->AsIntConstant()->GetValue(), 1); @@ -501,13 +560,13 @@ TEST(ConstantPropagation, IntConstantFoldingAndJumps) { { " 24: IntConstant\n", removed }, { " 34: IntConstant\n", removed }, }; - std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff); + std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); TestCode(data, expected_before, - expected_after_cp, + expected_after_cf, expected_after_dce, - check_after_cp); + check_after_cf); } @@ -524,7 +583,7 @@ TEST(ConstantPropagation, IntConstantFoldingAndJumps) { * L1: v2 <- v0 + v1 5. add-int v2, v0, v1 * return-void 7. return */ -TEST(ConstantPropagation, ConstantCondition) { +TEST(ConstantFolding, ConstantCondition) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 1 << 8 | 1 << 12, Instruction::CONST_4 | 0 << 8 | 0 << 12, @@ -553,17 +612,17 @@ TEST(ConstantPropagation, ConstantCondition) { "BasicBlock 5, pred: 1, succ: 3\n" " 21: Goto 3\n"; - // Expected difference after constant propagation. - diff_t expected_cp_diff = { + // Expected difference after constant folding. + diff_t expected_cf_diff = { { " 3: IntConstant [15, 22, 8]\n", " 3: IntConstant [15, 22]\n" }, { " 5: IntConstant [22, 8]\n", " 5: IntConstant [22]\n" }, { " 8: GreaterThanOrEqual(3, 5) [9]\n", " 23: IntConstant [9]\n" }, { " 9: If(8)\n", " 9: If(23)\n" } }; - std::string expected_after_cp = Patch(expected_before, expected_cp_diff); + std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the values of the computed constants. - auto check_after_cp = [](HGraph* graph) { + auto check_after_cf = [](HGraph* graph) { HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); ASSERT_TRUE(inst->IsIntConstant()); ASSERT_EQ(inst->AsIntConstant()->GetValue(), 1); @@ -575,13 +634,13 @@ TEST(ConstantPropagation, ConstantCondition) { { " 22: Phi(3, 5) [15]\n", " 22: Phi(3, 5)\n" }, { " 15: Add(22, 3)\n", removed } }; - std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff); + std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); TestCode(data, expected_before, - expected_after_cp, + expected_after_cf, expected_after_dce, - check_after_cp); + check_after_cf); } } // namespace art diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc index fe2adc77d0..fc3dd01ef5 100644 --- a/compiler/optimizing/dead_code_elimination.cc +++ b/compiler/optimizing/dead_code_elimination.cc @@ -20,7 +20,7 @@ namespace art { -void DeadCodeElimination::Run() { +void HDeadCodeElimination::Run() { // Process basic blocks in post-order in the dominator tree, so that // a dead instruction depending on another dead instruction is // removed. @@ -35,7 +35,10 @@ void DeadCodeElimination::Run() { for (i.Advance(); !i.Done(); i.Advance()) { HInstruction* inst = i.Current(); DCHECK(!inst->IsControlFlow()); - if (!inst->HasSideEffects() && !inst->HasUses() && !inst->IsSuspendCheck()) { + if (!inst->HasSideEffects() + && !inst->CanThrow() + && !inst->IsSuspendCheck() + && !inst->HasUses()) { block->RemoveInstruction(inst); } } diff --git a/compiler/optimizing/dead_code_elimination.h b/compiler/optimizing/dead_code_elimination.h index 48739be494..a4446ae04d 100644 --- a/compiler/optimizing/dead_code_elimination.h +++ b/compiler/optimizing/dead_code_elimination.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_DEAD_CODE_ELIMINATION_H_ #include "nodes.h" +#include "optimization.h" namespace art { @@ -25,17 +26,18 @@ namespace art { * Optimization pass performing dead code elimination (removal of * unused variables/instructions) on the SSA form. */ -class DeadCodeElimination : public ValueObject { +class HDeadCodeElimination : public HOptimization { public: - explicit DeadCodeElimination(HGraph* graph) - : graph_(graph) {} + HDeadCodeElimination(HGraph* graph, const HGraphVisualizer& visualizer) + : HOptimization(graph, true, kDeadCodeEliminationPassName, visualizer) {} - void Run(); + virtual void Run() OVERRIDE; - private: - HGraph* const graph_; + static constexpr const char* kDeadCodeEliminationPassName = + "dead_code_elimination"; - DISALLOW_COPY_AND_ASSIGN(DeadCodeElimination); + private: + DISALLOW_COPY_AND_ASSIGN(HDeadCodeElimination); }; } // namespace art diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc index 245bcb21d5..0c6807482a 100644 --- a/compiler/optimizing/dead_code_elimination_test.cc +++ b/compiler/optimizing/dead_code_elimination_test.cc @@ -14,10 +14,11 @@ * limitations under the License. */ +#include "code_generator_x86.h" #include "dead_code_elimination.h" -#include "pretty_printer.h" #include "graph_checker.h" #include "optimizing_unit_test.h" +#include "pretty_printer.h" #include "gtest/gtest.h" @@ -39,16 +40,17 @@ static void TestCode(const uint16_t* data, std::string actual_before = printer_before.str(); ASSERT_EQ(actual_before, expected_before); - DeadCodeElimination(graph).Run(); + x86::CodeGeneratorX86 codegen(graph); + HGraphVisualizer visualizer(nullptr, graph, codegen, ""); + HDeadCodeElimination(graph, visualizer).Run(); + SSAChecker ssa_checker(&allocator, graph); + ssa_checker.Run(); + ASSERT_TRUE(ssa_checker.IsValid()); StringPrettyPrinter printer_after(graph); printer_after.VisitInsertionOrder(); std::string actual_after = printer_after.str(); ASSERT_EQ(actual_after, expected_after); - - SSAChecker ssa_checker(&allocator, graph); - ssa_checker.VisitInsertionOrder(); - ASSERT_TRUE(ssa_checker.IsValid()); } @@ -94,6 +96,7 @@ TEST(DeadCodeElimination, AdditionAndConditionalJump) { "BasicBlock 5, pred: 1, succ: 3\n" " 21: Goto 3\n"; + // Expected difference after dead code elimination. diff_t expected_diff = { { " 3: IntConstant [15, 22, 8]\n", " 3: IntConstant [22, 8]\n" }, { " 22: Phi(3, 5) [15]\n", " 22: Phi(3, 5)\n" }, @@ -164,7 +167,7 @@ TEST(DeadCodeElimination, AdditionsAndInconditionalJumps) { "BasicBlock 5, pred: 4\n" " 28: Exit\n"; - // Expected difference after constant propagation. + // Expected difference after dead code elimination. diff_t expected_diff = { { " 13: IntConstant [14]\n", removed }, { " 24: IntConstant [25]\n", removed }, diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 589b44a167..743ffc46bf 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -264,21 +264,38 @@ void SSAChecker::CheckLoop(HBasicBlock* loop_header) { void SSAChecker::VisitInstruction(HInstruction* instruction) { super_type::VisitInstruction(instruction); - // Ensure an instruction dominates all its uses (or in the present - // case, that all uses of an instruction (used as input) are - // dominated by its definition). - for (HInputIterator input_it(instruction); !input_it.Done(); - input_it.Advance()) { - HInstruction* input = input_it.Current(); - if (!input->Dominates(instruction)) { + // Ensure an instruction dominates all its uses. + for (HUseIterator<HInstruction> use_it(instruction->GetUses()); + !use_it.Done(); use_it.Advance()) { + HInstruction* use = use_it.Current()->GetUser(); + if (!use->IsPhi() && !instruction->StrictlyDominates(use)) { std::stringstream error; - error << "Instruction " << input->GetId() - << " in block " << input->GetBlock()->GetBlockId() - << " does not dominate use " << instruction->GetId() - << " in block " << current_block_->GetBlockId() << "."; + error << "Instruction " << instruction->GetId() + << " in block " << current_block_->GetBlockId() + << " does not dominate use " << use->GetId() + << " in block " << use->GetBlock()->GetBlockId() << "."; errors_.Insert(error.str()); } } + + // Ensure an instruction having an environment is dominated by the + // instructions contained in the environment. + HEnvironment* environment = instruction->GetEnvironment(); + if (environment != nullptr) { + for (size_t i = 0, e = environment->Size(); i < e; ++i) { + HInstruction* env_instruction = environment->GetInstructionAt(i); + if (env_instruction != nullptr + && !env_instruction->StrictlyDominates(instruction)) { + std::stringstream error; + error << "Instruction " << env_instruction->GetId() + << " in environment of instruction " << instruction->GetId() + << " from block " << current_block_->GetBlockId() + << " does not dominate instruction " << instruction->GetId() + << "."; + errors_.Insert(error.str()); + } + } + } } void SSAChecker::VisitPhi(HPhi* phi) { diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h index 34a770b5f3..badf21d946 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -19,15 +19,22 @@ #include "nodes.h" +#include <ostream> + namespace art { // A control-flow graph visitor performing various checks. class GraphChecker : public HGraphVisitor { public: - GraphChecker(ArenaAllocator* allocator, HGraph* graph) + GraphChecker(ArenaAllocator* allocator, HGraph* graph, + const char* dump_prefix = "art::GraphChecker: ") : HGraphVisitor(graph), allocator_(allocator), - errors_(allocator, 0) {} + errors_(allocator, 0), + dump_prefix_(dump_prefix) {} + + // Check the whole graph (in insertion order). + virtual void Run() { VisitInsertionOrder(); } // Check `block`. virtual void VisitBasicBlock(HBasicBlock* block) OVERRIDE; @@ -45,6 +52,13 @@ class GraphChecker : public HGraphVisitor { return errors_; } + // Print detected errors on output stream `os`. + void Dump(std::ostream& os) const { + for (size_t i = 0, e = errors_.Size(); i < e; ++i) { + os << dump_prefix_ << errors_.Get(i) << std::endl; + } + } + protected: ArenaAllocator* const allocator_; // The block currently visited. @@ -53,6 +67,9 @@ class GraphChecker : public HGraphVisitor { GrowableArray<std::string> errors_; private: + // String displayed before dumped errors. + const char* const dump_prefix_; + DISALLOW_COPY_AND_ASSIGN(GraphChecker); }; @@ -63,7 +80,15 @@ class SSAChecker : public GraphChecker { typedef GraphChecker super_type; SSAChecker(ArenaAllocator* allocator, HGraph* graph) - : GraphChecker(allocator, graph) {} + : GraphChecker(allocator, graph, "art::SSAChecker: ") {} + + // Check the whole graph (in reverse post-order). + virtual void Run() { + // VisitReversePostOrder is used instead of VisitInsertionOrder, + // as the latter might visit dead blocks removed by the dominator + // computation. + VisitReversePostOrder(); + } // Perform SSA form checks on `block`. virtual void VisitBasicBlock(HBasicBlock* block) OVERRIDE; diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc index ea0692088d..39def82007 100644 --- a/compiler/optimizing/graph_checker_test.cc +++ b/compiler/optimizing/graph_checker_test.cc @@ -51,7 +51,7 @@ static void TestCode(const uint16_t* data) { ASSERT_NE(graph, nullptr); GraphChecker graph_checker(&allocator, graph); - graph_checker.VisitInsertionOrder(); + graph_checker.Run(); ASSERT_TRUE(graph_checker.IsValid()); } @@ -65,7 +65,7 @@ static void TestCodeSSA(const uint16_t* data) { graph->TransformToSSA(); SSAChecker ssa_checker(&allocator, graph); - ssa_checker.VisitInsertionOrder(); + ssa_checker.Run(); ASSERT_TRUE(ssa_checker.IsValid()); } @@ -113,13 +113,13 @@ TEST(GraphChecker, InconsistentPredecessorsAndSuccessors) { HGraph* graph = CreateSimpleCFG(&allocator); GraphChecker graph_checker(&allocator, graph); - graph_checker.VisitInsertionOrder(); + graph_checker.Run(); ASSERT_TRUE(graph_checker.IsValid()); // Remove the entry block from the exit block's predecessors, to create an // inconsistent successor/predecessor relation. graph->GetExitBlock()->RemovePredecessor(graph->GetEntryBlock()); - graph_checker.VisitInsertionOrder(); + graph_checker.Run(); ASSERT_FALSE(graph_checker.IsValid()); } @@ -131,7 +131,7 @@ TEST(GraphChecker, BlockEndingWithNonBranchInstruction) { HGraph* graph = CreateSimpleCFG(&allocator); GraphChecker graph_checker(&allocator, graph); - graph_checker.VisitInsertionOrder(); + graph_checker.Run(); ASSERT_TRUE(graph_checker.IsValid()); // Remove the sole instruction of the exit block (composed of a @@ -141,7 +141,7 @@ TEST(GraphChecker, BlockEndingWithNonBranchInstruction) { HInstruction* last_inst = exit_block->GetLastInstruction(); exit_block->RemoveInstruction(last_inst); - graph_checker.VisitInsertionOrder(); + graph_checker.Run(); ASSERT_FALSE(graph_checker.IsValid()); } diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index b4eb89d30b..4ed2156241 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -120,13 +120,11 @@ class HGraphVisualizerPrinter : public HGraphVisitor { output_<< std::endl; } - void DumpLocation(Location location, Primitive::Type type) { + void DumpLocation(Location location) { if (location.IsRegister()) { - if (type == Primitive::kPrimDouble || type == Primitive::kPrimFloat) { - codegen_.DumpFloatingPointRegister(output_, location.reg()); - } else { - codegen_.DumpCoreRegister(output_, location.reg()); - } + codegen_.DumpCoreRegister(output_, location.reg()); + } else if (location.IsFpuRegister()) { + codegen_.DumpFloatingPointRegister(output_, location.reg()); } else if (location.IsConstant()) { output_ << "constant"; HConstant* constant = location.GetConstant(); @@ -150,9 +148,9 @@ class HGraphVisualizerPrinter : public HGraphVisitor { output_ << " ("; for (size_t i = 0, e = instruction->NumMoves(); i < e; ++i) { MoveOperands* move = instruction->MoveOperandsAt(i); - DumpLocation(move->GetSource(), Primitive::kPrimInt); + DumpLocation(move->GetSource()); output_ << " -> "; - DumpLocation(move->GetDestination(), Primitive::kPrimInt); + DumpLocation(move->GetDestination()); if (i + 1 != e) { output_ << ", "; } @@ -183,13 +181,13 @@ class HGraphVisualizerPrinter : public HGraphVisitor { if (locations != nullptr) { output_ << " ( "; for (size_t i = 0; i < instruction->InputCount(); ++i) { - DumpLocation(locations->InAt(i), instruction->InputAt(i)->GetType()); + DumpLocation(locations->InAt(i)); output_ << " "; } output_ << ")"; if (locations->Out().IsValid()) { output_ << " -> "; - DumpLocation(locations->Out(), instruction->GetType()); + DumpLocation(locations->Out()); } } output_ << " (liveness: " << instruction->GetLifetimePosition() << ")"; @@ -309,7 +307,7 @@ HGraphVisualizer::HGraphVisualizer(std::ostream* output, printer.EndTag("compilation"); } -void HGraphVisualizer::DumpGraph(const char* pass_name) { +void HGraphVisualizer::DumpGraph(const char* pass_name) const { if (!is_enabled_) { return; } diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h index f17ba3bbac..4d8bec2422 100644 --- a/compiler/optimizing/graph_visualizer.h +++ b/compiler/optimizing/graph_visualizer.h @@ -17,6 +17,8 @@ #ifndef ART_COMPILER_OPTIMIZING_GRAPH_VISUALIZER_H_ #define ART_COMPILER_OPTIMIZING_GRAPH_VISUALIZER_H_ +#include <ostream> + #include "base/value_object.h" namespace art { @@ -61,7 +63,7 @@ class HGraphVisualizer : public ValueObject { * If this visualizer is enabled, emit the compilation information * in `output_`. */ - void DumpGraph(const char* pass_name); + void DumpGraph(const char* pass_name) const; private: std::ostream* const output_; diff --git a/compiler/optimizing/gvn.h b/compiler/optimizing/gvn.h index 41b3ceb509..a98d714476 100644 --- a/compiler/optimizing/gvn.h +++ b/compiler/optimizing/gvn.h @@ -17,7 +17,6 @@ #ifndef ART_COMPILER_OPTIMIZING_GVN_H_ #define ART_COMPILER_OPTIMIZING_GVN_H_ -#include <gtest/gtest.h> #include "nodes.h" namespace art { @@ -221,7 +220,7 @@ class GlobalValueNumberer : public ValueObject { // Mark visisted blocks. Only used for debugging. GrowableArray<bool> visited_; - FRIEND_TEST(GVNTest, LoopSideEffects); + ART_FRIEND_TEST(GVNTest, LoopSideEffects); DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer); }; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 2d9e35c3b6..29eabe7e29 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -50,7 +50,7 @@ void InstructionSimplifier::VisitEqual(HEqual* equal) { // Replace (bool_value == 0) with !bool_value DCHECK_EQ(input2->AsIntConstant()->GetValue(), 0); equal->GetBlock()->ReplaceAndRemoveInstructionWith( - equal, new (GetGraph()->GetArena()) HNot(input1)); + equal, new (GetGraph()->GetArena()) HNot(Primitive::kPrimBoolean, input1)); } } } diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc index eafc3e1483..89c949563b 100644 --- a/compiler/optimizing/live_ranges_test.cc +++ b/compiler/optimizing/live_ranges_test.cc @@ -73,9 +73,9 @@ TEST(LiveRangesTest, CFG1) { LiveRange* range = interval->GetFirstRange(); ASSERT_EQ(2u, range->GetStart()); // Last use is the return instruction. - ASSERT_EQ(9u, range->GetEnd()); + ASSERT_EQ(8u, range->GetEnd()); HBasicBlock* block = graph->GetBlocks().Get(1); - ASSERT_TRUE(block->GetLastInstruction()->AsReturn() != nullptr); + ASSERT_TRUE(block->GetLastInstruction()->IsReturn()); ASSERT_EQ(8u, block->GetLastInstruction()->GetLifetimePosition()); ASSERT_TRUE(range->GetNext() == nullptr); } @@ -119,9 +119,9 @@ TEST(LiveRangesTest, CFG2) { LiveRange* range = interval->GetFirstRange(); ASSERT_EQ(2u, range->GetStart()); // Last use is the return instruction. - ASSERT_EQ(23u, range->GetEnd()); + ASSERT_EQ(22u, range->GetEnd()); HBasicBlock* block = graph->GetBlocks().Get(3); - ASSERT_TRUE(block->GetLastInstruction()->AsReturn() != nullptr); + ASSERT_TRUE(block->GetLastInstruction()->IsReturn()); ASSERT_EQ(22u, block->GetLastInstruction()->GetLifetimePosition()); ASSERT_TRUE(range->GetNext() == nullptr); } @@ -193,7 +193,7 @@ TEST(LiveRangesTest, CFG3) { range = interval->GetFirstRange(); ASSERT_EQ(22u, liveness.GetInstructionFromSsaIndex(2)->GetLifetimePosition()); ASSERT_EQ(22u, range->GetStart()); - ASSERT_EQ(25u, range->GetEnd()); + ASSERT_EQ(24u, range->GetEnd()); ASSERT_TRUE(range->GetNext() == nullptr); } @@ -263,7 +263,7 @@ TEST(LiveRangesTest, Loop1) { range = interval->GetFirstRange(); // The instruction is live until the return instruction after the loop. ASSERT_EQ(6u, range->GetStart()); - ASSERT_EQ(27u, range->GetEnd()); + ASSERT_EQ(26u, range->GetEnd()); ASSERT_TRUE(range->GetNext() == nullptr); // Test for the phi. @@ -271,7 +271,7 @@ TEST(LiveRangesTest, Loop1) { range = interval->GetFirstRange(); // Instruction is consumed by the if. ASSERT_EQ(14u, range->GetStart()); - ASSERT_EQ(16u, range->GetEnd()); + ASSERT_EQ(17u, range->GetEnd()); ASSERT_TRUE(range->GetNext() == nullptr); } @@ -338,7 +338,7 @@ TEST(LiveRangesTest, Loop2) { range = range->GetNext(); ASSERT_TRUE(range != nullptr); ASSERT_EQ(24u, range->GetStart()); - ASSERT_EQ(27u, range->GetEnd()); + ASSERT_EQ(26u, range->GetEnd()); // Test for the add instruction. HAdd* add = liveness.GetInstructionFromSsaIndex(2)->AsAdd(); @@ -410,7 +410,7 @@ TEST(LiveRangesTest, CFG4) { interval = liveness.GetInstructionFromSsaIndex(1)->GetLiveInterval(); range = interval->GetFirstRange(); ASSERT_EQ(4u, range->GetStart()); - ASSERT_EQ(29u, range->GetEnd()); + ASSERT_EQ(28u, range->GetEnd()); ASSERT_TRUE(range->GetNext() == nullptr); // Test for the first add. diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc index 1637484799..ed5e260a5b 100644 --- a/compiler/optimizing/locations.cc +++ b/compiler/optimizing/locations.cc @@ -25,16 +25,14 @@ LocationSummary::LocationSummary(HInstruction* instruction, CallKind call_kind) temps_(instruction->GetBlock()->GetGraph()->GetArena(), 0), environment_(instruction->GetBlock()->GetGraph()->GetArena(), instruction->EnvironmentSize()), - dies_at_entry_(instruction->GetBlock()->GetGraph()->GetArena(), instruction->InputCount()), + output_overlaps_(true), call_kind_(call_kind), stack_mask_(nullptr), register_mask_(0), live_registers_() { inputs_.SetSize(instruction->InputCount()); - dies_at_entry_.SetSize(instruction->InputCount()); for (size_t i = 0; i < instruction->InputCount(); ++i) { inputs_.Put(i, Location()); - dies_at_entry_.Put(i, false); } environment_.SetSize(instruction->EnvironmentSize()); for (size_t i = 0; i < instruction->EnvironmentSize(); ++i) { diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h index dcf70f27b0..11bcd78521 100644 --- a/compiler/optimizing/locations.h +++ b/compiler/optimizing/locations.h @@ -34,7 +34,7 @@ class HInstruction; */ class Location : public ValueObject { public: - static constexpr bool kDiesAtEntry = true; + static constexpr bool kNoOutputOverlap = false; enum Kind { kInvalid = 0, @@ -373,8 +373,7 @@ class LocationSummary : public ArenaObject { LocationSummary(HInstruction* instruction, CallKind call_kind = kNoCall); - void SetInAt(uint32_t at, Location location, bool dies_at_entry = false) { - dies_at_entry_.Put(at, dies_at_entry); + void SetInAt(uint32_t at, Location location) { inputs_.Put(at, location); } @@ -386,7 +385,8 @@ class LocationSummary : public ArenaObject { return inputs_.Size(); } - void SetOut(Location location) { + void SetOut(Location location, bool overlaps = true) { + output_overlaps_ = overlaps; output_ = Location(location); } @@ -449,23 +449,30 @@ class LocationSummary : public ArenaObject { return &live_registers_; } - bool InputOverlapsWithOutputOrTemp(uint32_t input, bool is_environment) const { + bool InputOverlapsWithOutputOrTemp(uint32_t input_index, bool is_environment) const { if (is_environment) return true; - Location location = Out(); - if (input == 0 && location.IsUnallocated() && location.GetPolicy() == Location::kSameAsFirstInput) { + if ((input_index == 0) + && output_.IsUnallocated() + && (output_.GetPolicy() == Location::kSameAsFirstInput)) { return false; } - if (dies_at_entry_.Get(input)) { + if (inputs_.Get(input_index).IsRegister() || inputs_.Get(input_index).IsFpuRegister()) { return false; } return true; } + bool OutputOverlapsWithInputs() const { + return output_overlaps_; + } + private: GrowableArray<Location> inputs_; GrowableArray<Location> temps_; GrowableArray<Location> environment_; - GrowableArray<bool> dies_at_entry_; + // Whether the output overlaps with any of the inputs. If it overlaps, then it cannot + // share the same register as the inputs. + bool output_overlaps_; Location output_; const CallKind call_kind_; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 3a0b40c5de..d624ad5e5e 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -317,8 +317,8 @@ static void UpdateInputsUsers(HInstruction* instruction) { } void HBasicBlock::InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor) { - DCHECK(cursor->AsPhi() == nullptr); - DCHECK(instruction->AsPhi() == nullptr); + DCHECK(!cursor->IsPhi()); + DCHECK(!instruction->IsPhi()); DCHECK_EQ(instruction->GetId(), -1); DCHECK_NE(cursor->GetId(), -1); DCHECK_EQ(cursor->GetBlock(), this); @@ -363,6 +363,25 @@ void HBasicBlock::AddPhi(HPhi* phi) { Add(&phis_, this, phi); } +void HBasicBlock::InsertPhiAfter(HPhi* phi, HPhi* cursor) { + DCHECK_EQ(phi->GetId(), -1); + DCHECK_NE(cursor->GetId(), -1); + DCHECK_EQ(cursor->GetBlock(), this); + if (cursor->next_ == nullptr) { + cursor->next_ = phi; + phi->previous_ = cursor; + DCHECK(phi->next_ == nullptr); + } else { + phi->next_ = cursor->next_; + phi->previous_ = cursor; + cursor->next_ = phi; + phi->next_->previous_ = phi; + } + phi->SetBlock(this); + phi->SetId(GetGraph()->GetNextInstructionId()); + UpdateInputsUsers(phi); +} + static void Remove(HInstructionList* instruction_list, HBasicBlock* block, HInstruction* instruction) { @@ -472,7 +491,11 @@ bool HInstructionList::FoundBefore(const HInstruction* instruction1, return true; } -bool HInstruction::Dominates(HInstruction* other_instruction) const { +bool HInstruction::StrictlyDominates(HInstruction* other_instruction) const { + if (other_instruction == this) { + // An instruction does not strictly dominate itself. + return false; + } HBasicBlock* block = GetBlock(); HBasicBlock* other_block = other_instruction->GetBlock(); if (block != other_block) { @@ -527,6 +550,12 @@ void HInstruction::ReplaceWith(HInstruction* other) { env_uses_ = nullptr; } +void HInstruction::ReplaceInput(HInstruction* replacement, size_t index) { + InputAt(index)->RemoveUser(this, index); + SetRawInputAt(index, replacement); + replacement->AddUseAt(this, index); +} + size_t HInstruction::EnvironmentSize() const { return HasEnvironment() ? environment_->Size() : 0; } @@ -553,6 +582,12 @@ void HGraphVisitor::VisitInsertionOrder() { } } +void HGraphVisitor::VisitReversePostOrder() { + for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) { + VisitBasicBlock(it.Current()); + } +} + void HGraphVisitor::VisitBasicBlock(HBasicBlock* block) { for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { it.Current()->Accept(this); @@ -562,15 +597,30 @@ void HGraphVisitor::VisitBasicBlock(HBasicBlock* block) { } } -HConstant* HBinaryOperation::TryStaticEvaluation(ArenaAllocator* allocator) const { +HConstant* HUnaryOperation::TryStaticEvaluation() const { + if (GetInput()->IsIntConstant()) { + int32_t value = Evaluate(GetInput()->AsIntConstant()->GetValue()); + return new(GetBlock()->GetGraph()->GetArena()) HIntConstant(value); + } else if (GetInput()->IsLongConstant()) { + // TODO: Implement static evaluation of long unary operations. + // + // Do not exit with a fatal condition here. Instead, simply + // return `nullptr' to notify the caller that this instruction + // cannot (yet) be statically evaluated. + return nullptr; + } + return nullptr; +} + +HConstant* HBinaryOperation::TryStaticEvaluation() const { if (GetLeft()->IsIntConstant() && GetRight()->IsIntConstant()) { int32_t value = Evaluate(GetLeft()->AsIntConstant()->GetValue(), GetRight()->AsIntConstant()->GetValue()); - return new(allocator) HIntConstant(value); + return new(GetBlock()->GetGraph()->GetArena()) HIntConstant(value); } else if (GetLeft()->IsLongConstant() && GetRight()->IsLongConstant()) { int64_t value = Evaluate(GetLeft()->AsLongConstant()->GetValue(), GetRight()->AsLongConstant()->GetValue()); - return new(allocator) HLongConstant(value); + return new(GetBlock()->GetGraph()->GetArena()) HLongConstant(value); } return nullptr; } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 677a4f8591..7adb84008a 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -399,6 +399,7 @@ class HBasicBlock : public ArenaObject { void ReplaceAndRemoveInstructionWith(HInstruction* initial, HInstruction* replacement); void AddPhi(HPhi* phi); + void InsertPhiAfter(HPhi* instruction, HPhi* cursor); void RemovePhi(HPhi* phi); bool IsLoopHeader() const { @@ -484,7 +485,7 @@ class HBasicBlock : public ArenaObject { M(Local, Instruction) \ M(LongConstant, Constant) \ M(NewInstance, Instruction) \ - M(Not, Instruction) \ + M(Not, UnaryOperation) \ M(ParameterValue, Instruction) \ M(ParallelMove, Instruction) \ M(Phi, Instruction) \ @@ -502,11 +503,17 @@ class HBasicBlock : public ArenaObject { M(NullCheck, Instruction) \ M(Temporary, Instruction) \ M(SuspendCheck, Instruction) \ + M(Mul, BinaryOperation) \ + M(Neg, UnaryOperation) \ + M(FloatConstant, Constant) \ + M(DoubleConstant, Constant) \ + M(NewArray, Instruction) \ #define FOR_EACH_INSTRUCTION(M) \ FOR_EACH_CONCRETE_INSTRUCTION(M) \ M(Constant, Instruction) \ - M(BinaryOperation, Instruction) \ + M(UnaryOperation, Instruction) \ + M(BinaryOperation, Instruction) \ M(Invoke, Instruction) #define FORWARD_DECLARATION(type, super) class H##type; @@ -650,6 +657,7 @@ class HInstruction : public ArenaObject { virtual bool NeedsEnvironment() const { return false; } virtual bool IsControlFlow() const { return false; } + virtual bool CanThrow() const { return false; } bool HasSideEffects() const { return side_effects_.HasSideEffects(); } void AddUseAt(HInstruction* user, size_t index) { @@ -682,9 +690,10 @@ class HInstruction : public ArenaObject { return result; } - // Does this instruction dominate `other_instruction`? Aborts if - // this instruction and `other_instruction` are both phis. - bool Dominates(HInstruction* other_instruction) const; + // Does this instruction strictly dominate `other_instruction`? + // Returns false if this instruction and `other_instruction` are the same. + // Aborts if this instruction and `other_instruction` are both phis. + bool StrictlyDominates(HInstruction* other_instruction) const; int GetId() const { return id_; } void SetId(int id) { id_ = id; } @@ -705,6 +714,7 @@ class HInstruction : public ArenaObject { void SetLocations(LocationSummary* locations) { locations_ = locations; } void ReplaceWith(HInstruction* instruction); + void ReplaceInput(HInstruction* replacement, size_t index); bool HasOnlyOneUse() const { return uses_ != nullptr && uses_->GetTail() == nullptr; @@ -990,8 +1000,8 @@ class HExpression : public HTemplateInstruction<N> { virtual Primitive::Type GetType() const { return type_; } - private: - const Primitive::Type type_; + protected: + Primitive::Type type_; }; // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow @@ -1083,6 +1093,34 @@ class HIf : public HTemplateInstruction<1> { DISALLOW_COPY_AND_ASSIGN(HIf); }; +class HUnaryOperation : public HExpression<1> { + public: + HUnaryOperation(Primitive::Type result_type, HInstruction* input) + : HExpression(result_type, SideEffects::None()) { + SetRawInputAt(0, input); + } + + HInstruction* GetInput() const { return InputAt(0); } + Primitive::Type GetResultType() const { return GetType(); } + + virtual bool CanBeMoved() const { return true; } + virtual bool InstructionDataEquals(HInstruction* other) const { return true; } + + // Try to statically evaluate `operation` and return a HConstant + // containing the result of this evaluation. If `operation` cannot + // be evaluated as a constant, return nullptr. + HConstant* TryStaticEvaluation() const; + + // Apply this operation to `x`. + virtual int32_t Evaluate(int32_t x) const = 0; + virtual int64_t Evaluate(int64_t x) const = 0; + + DECLARE_INSTRUCTION(UnaryOperation); + + private: + DISALLOW_COPY_AND_ASSIGN(HUnaryOperation); +}; + class HBinaryOperation : public HExpression<2> { public: HBinaryOperation(Primitive::Type result_type, @@ -1101,10 +1139,10 @@ class HBinaryOperation : public HExpression<2> { virtual bool CanBeMoved() const { return true; } virtual bool InstructionDataEquals(HInstruction* other) const { return true; } - // Try to statically evaluate `operation` and return an HConstant + // Try to statically evaluate `operation` and return a HConstant // containing the result of this evaluation. If `operation` cannot // be evaluated as a constant, return nullptr. - HConstant* TryStaticEvaluation(ArenaAllocator* allocator) const; + HConstant* TryStaticEvaluation() const; // Apply this operation to `x` and `y`. virtual int32_t Evaluate(int32_t x, int32_t y) const = 0; @@ -1368,6 +1406,48 @@ class HConstant : public HExpression<0> { DISALLOW_COPY_AND_ASSIGN(HConstant); }; +class HFloatConstant : public HConstant { + public: + explicit HFloatConstant(float value) : HConstant(Primitive::kPrimFloat), value_(value) {} + + float GetValue() const { return value_; } + + virtual bool InstructionDataEquals(HInstruction* other) const { + return bit_cast<float, int32_t>(other->AsFloatConstant()->value_) == + bit_cast<float, int32_t>(value_); + } + + virtual size_t ComputeHashCode() const { return static_cast<size_t>(GetValue()); } + + DECLARE_INSTRUCTION(FloatConstant); + + private: + const float value_; + + DISALLOW_COPY_AND_ASSIGN(HFloatConstant); +}; + +class HDoubleConstant : public HConstant { + public: + explicit HDoubleConstant(double value) : HConstant(Primitive::kPrimDouble), value_(value) {} + + double GetValue() const { return value_; } + + virtual bool InstructionDataEquals(HInstruction* other) const { + return bit_cast<double, int64_t>(other->AsDoubleConstant()->value_) == + bit_cast<double, int64_t>(value_); + } + + virtual size_t ComputeHashCode() const { return static_cast<size_t>(GetValue()); } + + DECLARE_INSTRUCTION(DoubleConstant); + + private: + const double value_; + + DISALLOW_COPY_AND_ASSIGN(HDoubleConstant); +}; + // Constants of the type int. Those can be from Dex instructions, or // synthesized (for example with the if-eqz instruction). class HIntConstant : public HConstant { @@ -1515,6 +1595,44 @@ class HNewInstance : public HExpression<0> { DISALLOW_COPY_AND_ASSIGN(HNewInstance); }; +class HNeg : public HUnaryOperation { + public: + explicit HNeg(Primitive::Type result_type, HInstruction* input) + : HUnaryOperation(result_type, input) {} + + virtual int32_t Evaluate(int32_t x) const OVERRIDE { return -x; } + virtual int64_t Evaluate(int64_t x) const OVERRIDE { return -x; } + + DECLARE_INSTRUCTION(Neg); + + private: + DISALLOW_COPY_AND_ASSIGN(HNeg); +}; + +class HNewArray : public HExpression<1> { + public: + HNewArray(HInstruction* length, uint32_t dex_pc, uint16_t type_index) + : HExpression(Primitive::kPrimNot, SideEffects::None()), + dex_pc_(dex_pc), + type_index_(type_index) { + SetRawInputAt(0, length); + } + + uint32_t GetDexPc() const { return dex_pc_; } + uint16_t GetTypeIndex() const { return type_index_; } + + // Calls runtime so needs an environment. + virtual bool NeedsEnvironment() const { return true; } + + DECLARE_INSTRUCTION(NewArray); + + private: + const uint32_t dex_pc_; + const uint16_t type_index_; + + DISALLOW_COPY_AND_ASSIGN(HNewArray); +}; + class HAdd : public HBinaryOperation { public: HAdd(Primitive::Type result_type, HInstruction* left, HInstruction* right) @@ -1555,6 +1673,22 @@ class HSub : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HSub); }; +class HMul : public HBinaryOperation { + public: + HMul(Primitive::Type result_type, HInstruction* left, HInstruction* right) + : HBinaryOperation(result_type, left, right) {} + + virtual bool IsCommutative() { return true; } + + virtual int32_t Evaluate(int32_t x, int32_t y) const { return x * y; } + virtual int64_t Evaluate(int64_t x, int64_t y) const { return x * y; } + + DECLARE_INSTRUCTION(Mul); + + private: + DISALLOW_COPY_AND_ASSIGN(HMul); +}; + // The value of a parameter in this method. Its location depends on // the calling convention. class HParameterValue : public HExpression<0> { @@ -1574,15 +1708,17 @@ class HParameterValue : public HExpression<0> { DISALLOW_COPY_AND_ASSIGN(HParameterValue); }; -class HNot : public HExpression<1> { +class HNot : public HUnaryOperation { public: - explicit HNot(HInstruction* input) : HExpression(Primitive::kPrimBoolean, SideEffects::None()) { - SetRawInputAt(0, input); - } + explicit HNot(Primitive::Type result_type, HInstruction* input) + : HUnaryOperation(result_type, input) {} virtual bool CanBeMoved() const { return true; } virtual bool InstructionDataEquals(HInstruction* other) const { return true; } + virtual int32_t Evaluate(int32_t x) const OVERRIDE { return ~x; } + virtual int64_t Evaluate(int64_t x) const OVERRIDE { return ~x; } + DECLARE_INSTRUCTION(Not); private: @@ -1642,6 +1778,8 @@ class HNullCheck : public HExpression<1> { virtual bool NeedsEnvironment() const { return true; } + virtual bool CanThrow() const { return true; } + uint32_t GetDexPc() const { return dex_pc_; } DECLARE_INSTRUCTION(NullCheck); @@ -1729,6 +1867,7 @@ class HArrayGet : public HExpression<2> { virtual bool CanBeMoved() const { return true; } virtual bool InstructionDataEquals(HInstruction* other) const { return true; } + void SetType(Primitive::Type type) { type_ = type; } DECLARE_INSTRUCTION(ArrayGet); @@ -1741,11 +1880,11 @@ class HArraySet : public HTemplateInstruction<3> { HArraySet(HInstruction* array, HInstruction* index, HInstruction* value, - Primitive::Type component_type, + Primitive::Type expected_component_type, uint32_t dex_pc) : HTemplateInstruction(SideEffects::ChangesSomething()), dex_pc_(dex_pc), - component_type_(component_type) { + expected_component_type_(expected_component_type) { SetRawInputAt(0, array); SetRawInputAt(1, index); SetRawInputAt(2, value); @@ -1759,13 +1898,24 @@ class HArraySet : public HTemplateInstruction<3> { uint32_t GetDexPc() const { return dex_pc_; } - Primitive::Type GetComponentType() const { return component_type_; } + HInstruction* GetValue() const { return InputAt(2); } + + Primitive::Type GetComponentType() const { + // The Dex format does not type floating point index operations. Since the + // `expected_component_type_` is set during building and can therefore not + // be correct, we also check what is the value type. If it is a floating + // point type, we must use that type. + Primitive::Type value_type = GetValue()->GetType(); + return ((value_type == Primitive::kPrimFloat) || (value_type == Primitive::kPrimDouble)) + ? value_type + : expected_component_type_; + } DECLARE_INSTRUCTION(ArraySet); private: const uint32_t dex_pc_; - const Primitive::Type component_type_; + const Primitive::Type expected_component_type_; DISALLOW_COPY_AND_ASSIGN(HArraySet); }; @@ -1802,6 +1952,8 @@ class HBoundsCheck : public HExpression<2> { virtual bool NeedsEnvironment() const { return true; } + virtual bool CanThrow() const { return true; } + uint32_t GetDexPc() const { return dex_pc_; } DECLARE_INSTRUCTION(BoundsCheck); @@ -1956,8 +2108,12 @@ class HGraphVisitor : public ValueObject { virtual void VisitInstruction(HInstruction* instruction) {} virtual void VisitBasicBlock(HBasicBlock* block); + // Visit the graph following basic block insertion order. void VisitInsertionOrder(); + // Visit the graph following dominator tree reverse post-order. + void VisitReversePostOrder(); + HGraph* GetGraph() const { return graph_; } // Visit functions for instruction classes. @@ -1969,7 +2125,7 @@ class HGraphVisitor : public ValueObject { #undef DECLARE_VISIT_INSTRUCTION private: - HGraph* graph_; + HGraph* const graph_; DISALLOW_COPY_AND_ASSIGN(HGraphVisitor); }; diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc new file mode 100644 index 0000000000..ea98186d11 --- /dev/null +++ b/compiler/optimizing/optimization.cc @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "optimization.h" + +#include "base/dumpable.h" +#include "graph_checker.h" + +namespace art { + +void HOptimization::Execute() { + Run(); + visualizer_.DumpGraph(pass_name_); + Check(); +} + +void HOptimization::Check() { + if (kIsDebugBuild) { + if (is_in_ssa_form_) { + SSAChecker checker(graph_->GetArena(), graph_); + checker.Run(); + if (!checker.IsValid()) { + LOG(FATAL) << Dumpable<SSAChecker>(checker); + } + } else { + GraphChecker checker(graph_->GetArena(), graph_); + checker.Run(); + if (!checker.IsValid()) { + LOG(FATAL) << Dumpable<GraphChecker>(checker); + } + } + } +} + +} // namespace art diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h new file mode 100644 index 0000000000..59683e2075 --- /dev/null +++ b/compiler/optimizing/optimization.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_OPTIMIZATION_H_ +#define ART_COMPILER_OPTIMIZING_OPTIMIZATION_H_ + +#include "graph_visualizer.h" +#include "nodes.h" + +namespace art { + +/** + * Abstraction to implement an optimization pass. + */ +class HOptimization : public ValueObject { + public: + HOptimization(HGraph* graph, + bool is_in_ssa_form, + const char* pass_name, + const HGraphVisualizer& visualizer) + : graph_(graph), + is_in_ssa_form_(is_in_ssa_form), + pass_name_(pass_name), + visualizer_(visualizer) {} + + virtual ~HOptimization() {} + + // Execute the optimization pass. + void Execute(); + + // Return the name of the pass. + const char* GetPassName() const { return pass_name_; } + + // Peform the analysis itself. + virtual void Run() = 0; + + private: + // Verify the graph; abort if it is not valid. + void Check(); + + protected: + HGraph* const graph_; + + private: + // Does the analyzed graph use the SSA form? + const bool is_in_ssa_form_; + // Optimization pass name. + const char* pass_name_; + // A graph visualiser invoked after the execution of the optimization + // pass if enabled. + const HGraphVisualizer& visualizer_; + + DISALLOW_COPY_AND_ASSIGN(HOptimization); +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_OPTIMIZATION_H_ diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 3cf5a0b291..80e9cdb16f 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -22,6 +22,8 @@ #include "builder.h" #include "code_generator.h" #include "compiler.h" +#include "constant_folding.h" +#include "dead_code_elimination.h" #include "driver/compiler_driver.h" #include "driver/dex_compilation_unit.h" #include "graph_visualizer.h" @@ -213,7 +215,10 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite } // Do not attempt to compile on architectures we do not support. - if (instruction_set != kX86 && instruction_set != kX86_64 && instruction_set != kThumb2) { + if (instruction_set != kArm64 && + instruction_set != kThumb2 && + instruction_set != kX86 && + instruction_set != kX86_64) { return nullptr; } @@ -261,6 +266,9 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite visualizer.DumpGraph("ssa"); graph->FindNaturalLoops(); + HDeadCodeElimination(graph, visualizer).Execute(); + HConstantFolding(graph, visualizer).Execute(); + SsaRedundantPhiElimination(graph).Run(); SsaDeadPhiElimination(graph).Run(); InstructionSimplifier(graph).Run(); diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index 8b32262ab2..f95c4a47e3 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -16,6 +16,8 @@ #include "register_allocator.h" +#include <sstream> + #include "base/bit_vector-inl.h" #include "code_generator.h" #include "ssa_liveness_analysis.h" @@ -37,18 +39,21 @@ RegisterAllocator::RegisterAllocator(ArenaAllocator* allocator, handled_(allocator, 0), active_(allocator, 0), inactive_(allocator, 0), - physical_register_intervals_(allocator, codegen->GetNumberOfCoreRegisters()), + physical_core_register_intervals_(allocator, codegen->GetNumberOfCoreRegisters()), + physical_fp_register_intervals_(allocator, codegen->GetNumberOfFloatingPointRegisters()), temp_intervals_(allocator, 4), spill_slots_(allocator, kDefaultNumberOfSpillSlots), safepoints_(allocator, 0), processing_core_registers_(false), number_of_registers_(-1), registers_array_(nullptr), - blocked_registers_(codegen->GetBlockedCoreRegisters()), + blocked_core_registers_(codegen->GetBlockedCoreRegisters()), + blocked_fp_registers_(codegen->GetBlockedFloatingPointRegisters()), reserved_out_slots_(0), maximum_number_of_live_registers_(0) { codegen->SetupBlockedRegisters(); - physical_register_intervals_.SetSize(codegen->GetNumberOfCoreRegisters()); + physical_core_register_intervals_.SetSize(codegen->GetNumberOfCoreRegisters()); + physical_fp_register_intervals_.SetSize(codegen->GetNumberOfFloatingPointRegisters()); // Always reserve for the current method and the graph's max out registers. // TODO: compute it instead. reserved_out_slots_ = 1 + codegen->GetGraph()->GetMaximumNumberOfOutVRegs(); @@ -65,8 +70,10 @@ bool RegisterAllocator::CanAllocateRegistersFor(const HGraph& graph, it.Advance()) { HInstruction* current = it.Current(); if (current->GetType() == Primitive::kPrimLong && instruction_set != kX86_64) return false; - if (current->GetType() == Primitive::kPrimFloat) return false; - if (current->GetType() == Primitive::kPrimDouble) return false; + if ((current->GetType() == Primitive::kPrimFloat || current->GetType() == Primitive::kPrimDouble) + && instruction_set != kX86_64) { + return false; + } } } return true; @@ -93,14 +100,22 @@ void RegisterAllocator::AllocateRegisters() { void RegisterAllocator::BlockRegister(Location location, size_t start, - size_t end, - Primitive::Type type) { + size_t end) { int reg = location.reg(); - LiveInterval* interval = physical_register_intervals_.Get(reg); + DCHECK(location.IsRegister() || location.IsFpuRegister()); + LiveInterval* interval = location.IsRegister() + ? physical_core_register_intervals_.Get(reg) + : physical_fp_register_intervals_.Get(reg); + Primitive::Type type = location.IsRegister() + ? Primitive::kPrimInt + : Primitive::kPrimDouble; if (interval == nullptr) { interval = LiveInterval::MakeFixedInterval(allocator_, reg, type); - physical_register_intervals_.Put(reg, interval); - inactive_.Add(interval); + if (location.IsRegister()) { + physical_core_register_intervals_.Put(reg, interval); + } else { + physical_fp_register_intervals_.Put(reg, interval); + } } DCHECK(interval->GetRegister() == reg); interval->AddRange(start, end); @@ -123,8 +138,17 @@ void RegisterAllocator::AllocateRegistersInternal() { registers_array_ = allocator_->AllocArray<size_t>(number_of_registers_); processing_core_registers_ = true; unhandled_ = &unhandled_core_intervals_; + for (size_t i = 0, e = physical_core_register_intervals_.Size(); i < e; ++i) { + LiveInterval* fixed = physical_core_register_intervals_.Get(i); + if (fixed != nullptr) { + inactive_.Add(fixed); + } + } LinearScan(); + size_t saved_maximum_number_of_live_registers = maximum_number_of_live_registers_; + maximum_number_of_live_registers_ = 0; + inactive_.Reset(); active_.Reset(); handled_.Reset(); @@ -133,9 +157,14 @@ void RegisterAllocator::AllocateRegistersInternal() { registers_array_ = allocator_->AllocArray<size_t>(number_of_registers_); processing_core_registers_ = false; unhandled_ = &unhandled_fp_intervals_; - // TODO: Enable FP register allocation. - DCHECK(unhandled_->IsEmpty()); + for (size_t i = 0, e = physical_fp_register_intervals_.Size(); i < e; ++i) { + LiveInterval* fixed = physical_fp_register_intervals_.Get(i); + if (fixed != nullptr) { + inactive_.Add(fixed); + } + } LinearScan(); + maximum_number_of_live_registers_ += saved_maximum_number_of_live_registers; } void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { @@ -148,8 +177,9 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { for (size_t i = 0; i < locations->GetTempCount(); ++i) { Location temp = locations->GetTemp(i); if (temp.IsRegister()) { - BlockRegister(temp, position, position + 1, Primitive::kPrimInt); + BlockRegister(temp, position, position + 1); } else { + DCHECK(temp.IsUnallocated()); LiveInterval* interval = LiveInterval::MakeTempInterval(allocator_, Primitive::kPrimInt); temp_intervals_.Add(interval); interval->AddRange(position, position + 1); @@ -160,10 +190,6 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { bool core_register = (instruction->GetType() != Primitive::kPrimDouble) && (instruction->GetType() != Primitive::kPrimFloat); - GrowableArray<LiveInterval*>& unhandled = core_register - ? unhandled_core_intervals_ - : unhandled_fp_intervals_; - if (locations->CanCall()) { if (!instruction->IsSuspendCheck()) { codegen_->MarkNotLeaf(); @@ -180,7 +206,8 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { // maximum before updating locations. LiveInterval* interval = LiveInterval::MakeSlowPathInterval(allocator_, instruction); interval->AddRange(position, position + 1); - unhandled.Add(interval); + unhandled_core_intervals_.Add(interval); + unhandled_fp_intervals_.Add(interval); } } @@ -189,21 +216,29 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { for (size_t i = 0; i < codegen_->GetNumberOfCoreRegisters(); ++i) { BlockRegister(Location::RegisterLocation(i), position, - position + 1, - Primitive::kPrimInt); + position + 1); + } + for (size_t i = 0; i < codegen_->GetNumberOfFloatingPointRegisters(); ++i) { + BlockRegister(Location::FpuRegisterLocation(i), + position, + position + 1); } } for (size_t i = 0; i < instruction->InputCount(); ++i) { Location input = locations->InAt(i); - if (input.IsRegister()) { - BlockRegister(input, position, position + 1, instruction->InputAt(i)->GetType()); + if (input.IsRegister() || input.IsFpuRegister()) { + BlockRegister(input, position, position + 1); } } LiveInterval* current = instruction->GetLiveInterval(); if (current == nullptr) return; + GrowableArray<LiveInterval*>& unhandled = core_register + ? unhandled_core_intervals_ + : unhandled_fp_intervals_; + DCHECK(unhandled.IsEmpty() || current->StartsBeforeOrAt(unhandled.Peek())); // Some instructions define their output in fixed register/stack slot. We need // to ensure we know these locations before doing register allocation. For a @@ -213,21 +248,24 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { // // The backwards walking ensures the ranges are ordered on increasing start positions. Location output = locations->Out(); - if (output.IsRegister()) { + if (output.IsRegister() || output.IsFpuRegister()) { // Shift the interval's start by one to account for the blocked register. current->SetFrom(position + 1); current->SetRegister(output.reg()); - BlockRegister(output, position, position + 1, instruction->GetType()); + BlockRegister(output, position, position + 1); + } else if (!locations->OutputOverlapsWithInputs()) { + // Shift the interval's start by one to not interfere with the inputs. + current->SetFrom(position + 1); } else if (output.IsStackSlot() || output.IsDoubleStackSlot()) { current->SetSpillSlot(output.GetStackIndex()); } // If needed, add interval to the list of unhandled intervals. if (current->HasSpillSlot() || instruction->IsConstant()) { - // Split before first register use. + // Split just before first register use. size_t first_register_use = current->FirstRegisterUse(); if (first_register_use != kNoLifetime) { - LiveInterval* split = Split(current, first_register_use); + LiveInterval* split = Split(current, first_register_use - 1); // Don't add direclty to `unhandled`, it needs to be sorted and the start // of this new interval might be after intervals already in the list. AddSorted(&unhandled, split); @@ -278,10 +316,19 @@ bool RegisterAllocator::ValidateInternal(bool log_fatal_on_failure) const { } } - for (size_t i = 0, e = physical_register_intervals_.Size(); i < e; ++i) { - LiveInterval* fixed = physical_register_intervals_.Get(i); - if (fixed != nullptr && ShouldProcess(processing_core_registers_, fixed)) { - intervals.Add(fixed); + if (processing_core_registers_) { + for (size_t i = 0, e = physical_core_register_intervals_.Size(); i < e; ++i) { + LiveInterval* fixed = physical_core_register_intervals_.Get(i); + if (fixed != nullptr) { + intervals.Add(fixed); + } + } + } else { + for (size_t i = 0, e = physical_fp_register_intervals_.Size(); i < e; ++i) { + LiveInterval* fixed = physical_fp_register_intervals_.Get(i); + if (fixed != nullptr) { + intervals.Add(fixed); + } } } @@ -374,10 +421,10 @@ void RegisterAllocator::DumpInterval(std::ostream& stream, LiveInterval* interva interval->Dump(stream); stream << ": "; if (interval->HasRegister()) { - if (processing_core_registers_) { - codegen_->DumpCoreRegister(stream, interval->GetRegister()); - } else { + if (interval->IsFloatingPoint()) { codegen_->DumpFloatingPointRegister(stream, interval->GetRegister()); + } else { + codegen_->DumpCoreRegister(stream, interval->GetRegister()); } } else { stream << "spilled"; @@ -391,6 +438,7 @@ void RegisterAllocator::LinearScan() { // (1) Remove interval with the lowest start position from unhandled. LiveInterval* current = unhandled_->Pop(); DCHECK(!current->IsFixed() && !current->HasSpillSlot()); + DCHECK(unhandled_->IsEmpty() || unhandled_->Peek()->GetStart() >= current->GetStart()); size_t position = current->GetStart(); // (2) Remove currently active intervals that are dead at this position. @@ -519,10 +567,9 @@ bool RegisterAllocator::TryAllocateFreeReg(LiveInterval* current) { } bool RegisterAllocator::IsBlocked(int reg) const { - // TODO: This only works for core registers and needs to be adjusted for - // floating point registers. - DCHECK(processing_core_registers_); - return blocked_registers_[reg]; + return processing_core_registers_ + ? blocked_core_registers_[reg] + : blocked_fp_registers_[reg]; } // Find the register that is used the last, and spill the interval @@ -591,7 +638,9 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) { // If the first use of that instruction is after the last use of the found // register, we split this interval just before its first register use. AllocateSpillSlotFor(current); - LiveInterval* split = Split(current, first_register_use); + LiveInterval* split = Split(current, first_register_use - 1); + DCHECK_NE(current, split) << "There is not enough registers available for " + << split->GetParent()->GetDefinedBy()->DebugName(); AddSorted(unhandled_, split); return false; } else { @@ -635,6 +684,7 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) { } void RegisterAllocator::AddSorted(GrowableArray<LiveInterval*>* array, LiveInterval* interval) { + DCHECK(!interval->IsFixed() && !interval->HasSpillSlot()); size_t insert_at = 0; for (size_t i = array->Size(); i > 0; --i) { LiveInterval* current = array->Get(i - 1); @@ -723,17 +773,11 @@ void RegisterAllocator::AllocateSpillSlotFor(LiveInterval* interval) { parent->SetSpillSlot((slot + reserved_out_slots_) * kVRegSize); } -// We create a special marker for inputs moves to differentiate them from -// moves created during resolution. They must be different instructions -// because the input moves work on the assumption that the interval moves -// have been executed. -static constexpr size_t kInputMoveLifetimePosition = 0; -static bool IsInputMove(HInstruction* instruction) { - return instruction->GetLifetimePosition() == kInputMoveLifetimePosition; -} - static bool IsValidDestination(Location destination) { - return destination.IsRegister() || destination.IsStackSlot() || destination.IsDoubleStackSlot(); + return destination.IsRegister() + || destination.IsFpuRegister() + || destination.IsStackSlot() + || destination.IsDoubleStackSlot(); } void RegisterAllocator::AddInputMoveFor(HInstruction* user, @@ -742,20 +786,20 @@ void RegisterAllocator::AddInputMoveFor(HInstruction* user, DCHECK(IsValidDestination(destination)); if (source.Equals(destination)) return; - DCHECK(user->AsPhi() == nullptr); + DCHECK(!user->IsPhi()); HInstruction* previous = user->GetPrevious(); HParallelMove* move = nullptr; if (previous == nullptr - || previous->AsParallelMove() == nullptr - || !IsInputMove(previous)) { + || !previous->IsParallelMove() + || previous->GetLifetimePosition() < user->GetLifetimePosition()) { move = new (allocator_) HParallelMove(allocator_); - move->SetLifetimePosition(kInputMoveLifetimePosition); + move->SetLifetimePosition(user->GetLifetimePosition()); user->GetBlock()->InsertInstructionBefore(move, user); } else { move = previous->AsParallelMove(); } - DCHECK(IsInputMove(move)); + DCHECK_EQ(move->GetLifetimePosition(), user->GetLifetimePosition()); move->AddMove(new (allocator_) MoveOperands(source, destination, nullptr)); } @@ -778,7 +822,7 @@ void RegisterAllocator::InsertParallelMoveAt(size_t position, move = at->GetNext()->AsParallelMove(); // This is a parallel move for connecting siblings in a same block. We need to // differentiate it with moves for connecting blocks, and input moves. - if (move == nullptr || IsInputMove(move) || move->GetLifetimePosition() > position) { + if (move == nullptr || move->GetLifetimePosition() > position) { move = new (allocator_) HParallelMove(allocator_); move->SetLifetimePosition(position); at->GetBlock()->InsertInstructionBefore(move, at->GetNext()); @@ -786,12 +830,6 @@ void RegisterAllocator::InsertParallelMoveAt(size_t position, } else { // Move must happen before the instruction. HInstruction* previous = at->GetPrevious(); - if (previous != nullptr && previous->IsParallelMove() && IsInputMove(previous)) { - // This is a parallel move for connecting siblings in a same block. We need to - // differentiate it with input moves. - at = previous; - previous = previous->GetPrevious(); - } if (previous == nullptr || !previous->IsParallelMove() || previous->GetLifetimePosition() != position) { @@ -866,7 +904,7 @@ void RegisterAllocator::InsertMoveAfter(HInstruction* instruction, DCHECK(IsValidDestination(destination)); if (source.Equals(destination)) return; - if (instruction->AsPhi() != nullptr) { + if (instruction->IsPhi()) { InsertParallelMoveAtEntryOf(instruction->GetBlock(), instruction, source, destination); return; } @@ -889,7 +927,9 @@ void RegisterAllocator::ConnectSiblings(LiveInterval* interval) { if (current->HasSpillSlot() && current->HasRegister()) { // We spill eagerly, so move must be at definition. InsertMoveAfter(interval->GetDefinedBy(), - Location::RegisterLocation(interval->GetRegister()), + interval->IsFloatingPoint() + ? Location::FpuRegisterLocation(interval->GetRegister()) + : Location::RegisterLocation(interval->GetRegister()), interval->NeedsTwoSpillSlots() ? Location::DoubleStackSlot(interval->GetParent()->GetSpillSlot()) : Location::StackSlot(interval->GetParent()->GetSpillSlot())); @@ -947,6 +987,10 @@ void RegisterAllocator::ConnectSiblings(LiveInterval* interval) { } break; } + case Location::kFpuRegister: { + locations->AddLiveRegister(source); + break; + } case Location::kStackSlot: // Fall-through case Location::kDoubleStackSlot: // Fall-through case Location::kConstant: { @@ -1036,7 +1080,7 @@ void RegisterAllocator::Resolve() { LiveInterval* current = instruction->GetLiveInterval(); LocationSummary* locations = instruction->GetLocations(); Location location = locations->Out(); - if (instruction->AsParameterValue() != nullptr) { + if (instruction->IsParameterValue()) { // Now that we know the frame size, adjust the parameter's location. if (location.IsStackSlot()) { location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize()); @@ -1110,6 +1154,7 @@ void RegisterAllocator::Resolve() { current = at; } LocationSummary* locations = at->GetLocations(); + DCHECK(temp->GetType() == Primitive::kPrimInt); locations->SetTempAt( temp_index++, Location::RegisterLocation(temp->GetRegister())); } diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h index d4c233a7f8..b88153969b 100644 --- a/compiler/optimizing/register_allocator.h +++ b/compiler/optimizing/register_allocator.h @@ -21,8 +21,6 @@ #include "primitive.h" #include "utils/growable_array.h" -#include "gtest/gtest.h" - namespace art { class CodeGenerator; @@ -96,7 +94,7 @@ class RegisterAllocator { bool IsBlocked(int reg) const; // Update the interval for the register in `location` to cover [start, end). - void BlockRegister(Location location, size_t start, size_t end, Primitive::Type type); + void BlockRegister(Location location, size_t start, size_t end); // Allocate a spill slot for the given interval. void AllocateSpillSlotFor(LiveInterval* interval); @@ -158,7 +156,8 @@ class RegisterAllocator { // Fixed intervals for physical registers. Such intervals cover the positions // where an instruction requires a specific register. - GrowableArray<LiveInterval*> physical_register_intervals_; + GrowableArray<LiveInterval*> physical_core_register_intervals_; + GrowableArray<LiveInterval*> physical_fp_register_intervals_; // Intervals for temporaries. Such intervals cover the positions // where an instruction requires a temporary. @@ -181,7 +180,8 @@ class RegisterAllocator { size_t* registers_array_; // Blocked registers, as decided by the code generator. - bool* const blocked_registers_; + bool* const blocked_core_registers_; + bool* const blocked_fp_registers_; // Slots reserved for out arguments. size_t reserved_out_slots_; @@ -189,7 +189,7 @@ class RegisterAllocator { // The maximum live registers at safepoints. size_t maximum_number_of_live_registers_; - FRIEND_TEST(RegisterAllocatorTest, FreeUntil); + ART_FRIEND_TEST(RegisterAllocatorTest, FreeUntil); DISALLOW_COPY_AND_ASSIGN(RegisterAllocator); }; diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index 7517a6b003..2d84a9d335 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -348,14 +348,14 @@ TEST(RegisterAllocatorTest, FirstRegisterUse) { // Split at the next instruction. interval = interval->SplitAt(first_add->GetLifetimePosition() + 2); // The user of the split is the last add. - ASSERT_EQ(interval->FirstRegisterUse(), last_add->GetLifetimePosition() - 1); + ASSERT_EQ(interval->FirstRegisterUse(), last_add->GetLifetimePosition()); // Split before the last add. LiveInterval* new_interval = interval->SplitAt(last_add->GetLifetimePosition() - 1); // Ensure the current interval has no register use... ASSERT_EQ(interval->FirstRegisterUse(), kNoLifetime); // And the new interval has it for the last add. - ASSERT_EQ(new_interval->FirstRegisterUse(), last_add->GetLifetimePosition() - 1); + ASSERT_EQ(new_interval->FirstRegisterUse(), last_add->GetLifetimePosition()); } TEST(RegisterAllocatorTest, DeadPhi) { diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index 471307ec31..a0cc8a94ee 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -51,7 +51,7 @@ void SsaBuilder::BuildSsa() { !it.Done(); it.Advance()) { HInstruction* current = it.Current(); - if (current->AsLocal() != nullptr) { + if (current->IsLocal()) { current->GetBlock()->RemoveInstruction(current); } } @@ -129,8 +129,112 @@ void SsaBuilder::VisitBasicBlock(HBasicBlock* block) { } } +/** + * Constants in the Dex format are not typed. So the builder types them as + * integers, but when doing the SSA form, we might realize the constant + * is used for floating point operations. We create a floating-point equivalent + * constant to make the operations correctly typed. + */ +static HFloatConstant* GetFloatEquivalent(HIntConstant* constant) { + // We place the floating point constant next to this constant. + HFloatConstant* result = constant->GetNext()->AsFloatConstant(); + if (result == nullptr) { + HGraph* graph = constant->GetBlock()->GetGraph(); + ArenaAllocator* allocator = graph->GetArena(); + result = new (allocator) HFloatConstant(bit_cast<int32_t, float>(constant->GetValue())); + constant->GetBlock()->InsertInstructionBefore(result, constant->GetNext()); + } else { + // If there is already a constant with the expected type, we know it is + // the floating point equivalent of this constant. + DCHECK_EQ((bit_cast<float, int32_t>(result->GetValue())), constant->GetValue()); + } + return result; +} + +/** + * Wide constants in the Dex format are not typed. So the builder types them as + * longs, but when doing the SSA form, we might realize the constant + * is used for floating point operations. We create a floating-point equivalent + * constant to make the operations correctly typed. + */ +static HDoubleConstant* GetDoubleEquivalent(HLongConstant* constant) { + // We place the floating point constant next to this constant. + HDoubleConstant* result = constant->GetNext()->AsDoubleConstant(); + if (result == nullptr) { + HGraph* graph = constant->GetBlock()->GetGraph(); + ArenaAllocator* allocator = graph->GetArena(); + result = new (allocator) HDoubleConstant(bit_cast<int64_t, double>(constant->GetValue())); + constant->GetBlock()->InsertInstructionBefore(result, constant->GetNext()); + } else { + // If there is already a constant with the expected type, we know it is + // the floating point equivalent of this constant. + DCHECK_EQ((bit_cast<double, int64_t>(result->GetValue())), constant->GetValue()); + } + return result; +} + +/** + * Because of Dex format, we might end up having the same phi being + * used for non floating point operations and floating point operations. Because + * we want the graph to be correctly typed (and thereafter avoid moves between + * floating point registers and core registers), we need to create a copy of the + * phi with a floating point type. + */ +static HPhi* GetFloatOrDoubleEquivalentOfPhi(HPhi* phi, Primitive::Type type) { + // We place the floating point phi next to this phi. + HInstruction* next = phi->GetNext(); + if (next == nullptr + || (next->GetType() != Primitive::kPrimDouble && next->GetType() != Primitive::kPrimFloat)) { + ArenaAllocator* allocator = phi->GetBlock()->GetGraph()->GetArena(); + HPhi* new_phi = new (allocator) HPhi(allocator, phi->GetRegNumber(), phi->InputCount(), type); + for (size_t i = 0, e = phi->InputCount(); i < e; ++i) { + // Copy the inputs. Note that the graph may not be correctly typed by doing this copy, + // but the type propagation phase will fix it. + new_phi->SetRawInputAt(i, phi->InputAt(i)); + } + phi->GetBlock()->InsertPhiAfter(new_phi, phi); + return new_phi; + } else { + // If there is already a phi with the expected type, we know it is the floating + // point equivalent of this phi. + DCHECK_EQ(next->AsPhi()->GetRegNumber(), phi->GetRegNumber()); + return next->AsPhi(); + } +} + +HInstruction* SsaBuilder::GetFloatOrDoubleEquivalent(HInstruction* user, + HInstruction* value, + Primitive::Type type) { + if (value->IsArrayGet()) { + // The verifier has checked that values in arrays cannot be used for both + // floating point and non-floating point operations. It is therefore safe to just + // change the type of the operation. + value->AsArrayGet()->SetType(type); + return value; + } else if (value->IsLongConstant()) { + return GetDoubleEquivalent(value->AsLongConstant()); + } else if (value->IsIntConstant()) { + return GetFloatEquivalent(value->AsIntConstant()); + } else if (value->IsPhi()) { + return GetFloatOrDoubleEquivalentOfPhi(value->AsPhi(), type); + } else { + // For other instructions, we assume the verifier has checked that the dex format is correctly + // typed and the value in a dex register will not be used for both floating point and + // non-floating point operations. So the only reason an instruction would want a floating + // point equivalent is for an unused phi that will be removed by the dead phi elimination phase. + DCHECK(user->IsPhi()); + return value; + } +} + void SsaBuilder::VisitLoadLocal(HLoadLocal* load) { - load->ReplaceWith(current_locals_->Get(load->GetLocal()->GetRegNumber())); + HInstruction* value = current_locals_->Get(load->GetLocal()->GetRegNumber()); + if (load->GetType() != value->GetType() + && (load->GetType() == Primitive::kPrimFloat || load->GetType() == Primitive::kPrimDouble)) { + // If the operation requests a specific type, we make sure its input is of that type. + value = GetFloatOrDoubleEquivalent(load, value, load->GetType()); + } + load->ReplaceWith(value); load->GetBlock()->RemoveInstruction(load); } diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h index 9d8c0729ae..24f5ac55f7 100644 --- a/compiler/optimizing/ssa_builder.h +++ b/compiler/optimizing/ssa_builder.h @@ -52,6 +52,10 @@ class SsaBuilder : public HGraphVisitor { void VisitStoreLocal(HStoreLocal* store); void VisitInstruction(HInstruction* instruction); + static HInstruction* GetFloatOrDoubleEquivalent(HInstruction* user, + HInstruction* instruction, + Primitive::Type type); + private: // Locals for the current block being visited. GrowableArray<HInstruction*>* current_locals_; diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index f0edc6422c..1e34670d76 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -319,7 +319,7 @@ int LiveInterval::FindFirstRegisterHint(size_t* free_until) const { if (user->IsPhi()) { // If the phi has a register, try to use the same. Location phi_location = user->GetLiveInterval()->ToLocation(); - if (phi_location.IsRegister() && free_until[phi_location.reg()] >= use_position) { + if (SameRegisterKind(phi_location) && free_until[phi_location.reg()] >= use_position) { return phi_location.reg(); } const GrowableArray<HBasicBlock*>& predecessors = user->GetBlock()->GetPredecessors(); @@ -345,7 +345,7 @@ int LiveInterval::FindFirstRegisterHint(size_t* free_until) const { // We use the user's lifetime position - 1 (and not `use_position`) because the // register is blocked at the beginning of the user. size_t position = user->GetLifetimePosition() - 1; - if (expected.IsRegister() && free_until[expected.reg()] >= position) { + if (SameRegisterKind(expected) && free_until[expected.reg()] >= position) { return expected.reg(); } } @@ -368,7 +368,7 @@ int LiveInterval::FindHintAtDefinition() const { // If the input dies at the end of the predecessor, we know its register can // be reused. Location input_location = input_interval.ToLocation(); - if (input_location.IsRegister()) { + if (SameRegisterKind(input_location)) { return input_location.reg(); } } @@ -384,7 +384,7 @@ int LiveInterval::FindHintAtDefinition() const { // If the input dies at the start of this instruction, we know its register can // be reused. Location location = input_interval.ToLocation(); - if (location.IsRegister()) { + if (SameRegisterKind(location)) { return location.reg(); } } @@ -393,13 +393,21 @@ int LiveInterval::FindHintAtDefinition() const { return kNoRegister; } +bool LiveInterval::SameRegisterKind(Location other) const { + return IsFloatingPoint() + ? other.IsFpuRegister() + : other.IsRegister(); +} + bool LiveInterval::NeedsTwoSpillSlots() const { return type_ == Primitive::kPrimLong || type_ == Primitive::kPrimDouble; } Location LiveInterval::ToLocation() const { if (HasRegister()) { - return Location::RegisterLocation(GetRegister()); + return IsFloatingPoint() + ? Location::FpuRegisterLocation(GetRegister()) + : Location::RegisterLocation(GetRegister()); } else { HInstruction* defined_by = GetParent()->GetDefinedBy(); if (defined_by->IsConstant()) { diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index e9bd30338d..7dda4f61d5 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -32,6 +32,7 @@ class BlockInfo : public ArenaObject { live_in_(allocator, number_of_ssa_values, false), live_out_(allocator, number_of_ssa_values, false), kill_(allocator, number_of_ssa_values, false) { + UNUSED(block_); live_in_.ClearAllBits(); live_out_.ClearAllBits(); kill_.ClearAllBits(); @@ -188,10 +189,14 @@ class LiveInterval : public ArenaObject { && (first_use_->GetPosition() < position)) { // The user uses the instruction multiple times, and one use dies before the other. // We update the use list so that the latter is first. + UsePosition* cursor = first_use_; + while ((cursor->GetNext() != nullptr) && (cursor->GetNext()->GetPosition() < position)) { + cursor = cursor->GetNext(); + } DCHECK(first_use_->GetPosition() + 1 == position); UsePosition* new_use = new (allocator_) UsePosition( - instruction, input_index, is_environment, position, first_use_->GetNext()); - first_use_->SetNext(new_use); + instruction, input_index, is_environment, position, cursor->GetNext()); + cursor->SetNext(new_use); if (first_range_->GetEnd() == first_use_->GetPosition()) { first_range_->end_ = position; } @@ -354,6 +359,10 @@ class LiveInterval : public ArenaObject { || (location.GetPolicy() == Location::kSameAsFirstInput && locations->InAt(0).GetPolicy() == Location::kRequiresRegister)) { return position; + } else if ((location.GetPolicy() == Location::kRequiresFpuRegister) + || (location.GetPolicy() == Location::kSameAsFirstInput + && locations->InAt(0).GetPolicy() == Location::kRequiresFpuRegister)) { + return position; } } } @@ -362,12 +371,12 @@ class LiveInterval : public ArenaObject { size_t end = GetEnd(); while (use != nullptr && use->GetPosition() <= end) { size_t use_position = use->GetPosition(); - if (use_position >= position && !use->GetIsEnvironment()) { + if (use_position > position && !use->GetIsEnvironment()) { Location location = use->GetUser()->GetLocations()->InAt(use->GetInputIndex()); - if (location.IsUnallocated() && location.GetPolicy() == Location::kRequiresRegister) { - // Return the lifetime just before the user, so that the interval has a register - // when entering the user. - return use->GetUser()->GetLifetimePosition() - 1; + if (location.IsUnallocated() + && (location.GetPolicy() == Location::kRequiresRegister + || location.GetPolicy() == Location::kRequiresFpuRegister)) { + return use_position; } } use = use->GetNext(); @@ -498,6 +507,10 @@ class LiveInterval : public ArenaObject { // slots for spilling. bool NeedsTwoSpillSlots() const; + bool IsFloatingPoint() const { + return type_ == Primitive::kPrimFloat || type_ == Primitive::kPrimDouble; + } + // Converts the location of the interval to a `Location` object. Location ToLocation() const; @@ -509,6 +522,9 @@ class LiveInterval : public ArenaObject { bool IsTemp() const { return is_temp_; } + // Returns whether `other` and `this` share the same kind of register. + bool SameRegisterKind(Location other) const; + private: ArenaAllocator* const allocator_; diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc index e02a182ec8..4eda0f3757 100644 --- a/compiler/optimizing/ssa_phi_elimination.cc +++ b/compiler/optimizing/ssa_phi_elimination.cc @@ -24,18 +24,13 @@ void SsaDeadPhiElimination::Run() { HBasicBlock* block = it.Current(); for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { HPhi* phi = it.Current()->AsPhi(); - if (phi->HasEnvironmentUses()) { - // TODO: Do we want to keep that phi alive? - worklist_.Add(phi); - phi->SetLive(); - continue; - } for (HUseIterator<HInstruction> it(phi->GetUses()); !it.Done(); it.Advance()) { HUseListNode<HInstruction>* current = it.Current(); HInstruction* user = current->GetUser(); if (!user->IsPhi()) { worklist_.Add(phi); phi->SetLive(); + break; } else { phi->SetDead(); } @@ -76,6 +71,14 @@ void SsaDeadPhiElimination::Run() { current->RemoveUser(user, user_node->GetIndex()); } } + if (current->HasEnvironmentUses()) { + for (HUseIterator<HEnvironment> it(current->GetEnvUses()); !it.Done(); it.Advance()) { + HUseListNode<HEnvironment>* user_node = it.Current(); + HEnvironment* user = user_node->GetUser(); + user->SetRawEnvAt(user_node->GetIndex(), nullptr); + current->RemoveEnvironmentUser(user, user_node->GetIndex()); + } + } block->RemovePhi(current->AsPhi()); } current = next; diff --git a/compiler/optimizing/ssa_type_propagation.cc b/compiler/optimizing/ssa_type_propagation.cc index a860cb7cfe..3828142ed2 100644 --- a/compiler/optimizing/ssa_type_propagation.cc +++ b/compiler/optimizing/ssa_type_propagation.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "ssa_builder.h" #include "ssa_type_propagation.h" #include "nodes.h" @@ -38,15 +39,31 @@ static Primitive::Type MergeTypes(Primitive::Type existing, Primitive::Type new_ // Re-compute and update the type of the instruction. Returns // whether or not the type was changed. -static bool UpdateType(HPhi* phi) { +bool SsaTypePropagation::UpdateType(HPhi* phi) { Primitive::Type existing = phi->GetType(); - Primitive::Type new_type = Primitive::kPrimVoid; + Primitive::Type new_type = existing; for (size_t i = 0, e = phi->InputCount(); i < e; ++i) { Primitive::Type input_type = phi->InputAt(i)->GetType(); new_type = MergeTypes(new_type, input_type); } phi->SetType(new_type); + + if (new_type == Primitive::kPrimDouble || new_type == Primitive::kPrimFloat) { + // If the phi is of floating point type, we need to update its inputs to that + // type. For inputs that are phis, we need to recompute their types. + for (size_t i = 0, e = phi->InputCount(); i < e; ++i) { + HInstruction* input = phi->InputAt(i); + if (input->GetType() != new_type) { + HInstruction* equivalent = SsaBuilder::GetFloatOrDoubleEquivalent(phi, input, new_type); + phi->ReplaceInput(equivalent, i); + if (equivalent->IsPhi()) { + AddToWorklist(equivalent->AsPhi()); + } + } + } + } + return existing != new_type; } @@ -63,7 +80,12 @@ void SsaTypePropagation::VisitBasicBlock(HBasicBlock* block) { HPhi* phi = it.Current()->AsPhi(); // Set the initial type for the phi. Use the non back edge input for reaching // a fixed point faster. - phi->SetType(phi->InputAt(0)->GetType()); + Primitive::Type phi_type = phi->GetType(); + // We merge with the existing type, that has been set by the SSA builder. + DCHECK(phi_type == Primitive::kPrimVoid + || phi_type == Primitive::kPrimFloat + || phi_type == Primitive::kPrimDouble); + phi->SetType(MergeTypes(phi->InputAt(0)->GetType(), phi->GetType())); AddToWorklist(phi); } } else { diff --git a/compiler/optimizing/ssa_type_propagation.h b/compiler/optimizing/ssa_type_propagation.h index 5f471a9811..f4d3d6344a 100644 --- a/compiler/optimizing/ssa_type_propagation.h +++ b/compiler/optimizing/ssa_type_propagation.h @@ -34,6 +34,7 @@ class SsaTypePropagation : public ValueObject { void ProcessWorklist(); void AddToWorklist(HPhi* phi); void AddDependentInstructionsToWorklist(HPhi* phi); + bool UpdateType(HPhi* phi); HGraph* const graph_; GrowableArray<HPhi*> worklist_; diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc index 6da375aef0..733b58fa5a 100644 --- a/compiler/trampolines/trampoline_compiler.cc +++ b/compiler/trampolines/trampoline_compiler.cc @@ -62,23 +62,23 @@ static const std::vector<uint8_t>* CreateTrampoline(EntryPointCallingConvention switch (abi) { case kInterpreterAbi: // Thread* is first argument (X0) in interpreter ABI. - __ JumpTo(Arm64ManagedRegister::FromCoreRegister(X0), Offset(offset.Int32Value()), - Arm64ManagedRegister::FromCoreRegister(IP1)); + __ JumpTo(Arm64ManagedRegister::FromXRegister(X0), Offset(offset.Int32Value()), + Arm64ManagedRegister::FromXRegister(IP1)); break; case kJniAbi: // Load via Thread* held in JNIEnv* in first argument (X0). - __ LoadRawPtr(Arm64ManagedRegister::FromCoreRegister(IP1), - Arm64ManagedRegister::FromCoreRegister(X0), + __ LoadRawPtr(Arm64ManagedRegister::FromXRegister(IP1), + Arm64ManagedRegister::FromXRegister(X0), Offset(JNIEnvExt::SelfOffset().Int32Value())); - __ JumpTo(Arm64ManagedRegister::FromCoreRegister(IP1), Offset(offset.Int32Value()), - Arm64ManagedRegister::FromCoreRegister(IP0)); + __ JumpTo(Arm64ManagedRegister::FromXRegister(IP1), Offset(offset.Int32Value()), + Arm64ManagedRegister::FromXRegister(IP0)); break; case kPortableAbi: // X18 holds Thread*. case kQuickAbi: // Fall-through. - __ JumpTo(Arm64ManagedRegister::FromCoreRegister(TR), Offset(offset.Int32Value()), - Arm64ManagedRegister::FromCoreRegister(IP0)); + __ JumpTo(Arm64ManagedRegister::FromXRegister(TR), Offset(offset.Int32Value()), + Arm64ManagedRegister::FromXRegister(IP0)); break; } @@ -166,7 +166,7 @@ const std::vector<uint8_t>* CreateTrampoline64(InstructionSet isa, EntryPointCal return x86_64::CreateTrampoline(offset); default: LOG(FATAL) << "Unexpected InstructionSet: " << isa; - return nullptr; + UNREACHABLE(); } } @@ -182,7 +182,7 @@ const std::vector<uint8_t>* CreateTrampoline32(InstructionSet isa, EntryPointCal return x86::CreateTrampoline(offset); default: LOG(FATAL) << "Unexpected InstructionSet: " << isa; - return nullptr; + UNREACHABLE(); } } diff --git a/compiler/utils/arena_allocator.cc b/compiler/utils/arena_allocator.cc index 516ac2b388..0c93f0a71b 100644 --- a/compiler/utils/arena_allocator.cc +++ b/compiler/utils/arena_allocator.cc @@ -15,6 +15,7 @@ */ #include <algorithm> +#include <iomanip> #include <numeric> #include "arena_allocator.h" diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc index 25e02c35da..1af7374a3e 100644 --- a/compiler/utils/arm64/assembler_arm64.cc +++ b/compiler/utils/arm64/assembler_arm64.cc @@ -52,7 +52,7 @@ void Arm64Assembler::FinalizeInstructions(const MemoryRegion& region) { } void Arm64Assembler::GetCurrentThread(ManagedRegister tr) { - ___ Mov(reg_x(tr.AsArm64().AsCoreRegister()), reg_x(ETR)); + ___ Mov(reg_x(tr.AsArm64().AsXRegister()), reg_x(ETR)); } void Arm64Assembler::GetCurrentThread(FrameOffset offset, ManagedRegister /* scratch */) { @@ -71,11 +71,11 @@ void Arm64Assembler::DecreaseFrameSize(size_t adjust) { AddConstant(SP, adjust); } -void Arm64Assembler::AddConstant(Register rd, int32_t value, Condition cond) { +void Arm64Assembler::AddConstant(XRegister rd, int32_t value, Condition cond) { AddConstant(rd, rd, value, cond); } -void Arm64Assembler::AddConstant(Register rd, Register rn, int32_t value, +void Arm64Assembler::AddConstant(XRegister rd, XRegister rn, int32_t value, Condition cond) { if ((cond == al) || (cond == nv)) { // VIXL macro-assembler handles all variants. @@ -92,7 +92,7 @@ void Arm64Assembler::AddConstant(Register rd, Register rn, int32_t value, } void Arm64Assembler::StoreWToOffset(StoreOperandType type, WRegister source, - Register base, int32_t offset) { + XRegister base, int32_t offset) { switch (type) { case kStoreByte: ___ Strb(reg_w(source), MEM_OP(reg_x(base), offset)); @@ -108,16 +108,16 @@ void Arm64Assembler::StoreWToOffset(StoreOperandType type, WRegister source, } } -void Arm64Assembler::StoreToOffset(Register source, Register base, int32_t offset) { +void Arm64Assembler::StoreToOffset(XRegister source, XRegister base, int32_t offset) { CHECK_NE(source, SP); ___ Str(reg_x(source), MEM_OP(reg_x(base), offset)); } -void Arm64Assembler::StoreSToOffset(SRegister source, Register base, int32_t offset) { +void Arm64Assembler::StoreSToOffset(SRegister source, XRegister base, int32_t offset) { ___ Str(reg_s(source), MEM_OP(reg_x(base), offset)); } -void Arm64Assembler::StoreDToOffset(DRegister source, Register base, int32_t offset) { +void Arm64Assembler::StoreDToOffset(DRegister source, XRegister base, int32_t offset) { ___ Str(reg_d(source), MEM_OP(reg_x(base), offset)); } @@ -128,9 +128,9 @@ void Arm64Assembler::Store(FrameOffset offs, ManagedRegister m_src, size_t size) } else if (src.IsWRegister()) { CHECK_EQ(4u, size); StoreWToOffset(kStoreWord, src.AsWRegister(), SP, offs.Int32Value()); - } else if (src.IsCoreRegister()) { + } else if (src.IsXRegister()) { CHECK_EQ(8u, size); - StoreToOffset(src.AsCoreRegister(), SP, offs.Int32Value()); + StoreToOffset(src.AsXRegister(), SP, offs.Int32Value()); } else if (src.IsSRegister()) { StoreSToOffset(src.AsSRegister(), SP, offs.Int32Value()); } else { @@ -141,41 +141,41 @@ void Arm64Assembler::Store(FrameOffset offs, ManagedRegister m_src, size_t size) void Arm64Assembler::StoreRef(FrameOffset offs, ManagedRegister m_src) { Arm64ManagedRegister src = m_src.AsArm64(); - CHECK(src.IsCoreRegister()) << src; - StoreWToOffset(kStoreWord, src.AsOverlappingCoreRegisterLow(), SP, + CHECK(src.IsXRegister()) << src; + StoreWToOffset(kStoreWord, src.AsOverlappingWRegister(), SP, offs.Int32Value()); } void Arm64Assembler::StoreRawPtr(FrameOffset offs, ManagedRegister m_src) { Arm64ManagedRegister src = m_src.AsArm64(); - CHECK(src.IsCoreRegister()) << src; - StoreToOffset(src.AsCoreRegister(), SP, offs.Int32Value()); + CHECK(src.IsXRegister()) << src; + StoreToOffset(src.AsXRegister(), SP, offs.Int32Value()); } void Arm64Assembler::StoreImmediateToFrame(FrameOffset offs, uint32_t imm, ManagedRegister m_scratch) { Arm64ManagedRegister scratch = m_scratch.AsArm64(); - CHECK(scratch.IsCoreRegister()) << scratch; - LoadImmediate(scratch.AsCoreRegister(), imm); - StoreWToOffset(kStoreWord, scratch.AsOverlappingCoreRegisterLow(), SP, + CHECK(scratch.IsXRegister()) << scratch; + LoadImmediate(scratch.AsXRegister(), imm); + StoreWToOffset(kStoreWord, scratch.AsOverlappingWRegister(), SP, offs.Int32Value()); } void Arm64Assembler::StoreImmediateToThread64(ThreadOffset<8> offs, uint32_t imm, ManagedRegister m_scratch) { Arm64ManagedRegister scratch = m_scratch.AsArm64(); - CHECK(scratch.IsCoreRegister()) << scratch; - LoadImmediate(scratch.AsCoreRegister(), imm); - StoreToOffset(scratch.AsCoreRegister(), ETR, offs.Int32Value()); + CHECK(scratch.IsXRegister()) << scratch; + LoadImmediate(scratch.AsXRegister(), imm); + StoreToOffset(scratch.AsXRegister(), ETR, offs.Int32Value()); } void Arm64Assembler::StoreStackOffsetToThread64(ThreadOffset<8> tr_offs, FrameOffset fr_offs, ManagedRegister m_scratch) { Arm64ManagedRegister scratch = m_scratch.AsArm64(); - CHECK(scratch.IsCoreRegister()) << scratch; - AddConstant(scratch.AsCoreRegister(), SP, fr_offs.Int32Value()); - StoreToOffset(scratch.AsCoreRegister(), ETR, tr_offs.Int32Value()); + CHECK(scratch.IsXRegister()) << scratch; + AddConstant(scratch.AsXRegister(), SP, fr_offs.Int32Value()); + StoreToOffset(scratch.AsXRegister(), ETR, tr_offs.Int32Value()); } void Arm64Assembler::StoreStackPointerToThread64(ThreadOffset<8> tr_offs) { @@ -189,13 +189,13 @@ void Arm64Assembler::StoreSpanning(FrameOffset dest_off, ManagedRegister m_sourc FrameOffset in_off, ManagedRegister m_scratch) { Arm64ManagedRegister source = m_source.AsArm64(); Arm64ManagedRegister scratch = m_scratch.AsArm64(); - StoreToOffset(source.AsCoreRegister(), SP, dest_off.Int32Value()); - LoadFromOffset(scratch.AsCoreRegister(), SP, in_off.Int32Value()); - StoreToOffset(scratch.AsCoreRegister(), SP, dest_off.Int32Value() + 8); + StoreToOffset(source.AsXRegister(), SP, dest_off.Int32Value()); + LoadFromOffset(scratch.AsXRegister(), SP, in_off.Int32Value()); + StoreToOffset(scratch.AsXRegister(), SP, dest_off.Int32Value() + 8); } // Load routines. -void Arm64Assembler::LoadImmediate(Register dest, int32_t value, +void Arm64Assembler::LoadImmediate(XRegister dest, int32_t value, Condition cond) { if ((cond == al) || (cond == nv)) { ___ Mov(reg_x(dest), value); @@ -215,7 +215,7 @@ void Arm64Assembler::LoadImmediate(Register dest, int32_t value, } void Arm64Assembler::LoadWFromOffset(LoadOperandType type, WRegister dest, - Register base, int32_t offset) { + XRegister base, int32_t offset) { switch (type) { case kLoadSignedByte: ___ Ldrsb(reg_w(dest), MEM_OP(reg_x(base), offset)); @@ -239,36 +239,36 @@ void Arm64Assembler::LoadWFromOffset(LoadOperandType type, WRegister dest, // Note: We can extend this member by adding load type info - see // sign extended A64 load variants. -void Arm64Assembler::LoadFromOffset(Register dest, Register base, +void Arm64Assembler::LoadFromOffset(XRegister dest, XRegister base, int32_t offset) { CHECK_NE(dest, SP); ___ Ldr(reg_x(dest), MEM_OP(reg_x(base), offset)); } -void Arm64Assembler::LoadSFromOffset(SRegister dest, Register base, +void Arm64Assembler::LoadSFromOffset(SRegister dest, XRegister base, int32_t offset) { ___ Ldr(reg_s(dest), MEM_OP(reg_x(base), offset)); } -void Arm64Assembler::LoadDFromOffset(DRegister dest, Register base, +void Arm64Assembler::LoadDFromOffset(DRegister dest, XRegister base, int32_t offset) { ___ Ldr(reg_d(dest), MEM_OP(reg_x(base), offset)); } -void Arm64Assembler::Load(Arm64ManagedRegister dest, Register base, +void Arm64Assembler::Load(Arm64ManagedRegister dest, XRegister base, int32_t offset, size_t size) { if (dest.IsNoRegister()) { CHECK_EQ(0u, size) << dest; } else if (dest.IsWRegister()) { CHECK_EQ(4u, size) << dest; ___ Ldr(reg_w(dest.AsWRegister()), MEM_OP(reg_x(base), offset)); - } else if (dest.IsCoreRegister()) { - CHECK_NE(dest.AsCoreRegister(), SP) << dest; + } else if (dest.IsXRegister()) { + CHECK_NE(dest.AsXRegister(), SP) << dest; if (size == 4u) { - ___ Ldr(reg_w(dest.AsOverlappingCoreRegisterLow()), MEM_OP(reg_x(base), offset)); + ___ Ldr(reg_w(dest.AsOverlappingWRegister()), MEM_OP(reg_x(base), offset)); } else { CHECK_EQ(8u, size) << dest; - ___ Ldr(reg_x(dest.AsCoreRegister()), MEM_OP(reg_x(base), offset)); + ___ Ldr(reg_x(dest.AsXRegister()), MEM_OP(reg_x(base), offset)); } } else if (dest.IsSRegister()) { ___ Ldr(reg_s(dest.AsSRegister()), MEM_OP(reg_x(base), offset)); @@ -288,19 +288,19 @@ void Arm64Assembler::LoadFromThread64(ManagedRegister m_dst, ThreadOffset<8> src void Arm64Assembler::LoadRef(ManagedRegister m_dst, FrameOffset offs) { Arm64ManagedRegister dst = m_dst.AsArm64(); - CHECK(dst.IsCoreRegister()) << dst; - LoadWFromOffset(kLoadWord, dst.AsOverlappingCoreRegisterLow(), SP, offs.Int32Value()); + CHECK(dst.IsXRegister()) << dst; + LoadWFromOffset(kLoadWord, dst.AsOverlappingWRegister(), SP, offs.Int32Value()); } void Arm64Assembler::LoadRef(ManagedRegister m_dst, ManagedRegister m_base, MemberOffset offs) { Arm64ManagedRegister dst = m_dst.AsArm64(); Arm64ManagedRegister base = m_base.AsArm64(); - CHECK(dst.IsCoreRegister() && base.IsCoreRegister()); - LoadWFromOffset(kLoadWord, dst.AsOverlappingCoreRegisterLow(), base.AsCoreRegister(), + CHECK(dst.IsXRegister() && base.IsXRegister()); + LoadWFromOffset(kLoadWord, dst.AsOverlappingWRegister(), base.AsXRegister(), offs.Int32Value()); if (kPoisonHeapReferences) { - WRegister ref_reg = dst.AsOverlappingCoreRegisterLow(); + WRegister ref_reg = dst.AsOverlappingWRegister(); ___ Neg(reg_w(ref_reg), vixl::Operand(reg_w(ref_reg))); } } @@ -308,17 +308,17 @@ void Arm64Assembler::LoadRef(ManagedRegister m_dst, ManagedRegister m_base, void Arm64Assembler::LoadRawPtr(ManagedRegister m_dst, ManagedRegister m_base, Offset offs) { Arm64ManagedRegister dst = m_dst.AsArm64(); Arm64ManagedRegister base = m_base.AsArm64(); - CHECK(dst.IsCoreRegister() && base.IsCoreRegister()); + CHECK(dst.IsXRegister() && base.IsXRegister()); // Remove dst and base form the temp list - higher level API uses IP1, IP0. vixl::UseScratchRegisterScope temps(vixl_masm_); - temps.Exclude(reg_x(dst.AsCoreRegister()), reg_x(base.AsCoreRegister())); - ___ Ldr(reg_x(dst.AsCoreRegister()), MEM_OP(reg_x(base.AsCoreRegister()), offs.Int32Value())); + temps.Exclude(reg_x(dst.AsXRegister()), reg_x(base.AsXRegister())); + ___ Ldr(reg_x(dst.AsXRegister()), MEM_OP(reg_x(base.AsXRegister()), offs.Int32Value())); } void Arm64Assembler::LoadRawPtrFromThread64(ManagedRegister m_dst, ThreadOffset<8> offs) { Arm64ManagedRegister dst = m_dst.AsArm64(); - CHECK(dst.IsCoreRegister()) << dst; - LoadFromOffset(dst.AsCoreRegister(), ETR, offs.Int32Value()); + CHECK(dst.IsXRegister()) << dst; + LoadFromOffset(dst.AsXRegister(), ETR, offs.Int32Value()); } // Copying routines. @@ -326,15 +326,15 @@ void Arm64Assembler::Move(ManagedRegister m_dst, ManagedRegister m_src, size_t s Arm64ManagedRegister dst = m_dst.AsArm64(); Arm64ManagedRegister src = m_src.AsArm64(); if (!dst.Equals(src)) { - if (dst.IsCoreRegister()) { + if (dst.IsXRegister()) { if (size == 4) { CHECK(src.IsWRegister()); - ___ Mov(reg_x(dst.AsCoreRegister()), reg_w(src.AsWRegister())); + ___ Mov(reg_x(dst.AsXRegister()), reg_w(src.AsWRegister())); } else { - if (src.IsCoreRegister()) { - ___ Mov(reg_x(dst.AsCoreRegister()), reg_x(src.AsCoreRegister())); + if (src.IsXRegister()) { + ___ Mov(reg_x(dst.AsXRegister()), reg_x(src.AsXRegister())); } else { - ___ Mov(reg_x(dst.AsCoreRegister()), reg_w(src.AsWRegister())); + ___ Mov(reg_x(dst.AsXRegister()), reg_w(src.AsWRegister())); } } } else if (dst.IsWRegister()) { @@ -355,41 +355,41 @@ void Arm64Assembler::CopyRawPtrFromThread64(FrameOffset fr_offs, ThreadOffset<8> tr_offs, ManagedRegister m_scratch) { Arm64ManagedRegister scratch = m_scratch.AsArm64(); - CHECK(scratch.IsCoreRegister()) << scratch; - LoadFromOffset(scratch.AsCoreRegister(), ETR, tr_offs.Int32Value()); - StoreToOffset(scratch.AsCoreRegister(), SP, fr_offs.Int32Value()); + CHECK(scratch.IsXRegister()) << scratch; + LoadFromOffset(scratch.AsXRegister(), ETR, tr_offs.Int32Value()); + StoreToOffset(scratch.AsXRegister(), SP, fr_offs.Int32Value()); } void Arm64Assembler::CopyRawPtrToThread64(ThreadOffset<8> tr_offs, FrameOffset fr_offs, ManagedRegister m_scratch) { Arm64ManagedRegister scratch = m_scratch.AsArm64(); - CHECK(scratch.IsCoreRegister()) << scratch; - LoadFromOffset(scratch.AsCoreRegister(), SP, fr_offs.Int32Value()); - StoreToOffset(scratch.AsCoreRegister(), ETR, tr_offs.Int32Value()); + CHECK(scratch.IsXRegister()) << scratch; + LoadFromOffset(scratch.AsXRegister(), SP, fr_offs.Int32Value()); + StoreToOffset(scratch.AsXRegister(), ETR, tr_offs.Int32Value()); } void Arm64Assembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister m_scratch) { Arm64ManagedRegister scratch = m_scratch.AsArm64(); - CHECK(scratch.IsCoreRegister()) << scratch; - LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), + CHECK(scratch.IsXRegister()) << scratch; + LoadWFromOffset(kLoadWord, scratch.AsOverlappingWRegister(), SP, src.Int32Value()); - StoreWToOffset(kStoreWord, scratch.AsOverlappingCoreRegisterLow(), + StoreWToOffset(kStoreWord, scratch.AsOverlappingWRegister(), SP, dest.Int32Value()); } void Arm64Assembler::Copy(FrameOffset dest, FrameOffset src, ManagedRegister m_scratch, size_t size) { Arm64ManagedRegister scratch = m_scratch.AsArm64(); - CHECK(scratch.IsCoreRegister()) << scratch; + CHECK(scratch.IsXRegister()) << scratch; CHECK(size == 4 || size == 8) << size; if (size == 4) { - LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), SP, src.Int32Value()); - StoreWToOffset(kStoreWord, scratch.AsOverlappingCoreRegisterLow(), SP, dest.Int32Value()); + LoadWFromOffset(kLoadWord, scratch.AsOverlappingWRegister(), SP, src.Int32Value()); + StoreWToOffset(kStoreWord, scratch.AsOverlappingWRegister(), SP, dest.Int32Value()); } else if (size == 8) { - LoadFromOffset(scratch.AsCoreRegister(), SP, src.Int32Value()); - StoreToOffset(scratch.AsCoreRegister(), SP, dest.Int32Value()); + LoadFromOffset(scratch.AsXRegister(), SP, src.Int32Value()); + StoreToOffset(scratch.AsXRegister(), SP, dest.Int32Value()); } else { UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8"; } @@ -399,16 +399,16 @@ void Arm64Assembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src ManagedRegister m_scratch, size_t size) { Arm64ManagedRegister scratch = m_scratch.AsArm64(); Arm64ManagedRegister base = src_base.AsArm64(); - CHECK(base.IsCoreRegister()) << base; - CHECK(scratch.IsCoreRegister() || scratch.IsWRegister()) << scratch; + CHECK(base.IsXRegister()) << base; + CHECK(scratch.IsXRegister() || scratch.IsWRegister()) << scratch; CHECK(size == 4 || size == 8) << size; if (size == 4) { - LoadWFromOffset(kLoadWord, scratch.AsWRegister(), base.AsCoreRegister(), + LoadWFromOffset(kLoadWord, scratch.AsWRegister(), base.AsXRegister(), src_offset.Int32Value()); StoreWToOffset(kStoreWord, scratch.AsWRegister(), SP, dest.Int32Value()); } else if (size == 8) { - LoadFromOffset(scratch.AsCoreRegister(), base.AsCoreRegister(), src_offset.Int32Value()); - StoreToOffset(scratch.AsCoreRegister(), SP, dest.Int32Value()); + LoadFromOffset(scratch.AsXRegister(), base.AsXRegister(), src_offset.Int32Value()); + StoreToOffset(scratch.AsXRegister(), SP, dest.Int32Value()); } else { UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8"; } @@ -418,16 +418,16 @@ void Arm64Assembler::Copy(ManagedRegister m_dest_base, Offset dest_offs, FrameOf ManagedRegister m_scratch, size_t size) { Arm64ManagedRegister scratch = m_scratch.AsArm64(); Arm64ManagedRegister base = m_dest_base.AsArm64(); - CHECK(base.IsCoreRegister()) << base; - CHECK(scratch.IsCoreRegister() || scratch.IsWRegister()) << scratch; + CHECK(base.IsXRegister()) << base; + CHECK(scratch.IsXRegister() || scratch.IsWRegister()) << scratch; CHECK(size == 4 || size == 8) << size; if (size == 4) { LoadWFromOffset(kLoadWord, scratch.AsWRegister(), SP, src.Int32Value()); - StoreWToOffset(kStoreWord, scratch.AsWRegister(), base.AsCoreRegister(), + StoreWToOffset(kStoreWord, scratch.AsWRegister(), base.AsXRegister(), dest_offs.Int32Value()); } else if (size == 8) { - LoadFromOffset(scratch.AsCoreRegister(), SP, src.Int32Value()); - StoreToOffset(scratch.AsCoreRegister(), base.AsCoreRegister(), dest_offs.Int32Value()); + LoadFromOffset(scratch.AsXRegister(), SP, src.Int32Value()); + StoreToOffset(scratch.AsXRegister(), base.AsXRegister(), dest_offs.Int32Value()); } else { UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8"; } @@ -444,25 +444,25 @@ void Arm64Assembler::Copy(ManagedRegister m_dest, Offset dest_offset, Arm64ManagedRegister scratch = m_scratch.AsArm64(); Arm64ManagedRegister src = m_src.AsArm64(); Arm64ManagedRegister dest = m_dest.AsArm64(); - CHECK(dest.IsCoreRegister()) << dest; - CHECK(src.IsCoreRegister()) << src; - CHECK(scratch.IsCoreRegister() || scratch.IsWRegister()) << scratch; + CHECK(dest.IsXRegister()) << dest; + CHECK(src.IsXRegister()) << src; + CHECK(scratch.IsXRegister() || scratch.IsWRegister()) << scratch; CHECK(size == 4 || size == 8) << size; if (size == 4) { if (scratch.IsWRegister()) { - LoadWFromOffset(kLoadWord, scratch.AsWRegister(), src.AsCoreRegister(), + LoadWFromOffset(kLoadWord, scratch.AsWRegister(), src.AsXRegister(), src_offset.Int32Value()); - StoreWToOffset(kStoreWord, scratch.AsWRegister(), dest.AsCoreRegister(), + StoreWToOffset(kStoreWord, scratch.AsWRegister(), dest.AsXRegister(), dest_offset.Int32Value()); } else { - LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), src.AsCoreRegister(), + LoadWFromOffset(kLoadWord, scratch.AsOverlappingWRegister(), src.AsXRegister(), src_offset.Int32Value()); - StoreWToOffset(kStoreWord, scratch.AsOverlappingCoreRegisterLow(), dest.AsCoreRegister(), + StoreWToOffset(kStoreWord, scratch.AsOverlappingWRegister(), dest.AsXRegister(), dest_offset.Int32Value()); } } else if (size == 8) { - LoadFromOffset(scratch.AsCoreRegister(), src.AsCoreRegister(), src_offset.Int32Value()); - StoreToOffset(scratch.AsCoreRegister(), dest.AsCoreRegister(), dest_offset.Int32Value()); + LoadFromOffset(scratch.AsXRegister(), src.AsXRegister(), src_offset.Int32Value()); + StoreToOffset(scratch.AsXRegister(), dest.AsXRegister(), dest_offset.Int32Value()); } else { UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8"; } @@ -514,31 +514,31 @@ void Arm64Assembler::VerifyObject(FrameOffset /*src*/, bool /*could_be_null*/) { void Arm64Assembler::Call(ManagedRegister m_base, Offset offs, ManagedRegister m_scratch) { Arm64ManagedRegister base = m_base.AsArm64(); Arm64ManagedRegister scratch = m_scratch.AsArm64(); - CHECK(base.IsCoreRegister()) << base; - CHECK(scratch.IsCoreRegister()) << scratch; - LoadFromOffset(scratch.AsCoreRegister(), base.AsCoreRegister(), offs.Int32Value()); - ___ Blr(reg_x(scratch.AsCoreRegister())); + CHECK(base.IsXRegister()) << base; + CHECK(scratch.IsXRegister()) << scratch; + LoadFromOffset(scratch.AsXRegister(), base.AsXRegister(), offs.Int32Value()); + ___ Blr(reg_x(scratch.AsXRegister())); } void Arm64Assembler::JumpTo(ManagedRegister m_base, Offset offs, ManagedRegister m_scratch) { Arm64ManagedRegister base = m_base.AsArm64(); Arm64ManagedRegister scratch = m_scratch.AsArm64(); - CHECK(base.IsCoreRegister()) << base; - CHECK(scratch.IsCoreRegister()) << scratch; + CHECK(base.IsXRegister()) << base; + CHECK(scratch.IsXRegister()) << scratch; // Remove base and scratch form the temp list - higher level API uses IP1, IP0. vixl::UseScratchRegisterScope temps(vixl_masm_); - temps.Exclude(reg_x(base.AsCoreRegister()), reg_x(scratch.AsCoreRegister())); - ___ Ldr(reg_x(scratch.AsCoreRegister()), MEM_OP(reg_x(base.AsCoreRegister()), offs.Int32Value())); - ___ Br(reg_x(scratch.AsCoreRegister())); + temps.Exclude(reg_x(base.AsXRegister()), reg_x(scratch.AsXRegister())); + ___ Ldr(reg_x(scratch.AsXRegister()), MEM_OP(reg_x(base.AsXRegister()), offs.Int32Value())); + ___ Br(reg_x(scratch.AsXRegister())); } void Arm64Assembler::Call(FrameOffset base, Offset offs, ManagedRegister m_scratch) { Arm64ManagedRegister scratch = m_scratch.AsArm64(); - CHECK(scratch.IsCoreRegister()) << scratch; + CHECK(scratch.IsXRegister()) << scratch; // Call *(*(SP + base) + offset) - LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), SP, base.Int32Value()); - LoadFromOffset(scratch.AsCoreRegister(), scratch.AsCoreRegister(), offs.Int32Value()); - ___ Blr(reg_x(scratch.AsCoreRegister())); + LoadWFromOffset(kLoadWord, scratch.AsOverlappingWRegister(), SP, base.Int32Value()); + LoadFromOffset(scratch.AsXRegister(), scratch.AsXRegister(), offs.Int32Value()); + ___ Blr(reg_x(scratch.AsXRegister())); } void Arm64Assembler::CallFromThread64(ThreadOffset<8> /*offset*/, ManagedRegister /*scratch*/) { @@ -550,59 +550,59 @@ void Arm64Assembler::CreateHandleScopeEntry(ManagedRegister m_out_reg, FrameOffs Arm64ManagedRegister out_reg = m_out_reg.AsArm64(); Arm64ManagedRegister in_reg = m_in_reg.AsArm64(); // For now we only hold stale handle scope entries in x registers. - CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg; - CHECK(out_reg.IsCoreRegister()) << out_reg; + CHECK(in_reg.IsNoRegister() || in_reg.IsXRegister()) << in_reg; + CHECK(out_reg.IsXRegister()) << out_reg; if (null_allowed) { // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is // the address in the handle scope holding the reference. // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset) if (in_reg.IsNoRegister()) { - LoadWFromOffset(kLoadWord, out_reg.AsOverlappingCoreRegisterLow(), SP, + LoadWFromOffset(kLoadWord, out_reg.AsOverlappingWRegister(), SP, handle_scope_offs.Int32Value()); in_reg = out_reg; } - ___ Cmp(reg_w(in_reg.AsOverlappingCoreRegisterLow()), 0); + ___ Cmp(reg_w(in_reg.AsOverlappingWRegister()), 0); if (!out_reg.Equals(in_reg)) { - LoadImmediate(out_reg.AsCoreRegister(), 0, eq); + LoadImmediate(out_reg.AsXRegister(), 0, eq); } - AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offs.Int32Value(), ne); + AddConstant(out_reg.AsXRegister(), SP, handle_scope_offs.Int32Value(), ne); } else { - AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offs.Int32Value(), al); + AddConstant(out_reg.AsXRegister(), SP, handle_scope_offs.Int32Value(), al); } } void Arm64Assembler::CreateHandleScopeEntry(FrameOffset out_off, FrameOffset handle_scope_offset, ManagedRegister m_scratch, bool null_allowed) { Arm64ManagedRegister scratch = m_scratch.AsArm64(); - CHECK(scratch.IsCoreRegister()) << scratch; + CHECK(scratch.IsXRegister()) << scratch; if (null_allowed) { - LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), SP, + LoadWFromOffset(kLoadWord, scratch.AsOverlappingWRegister(), SP, handle_scope_offset.Int32Value()); // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is // the address in the handle scope holding the reference. // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset) - ___ Cmp(reg_w(scratch.AsOverlappingCoreRegisterLow()), 0); + ___ Cmp(reg_w(scratch.AsOverlappingWRegister()), 0); // Move this logic in add constants with flags. - AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), ne); + AddConstant(scratch.AsXRegister(), SP, handle_scope_offset.Int32Value(), ne); } else { - AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), al); + AddConstant(scratch.AsXRegister(), SP, handle_scope_offset.Int32Value(), al); } - StoreToOffset(scratch.AsCoreRegister(), SP, out_off.Int32Value()); + StoreToOffset(scratch.AsXRegister(), SP, out_off.Int32Value()); } void Arm64Assembler::LoadReferenceFromHandleScope(ManagedRegister m_out_reg, ManagedRegister m_in_reg) { Arm64ManagedRegister out_reg = m_out_reg.AsArm64(); Arm64ManagedRegister in_reg = m_in_reg.AsArm64(); - CHECK(out_reg.IsCoreRegister()) << out_reg; - CHECK(in_reg.IsCoreRegister()) << in_reg; + CHECK(out_reg.IsXRegister()) << out_reg; + CHECK(in_reg.IsXRegister()) << in_reg; vixl::Label exit; if (!out_reg.Equals(in_reg)) { // FIXME: Who sets the flags here? - LoadImmediate(out_reg.AsCoreRegister(), 0, eq); + LoadImmediate(out_reg.AsXRegister(), 0, eq); } - ___ Cbz(reg_x(in_reg.AsCoreRegister()), &exit); - LoadFromOffset(out_reg.AsCoreRegister(), in_reg.AsCoreRegister(), 0); + ___ Cbz(reg_x(in_reg.AsXRegister()), &exit); + LoadFromOffset(out_reg.AsXRegister(), in_reg.AsXRegister(), 0); ___ Bind(&exit); } @@ -611,13 +611,13 @@ void Arm64Assembler::ExceptionPoll(ManagedRegister m_scratch, size_t stack_adjus Arm64ManagedRegister scratch = m_scratch.AsArm64(); Arm64Exception *current_exception = new Arm64Exception(scratch, stack_adjust); exception_blocks_.push_back(current_exception); - LoadFromOffset(scratch.AsCoreRegister(), ETR, Thread::ExceptionOffset<8>().Int32Value()); - ___ Cbnz(reg_x(scratch.AsCoreRegister()), current_exception->Entry()); + LoadFromOffset(scratch.AsXRegister(), ETR, Thread::ExceptionOffset<8>().Int32Value()); + ___ Cbnz(reg_x(scratch.AsXRegister()), current_exception->Entry()); } void Arm64Assembler::EmitExceptionPoll(Arm64Exception *exception) { vixl::UseScratchRegisterScope temps(vixl_masm_); - temps.Exclude(reg_x(exception->scratch_.AsCoreRegister())); + temps.Exclude(reg_x(exception->scratch_.AsXRegister())); vixl::Register temp = temps.AcquireX(); // Bind exception poll entry. @@ -627,7 +627,7 @@ void Arm64Assembler::EmitExceptionPoll(Arm64Exception *exception) { } // Pass exception object as argument. // Don't care about preserving X0 as this won't return. - ___ Mov(reg_x(X0), reg_x(exception->scratch_.AsCoreRegister())); + ___ Mov(reg_x(X0), reg_x(exception->scratch_.AsXRegister())); ___ Ldr(temp, MEM_OP(reg_x(ETR), QUICK_ENTRYPOINT_OFFSET(8, pDeliverException).Int32Value())); // Move ETR(Callee saved) back to TR(Caller saved) reg. We use ETR on calls @@ -646,7 +646,7 @@ void Arm64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, const std::vector<ManagedRegister>& callee_save_regs, const ManagedRegisterEntrySpills& entry_spills) { CHECK_ALIGNED(frame_size, kStackAlignment); - CHECK(X0 == method_reg.AsArm64().AsCoreRegister()); + CHECK(X0 == method_reg.AsArm64().AsXRegister()); // TODO: *create APCS FP - end of FP chain; // *add support for saving a different set of callee regs. @@ -700,8 +700,8 @@ void Arm64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, // only increment stack offset. ManagedRegisterSpill spill = entry_spills.at(i); offset += spill.getSize(); - } else if (reg.IsCoreRegister()) { - StoreToOffset(reg.AsCoreRegister(), SP, offset); + } else if (reg.IsXRegister()) { + StoreToOffset(reg.AsXRegister(), SP, offset); offset += 8; } else if (reg.IsWRegister()) { StoreWToOffset(kStoreWord, reg.AsWRegister(), SP, offset); diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h index ef833345f7..1b1d121725 100644 --- a/compiler/utils/arm64/assembler_arm64.h +++ b/compiler/utils/arm64/assembler_arm64.h @@ -173,7 +173,7 @@ class Arm64Assembler FINAL : public Assembler { private: static vixl::Register reg_x(int code) { - CHECK(code < kNumberOfCoreRegisters) << code; + CHECK(code < kNumberOfXRegisters) << code; if (code == SP) { return vixl::sp; } else if (code == XZR) { @@ -183,6 +183,12 @@ class Arm64Assembler FINAL : public Assembler { } static vixl::Register reg_w(int code) { + CHECK(code < kNumberOfWRegisters) << code; + if (code == WSP) { + return vixl::wsp; + } else if (code == WZR) { + return vixl::wzr; + } return vixl::Register::WRegFromCode(code); } @@ -198,27 +204,28 @@ class Arm64Assembler FINAL : public Assembler { void EmitExceptionPoll(Arm64Exception *exception); void StoreWToOffset(StoreOperandType type, WRegister source, - Register base, int32_t offset); - void StoreToOffset(Register source, Register base, int32_t offset); - void StoreSToOffset(SRegister source, Register base, int32_t offset); - void StoreDToOffset(DRegister source, Register base, int32_t offset); + XRegister base, int32_t offset); + void StoreToOffset(XRegister source, XRegister base, int32_t offset); + void StoreSToOffset(SRegister source, XRegister base, int32_t offset); + void StoreDToOffset(DRegister source, XRegister base, int32_t offset); - void LoadImmediate(Register dest, int32_t value, vixl::Condition cond = vixl::al); - void Load(Arm64ManagedRegister dst, Register src, int32_t src_offset, size_t size); + void LoadImmediate(XRegister dest, int32_t value, vixl::Condition cond = vixl::al); + void Load(Arm64ManagedRegister dst, XRegister src, int32_t src_offset, size_t size); void LoadWFromOffset(LoadOperandType type, WRegister dest, - Register base, int32_t offset); - void LoadFromOffset(Register dest, Register base, int32_t offset); - void LoadSFromOffset(SRegister dest, Register base, int32_t offset); - void LoadDFromOffset(DRegister dest, Register base, int32_t offset); - void AddConstant(Register rd, int32_t value, vixl::Condition cond = vixl::al); - void AddConstant(Register rd, Register rn, int32_t value, vixl::Condition cond = vixl::al); - - // Vixl assembler. - vixl::MacroAssembler* const vixl_masm_; + XRegister base, int32_t offset); + void LoadFromOffset(XRegister dest, XRegister base, int32_t offset); + void LoadSFromOffset(SRegister dest, XRegister base, int32_t offset); + void LoadDFromOffset(DRegister dest, XRegister base, int32_t offset); + void AddConstant(XRegister rd, int32_t value, vixl::Condition cond = vixl::al); + void AddConstant(XRegister rd, XRegister rn, int32_t value, vixl::Condition cond = vixl::al); // List of exception blocks to generate at the end of the code cache. std::vector<Arm64Exception*> exception_blocks_; + public: + // Vixl assembler. + vixl::MacroAssembler* const vixl_masm_; + // Used for testing. friend class Arm64ManagedRegister_VixlRegisters_Test; }; diff --git a/compiler/utils/arm64/managed_register_arm64.cc b/compiler/utils/arm64/managed_register_arm64.cc index 8977313256..47924bf99f 100644 --- a/compiler/utils/arm64/managed_register_arm64.cc +++ b/compiler/utils/arm64/managed_register_arm64.cc @@ -27,7 +27,7 @@ namespace arm64 { // * [W0, W15] // * [D0, D31] // * [S0, S31] -// static const int kNumberOfAvailableCoreRegisters = (X15 - X0) + 1; +// static const int kNumberOfAvailableXRegisters = (X15 - X0) + 1; // static const int kNumberOfAvailableWRegisters = (W15 - W0) + 1; // static const int kNumberOfAvailableDRegisters = kNumberOfDRegisters; // static const int kNumberOfAvailableSRegisters = kNumberOfSRegisters; @@ -42,22 +42,14 @@ namespace arm64 { // 63__________0 D[n] bool Arm64ManagedRegister::Overlaps(const Arm64ManagedRegister& other) const { if (IsNoRegister() || other.IsNoRegister()) return false; - if ((IsGPRegister() && other.IsGPRegister()) || - (IsFPRegister() && other.IsFPRegister())) { - return (RegNo() == other.RegNo()); - } - return false; + return (IsGPRegister() == other.IsGPRegister()) && (RegNo() == other.RegNo()); } int Arm64ManagedRegister::RegNo() const { CHECK(!IsNoRegister()); int no; - if (IsCoreRegister()) { - if (IsZeroRegister()) { - no = static_cast<int>(X31); - } else { - no = static_cast<int>(AsCoreRegister()); - } + if (IsXRegister()) { + no = static_cast<int>(AsXRegister()); } else if (IsWRegister()) { no = static_cast<int>(AsWRegister()); } else if (IsDRegister()) { @@ -71,12 +63,12 @@ int Arm64ManagedRegister::RegNo() const { } int Arm64ManagedRegister::RegIdLow() const { - CHECK(IsCoreRegister() || IsDRegister()); + CHECK(IsXRegister() || IsDRegister()); int low = RegNo(); - if (IsCoreRegister()) { - low += kNumberOfCoreRegIds; + if (IsXRegister()) { + low += kNumberOfXRegIds; } else if (IsDRegister()) { - low += kNumberOfCoreRegIds + kNumberOfWRegIds + kNumberOfDRegIds; + low += kNumberOfXRegIds + kNumberOfWRegIds + kNumberOfDRegIds; } return low; } @@ -86,7 +78,7 @@ int Arm64ManagedRegister::RegIdHigh() const { CHECK(IsWRegister() || IsSRegister()); int high = RegNo(); if (IsSRegister()) { - high += kNumberOfCoreRegIds + kNumberOfWRegIds; + high += kNumberOfXRegIds + kNumberOfWRegIds; } return high; } @@ -94,8 +86,8 @@ int Arm64ManagedRegister::RegIdHigh() const { void Arm64ManagedRegister::Print(std::ostream& os) const { if (!IsValidManagedRegister()) { os << "No Register"; - } else if (IsCoreRegister()) { - os << "XCore: " << static_cast<int>(AsCoreRegister()); + } else if (IsXRegister()) { + os << "XCore: " << static_cast<int>(AsXRegister()); } else if (IsWRegister()) { os << "WCore: " << static_cast<int>(AsWRegister()); } else if (IsDRegister()) { diff --git a/compiler/utils/arm64/managed_register_arm64.h b/compiler/utils/arm64/managed_register_arm64.h index a0f520f6a7..e1d6f3179d 100644 --- a/compiler/utils/arm64/managed_register_arm64.h +++ b/compiler/utils/arm64/managed_register_arm64.h @@ -24,29 +24,29 @@ namespace art { namespace arm64 { -const int kNumberOfCoreRegIds = kNumberOfCoreRegisters; +const int kNumberOfXRegIds = kNumberOfXRegisters; const int kNumberOfWRegIds = kNumberOfWRegisters; const int kNumberOfDRegIds = kNumberOfDRegisters; const int kNumberOfSRegIds = kNumberOfSRegisters; -const int kNumberOfRegIds = kNumberOfCoreRegIds + kNumberOfWRegIds + +const int kNumberOfRegIds = kNumberOfXRegIds + kNumberOfWRegIds + kNumberOfDRegIds + kNumberOfSRegIds; // Register ids map: -// [0..X[ core registers 64bit (enum Register) +// [0..X[ core registers 64bit (enum XRegister) // [X..W[ core registers 32bit (enum WRegister) // [W..D[ double precision VFP registers (enum DRegister) // [D..S[ single precision VFP registers (enum SRegister) // // where: -// X = kNumberOfCoreRegIds +// X = kNumberOfXRegIds // W = X + kNumberOfWRegIds // D = W + kNumberOfDRegIds // S = D + kNumberOfSRegIds // // An instance of class 'ManagedRegister' represents a single Arm64 // register. A register can be one of the following: -// * core register 64bit context (enum Register) +// * core register 64bit context (enum XRegister) // * core register 32bit context (enum WRegister) // * VFP double precision register (enum DRegister) // * VFP single precision register (enum SRegister) @@ -55,76 +55,74 @@ const int kNumberOfRegIds = kNumberOfCoreRegIds + kNumberOfWRegIds + class Arm64ManagedRegister : public ManagedRegister { public: - Register AsCoreRegister() const { - CHECK(IsCoreRegister()); - return static_cast<Register>(id_); + XRegister AsXRegister() const { + CHECK(IsXRegister()); + return static_cast<XRegister>(id_); } WRegister AsWRegister() const { CHECK(IsWRegister()); - return static_cast<WRegister>(id_ - kNumberOfCoreRegIds); + return static_cast<WRegister>(id_ - kNumberOfXRegIds); } DRegister AsDRegister() const { CHECK(IsDRegister()); - return static_cast<DRegister>(id_ - kNumberOfCoreRegIds - kNumberOfWRegIds); + return static_cast<DRegister>(id_ - kNumberOfXRegIds - kNumberOfWRegIds); } SRegister AsSRegister() const { CHECK(IsSRegister()); - return static_cast<SRegister>(id_ - kNumberOfCoreRegIds - kNumberOfWRegIds - + return static_cast<SRegister>(id_ - kNumberOfXRegIds - kNumberOfWRegIds - kNumberOfDRegIds); } - WRegister AsOverlappingCoreRegisterLow() const { + WRegister AsOverlappingWRegister() const { CHECK(IsValidManagedRegister()); - if (IsZeroRegister()) return W31; - return static_cast<WRegister>(AsCoreRegister()); + if (IsZeroRegister()) return WZR; + return static_cast<WRegister>(AsXRegister()); } - // FIXME: Find better naming. - Register AsOverlappingWRegisterCore() const { + XRegister AsOverlappingXRegister() const { CHECK(IsValidManagedRegister()); - return static_cast<Register>(AsWRegister()); + return static_cast<XRegister>(AsWRegister()); } - SRegister AsOverlappingDRegisterLow() const { + SRegister AsOverlappingSRegister() const { CHECK(IsValidManagedRegister()); return static_cast<SRegister>(AsDRegister()); } - // FIXME: Find better naming. - DRegister AsOverlappingSRegisterD() const { + DRegister AsOverlappingDRegister() const { CHECK(IsValidManagedRegister()); return static_cast<DRegister>(AsSRegister()); } - bool IsCoreRegister() const { + bool IsXRegister() const { CHECK(IsValidManagedRegister()); - return (0 <= id_) && (id_ < kNumberOfCoreRegIds); + return (0 <= id_) && (id_ < kNumberOfXRegIds); } bool IsWRegister() const { CHECK(IsValidManagedRegister()); - const int test = id_ - kNumberOfCoreRegIds; + const int test = id_ - kNumberOfXRegIds; return (0 <= test) && (test < kNumberOfWRegIds); } bool IsDRegister() const { CHECK(IsValidManagedRegister()); - const int test = id_ - (kNumberOfCoreRegIds + kNumberOfWRegIds); + const int test = id_ - (kNumberOfXRegIds + kNumberOfWRegIds); return (0 <= test) && (test < kNumberOfDRegIds); } bool IsSRegister() const { CHECK(IsValidManagedRegister()); - const int test = id_ - (kNumberOfCoreRegIds + kNumberOfWRegIds + + const int test = id_ - (kNumberOfXRegIds + kNumberOfWRegIds + kNumberOfDRegIds); return (0 <= test) && (test < kNumberOfSRegIds); } bool IsGPRegister() const { - return IsCoreRegister() || IsWRegister(); + return IsXRegister() || IsWRegister(); } bool IsFPRegister() const { @@ -134,7 +132,7 @@ class Arm64ManagedRegister : public ManagedRegister { bool IsSameType(Arm64ManagedRegister test) const { CHECK(IsValidManagedRegister() && test.IsValidManagedRegister()); return - (IsCoreRegister() && test.IsCoreRegister()) || + (IsXRegister() && test.IsXRegister()) || (IsWRegister() && test.IsWRegister()) || (IsDRegister() && test.IsDRegister()) || (IsSRegister() && test.IsSRegister()); @@ -147,29 +145,29 @@ class Arm64ManagedRegister : public ManagedRegister { void Print(std::ostream& os) const; - static Arm64ManagedRegister FromCoreRegister(Register r) { + static Arm64ManagedRegister FromXRegister(XRegister r) { CHECK_NE(r, kNoRegister); return FromRegId(r); } static Arm64ManagedRegister FromWRegister(WRegister r) { CHECK_NE(r, kNoWRegister); - return FromRegId(r + kNumberOfCoreRegIds); + return FromRegId(r + kNumberOfXRegIds); } static Arm64ManagedRegister FromDRegister(DRegister r) { CHECK_NE(r, kNoDRegister); - return FromRegId(r + (kNumberOfCoreRegIds + kNumberOfWRegIds)); + return FromRegId(r + (kNumberOfXRegIds + kNumberOfWRegIds)); } static Arm64ManagedRegister FromSRegister(SRegister r) { CHECK_NE(r, kNoSRegister); - return FromRegId(r + (kNumberOfCoreRegIds + kNumberOfWRegIds + + return FromRegId(r + (kNumberOfXRegIds + kNumberOfWRegIds + kNumberOfDRegIds)); } // Returns the X register overlapping W register r. - static Arm64ManagedRegister FromWRegisterCore(WRegister r) { + static Arm64ManagedRegister FromWRegisterX(WRegister r) { CHECK_NE(r, kNoWRegister); return FromRegId(r); } @@ -177,7 +175,7 @@ class Arm64ManagedRegister : public ManagedRegister { // Return the D register overlapping S register r. static Arm64ManagedRegister FromSRegisterD(SRegister r) { CHECK_NE(r, kNoSRegister); - return FromRegId(r + (kNumberOfCoreRegIds + kNumberOfWRegIds)); + return FromRegId(r + (kNumberOfXRegIds + kNumberOfWRegIds)); } private: @@ -186,11 +184,11 @@ class Arm64ManagedRegister : public ManagedRegister { } bool IsStackPointer() const { - return IsCoreRegister() && (id_ == SP); + return IsXRegister() && (id_ == SP); } bool IsZeroRegister() const { - return IsCoreRegister() && (id_ == XZR); + return IsXRegister() && (id_ == XZR); } int RegId() const { diff --git a/compiler/utils/arm64/managed_register_arm64_test.cc b/compiler/utils/arm64/managed_register_arm64_test.cc index f149f1bcf1..32c2e624c3 100644 --- a/compiler/utils/arm64/managed_register_arm64_test.cc +++ b/compiler/utils/arm64/managed_register_arm64_test.cc @@ -29,84 +29,84 @@ TEST(Arm64ManagedRegister, NoRegister) { } // X Register test. -TEST(Arm64ManagedRegister, CoreRegister) { - Arm64ManagedRegister reg = Arm64ManagedRegister::FromCoreRegister(X0); +TEST(Arm64ManagedRegister, XRegister) { + Arm64ManagedRegister reg = Arm64ManagedRegister::FromXRegister(X0); Arm64ManagedRegister wreg = Arm64ManagedRegister::FromWRegister(W0); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsXRegister()); EXPECT_TRUE(!reg.IsWRegister()); EXPECT_TRUE(!reg.IsDRegister()); EXPECT_TRUE(!reg.IsSRegister()); EXPECT_TRUE(reg.Overlaps(wreg)); - EXPECT_EQ(X0, reg.AsCoreRegister()); + EXPECT_EQ(X0, reg.AsXRegister()); - reg = Arm64ManagedRegister::FromCoreRegister(X1); + reg = Arm64ManagedRegister::FromXRegister(X1); wreg = Arm64ManagedRegister::FromWRegister(W1); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsXRegister()); EXPECT_TRUE(!reg.IsWRegister()); EXPECT_TRUE(!reg.IsDRegister()); EXPECT_TRUE(!reg.IsSRegister()); EXPECT_TRUE(reg.Overlaps(wreg)); - EXPECT_EQ(X1, reg.AsCoreRegister()); + EXPECT_EQ(X1, reg.AsXRegister()); - reg = Arm64ManagedRegister::FromCoreRegister(X7); + reg = Arm64ManagedRegister::FromXRegister(X7); wreg = Arm64ManagedRegister::FromWRegister(W7); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsXRegister()); EXPECT_TRUE(!reg.IsWRegister()); EXPECT_TRUE(!reg.IsDRegister()); EXPECT_TRUE(!reg.IsSRegister()); EXPECT_TRUE(reg.Overlaps(wreg)); - EXPECT_EQ(X7, reg.AsCoreRegister()); + EXPECT_EQ(X7, reg.AsXRegister()); - reg = Arm64ManagedRegister::FromCoreRegister(X15); + reg = Arm64ManagedRegister::FromXRegister(X15); wreg = Arm64ManagedRegister::FromWRegister(W15); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsXRegister()); EXPECT_TRUE(!reg.IsWRegister()); EXPECT_TRUE(!reg.IsDRegister()); EXPECT_TRUE(!reg.IsSRegister()); EXPECT_TRUE(reg.Overlaps(wreg)); - EXPECT_EQ(X15, reg.AsCoreRegister()); + EXPECT_EQ(X15, reg.AsXRegister()); - reg = Arm64ManagedRegister::FromCoreRegister(X19); + reg = Arm64ManagedRegister::FromXRegister(X19); wreg = Arm64ManagedRegister::FromWRegister(W19); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsXRegister()); EXPECT_TRUE(!reg.IsWRegister()); EXPECT_TRUE(!reg.IsDRegister()); EXPECT_TRUE(!reg.IsSRegister()); EXPECT_TRUE(reg.Overlaps(wreg)); - EXPECT_EQ(X19, reg.AsCoreRegister()); + EXPECT_EQ(X19, reg.AsXRegister()); - reg = Arm64ManagedRegister::FromCoreRegister(X16); + reg = Arm64ManagedRegister::FromXRegister(X16); wreg = Arm64ManagedRegister::FromWRegister(W16); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsXRegister()); EXPECT_TRUE(!reg.IsWRegister()); EXPECT_TRUE(!reg.IsDRegister()); EXPECT_TRUE(!reg.IsSRegister()); EXPECT_TRUE(reg.Overlaps(wreg)); - EXPECT_EQ(IP0, reg.AsCoreRegister()); + EXPECT_EQ(IP0, reg.AsXRegister()); - reg = Arm64ManagedRegister::FromCoreRegister(SP); + reg = Arm64ManagedRegister::FromXRegister(SP); wreg = Arm64ManagedRegister::FromWRegister(WZR); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsXRegister()); EXPECT_TRUE(!reg.IsWRegister()); EXPECT_TRUE(!reg.IsDRegister()); EXPECT_TRUE(!reg.IsSRegister()); - EXPECT_TRUE(reg.Overlaps(wreg)); - EXPECT_EQ(SP, reg.AsCoreRegister()); + EXPECT_TRUE(!reg.Overlaps(wreg)); + EXPECT_EQ(SP, reg.AsXRegister()); } // W register test. TEST(Arm64ManagedRegister, WRegister) { Arm64ManagedRegister reg = Arm64ManagedRegister::FromWRegister(W0); - Arm64ManagedRegister xreg = Arm64ManagedRegister::FromCoreRegister(X0); + Arm64ManagedRegister xreg = Arm64ManagedRegister::FromXRegister(X0); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsXRegister()); EXPECT_TRUE(reg.IsWRegister()); EXPECT_TRUE(!reg.IsDRegister()); EXPECT_TRUE(!reg.IsSRegister()); @@ -114,9 +114,9 @@ TEST(Arm64ManagedRegister, WRegister) { EXPECT_EQ(W0, reg.AsWRegister()); reg = Arm64ManagedRegister::FromWRegister(W5); - xreg = Arm64ManagedRegister::FromCoreRegister(X5); + xreg = Arm64ManagedRegister::FromXRegister(X5); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsXRegister()); EXPECT_TRUE(reg.IsWRegister()); EXPECT_TRUE(!reg.IsDRegister()); EXPECT_TRUE(!reg.IsSRegister()); @@ -124,9 +124,9 @@ TEST(Arm64ManagedRegister, WRegister) { EXPECT_EQ(W5, reg.AsWRegister()); reg = Arm64ManagedRegister::FromWRegister(W6); - xreg = Arm64ManagedRegister::FromCoreRegister(X6); + xreg = Arm64ManagedRegister::FromXRegister(X6); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsXRegister()); EXPECT_TRUE(reg.IsWRegister()); EXPECT_TRUE(!reg.IsDRegister()); EXPECT_TRUE(!reg.IsSRegister()); @@ -134,9 +134,9 @@ TEST(Arm64ManagedRegister, WRegister) { EXPECT_EQ(W6, reg.AsWRegister()); reg = Arm64ManagedRegister::FromWRegister(W18); - xreg = Arm64ManagedRegister::FromCoreRegister(X18); + xreg = Arm64ManagedRegister::FromXRegister(X18); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsXRegister()); EXPECT_TRUE(reg.IsWRegister()); EXPECT_TRUE(!reg.IsDRegister()); EXPECT_TRUE(!reg.IsSRegister()); @@ -144,9 +144,9 @@ TEST(Arm64ManagedRegister, WRegister) { EXPECT_EQ(W18, reg.AsWRegister()); reg = Arm64ManagedRegister::FromWRegister(W29); - xreg = Arm64ManagedRegister::FromCoreRegister(FP); + xreg = Arm64ManagedRegister::FromXRegister(FP); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsXRegister()); EXPECT_TRUE(reg.IsWRegister()); EXPECT_TRUE(!reg.IsDRegister()); EXPECT_TRUE(!reg.IsSRegister()); @@ -154,14 +154,13 @@ TEST(Arm64ManagedRegister, WRegister) { EXPECT_EQ(W29, reg.AsWRegister()); reg = Arm64ManagedRegister::FromWRegister(WZR); - xreg = Arm64ManagedRegister::FromCoreRegister(SP); + xreg = Arm64ManagedRegister::FromXRegister(SP); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsXRegister()); EXPECT_TRUE(reg.IsWRegister()); EXPECT_TRUE(!reg.IsDRegister()); EXPECT_TRUE(!reg.IsSRegister()); - EXPECT_TRUE(reg.Overlaps(xreg)); - EXPECT_EQ(W31, reg.AsWRegister()); + EXPECT_TRUE(!reg.Overlaps(xreg)); } // D Register test. @@ -169,49 +168,49 @@ TEST(Arm64ManagedRegister, DRegister) { Arm64ManagedRegister reg = Arm64ManagedRegister::FromDRegister(D0); Arm64ManagedRegister sreg = Arm64ManagedRegister::FromSRegister(S0); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsXRegister()); EXPECT_TRUE(!reg.IsWRegister()); EXPECT_TRUE(reg.IsDRegister()); EXPECT_TRUE(!reg.IsSRegister()); EXPECT_TRUE(reg.Overlaps(sreg)); EXPECT_EQ(D0, reg.AsDRegister()); - EXPECT_EQ(S0, reg.AsOverlappingDRegisterLow()); + EXPECT_EQ(S0, reg.AsOverlappingSRegister()); EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromDRegister(D0))); reg = Arm64ManagedRegister::FromDRegister(D1); sreg = Arm64ManagedRegister::FromSRegister(S1); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsXRegister()); EXPECT_TRUE(!reg.IsWRegister()); EXPECT_TRUE(reg.IsDRegister()); EXPECT_TRUE(!reg.IsSRegister()); EXPECT_TRUE(reg.Overlaps(sreg)); EXPECT_EQ(D1, reg.AsDRegister()); - EXPECT_EQ(S1, reg.AsOverlappingDRegisterLow()); + EXPECT_EQ(S1, reg.AsOverlappingSRegister()); EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromDRegister(D1))); reg = Arm64ManagedRegister::FromDRegister(D20); sreg = Arm64ManagedRegister::FromSRegister(S20); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsXRegister()); EXPECT_TRUE(!reg.IsWRegister()); EXPECT_TRUE(reg.IsDRegister()); EXPECT_TRUE(!reg.IsSRegister()); EXPECT_TRUE(reg.Overlaps(sreg)); EXPECT_EQ(D20, reg.AsDRegister()); - EXPECT_EQ(S20, reg.AsOverlappingDRegisterLow()); + EXPECT_EQ(S20, reg.AsOverlappingSRegister()); EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromDRegister(D20))); reg = Arm64ManagedRegister::FromDRegister(D31); sreg = Arm64ManagedRegister::FromSRegister(S31); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsXRegister()); EXPECT_TRUE(!reg.IsWRegister()); EXPECT_TRUE(reg.IsDRegister()); EXPECT_TRUE(!reg.IsSRegister()); EXPECT_TRUE(reg.Overlaps(sreg)); EXPECT_EQ(D31, reg.AsDRegister()); - EXPECT_EQ(S31, reg.AsOverlappingDRegisterLow()); + EXPECT_EQ(S31, reg.AsOverlappingSRegister()); EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromDRegister(D31))); } @@ -220,101 +219,90 @@ TEST(Arm64ManagedRegister, SRegister) { Arm64ManagedRegister reg = Arm64ManagedRegister::FromSRegister(S0); Arm64ManagedRegister dreg = Arm64ManagedRegister::FromDRegister(D0); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsXRegister()); EXPECT_TRUE(!reg.IsWRegister()); EXPECT_TRUE(reg.IsSRegister()); EXPECT_TRUE(!reg.IsDRegister()); EXPECT_TRUE(reg.Overlaps(dreg)); EXPECT_EQ(S0, reg.AsSRegister()); - EXPECT_EQ(D0, reg.AsOverlappingSRegisterD()); + EXPECT_EQ(D0, reg.AsOverlappingDRegister()); EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromSRegister(S0))); reg = Arm64ManagedRegister::FromSRegister(S5); dreg = Arm64ManagedRegister::FromDRegister(D5); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsXRegister()); EXPECT_TRUE(!reg.IsWRegister()); EXPECT_TRUE(reg.IsSRegister()); EXPECT_TRUE(!reg.IsDRegister()); EXPECT_TRUE(reg.Overlaps(dreg)); EXPECT_EQ(S5, reg.AsSRegister()); - EXPECT_EQ(D5, reg.AsOverlappingSRegisterD()); + EXPECT_EQ(D5, reg.AsOverlappingDRegister()); EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromSRegister(S5))); reg = Arm64ManagedRegister::FromSRegister(S7); dreg = Arm64ManagedRegister::FromDRegister(D7); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsXRegister()); EXPECT_TRUE(!reg.IsWRegister()); EXPECT_TRUE(reg.IsSRegister()); EXPECT_TRUE(!reg.IsDRegister()); EXPECT_TRUE(reg.Overlaps(dreg)); EXPECT_EQ(S7, reg.AsSRegister()); - EXPECT_EQ(D7, reg.AsOverlappingSRegisterD()); + EXPECT_EQ(D7, reg.AsOverlappingDRegister()); EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromSRegister(S7))); reg = Arm64ManagedRegister::FromSRegister(S31); dreg = Arm64ManagedRegister::FromDRegister(D31); EXPECT_TRUE(!reg.IsNoRegister()); - EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsXRegister()); EXPECT_TRUE(!reg.IsWRegister()); EXPECT_TRUE(reg.IsSRegister()); EXPECT_TRUE(!reg.IsDRegister()); EXPECT_TRUE(reg.Overlaps(dreg)); EXPECT_EQ(S31, reg.AsSRegister()); - EXPECT_EQ(D31, reg.AsOverlappingSRegisterD()); + EXPECT_EQ(D31, reg.AsOverlappingDRegister()); EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromSRegister(S31))); } TEST(Arm64ManagedRegister, Equals) { ManagedRegister no_reg = ManagedRegister::NoRegister(); EXPECT_TRUE(no_reg.Equals(Arm64ManagedRegister::NoRegister())); - EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); - EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromXRegister(X0))); + EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromXRegister(X1))); EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromWRegister(W0))); EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromWRegister(W1))); EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromDRegister(D0))); EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromSRegister(S0))); - Arm64ManagedRegister reg_X0 = Arm64ManagedRegister::FromCoreRegister(X0); + Arm64ManagedRegister reg_X0 = Arm64ManagedRegister::FromXRegister(X0); EXPECT_TRUE(!reg_X0.Equals(Arm64ManagedRegister::NoRegister())); - EXPECT_TRUE(reg_X0.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); - EXPECT_TRUE(!reg_X0.Equals(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(reg_X0.Equals(Arm64ManagedRegister::FromXRegister(X0))); + EXPECT_TRUE(!reg_X0.Equals(Arm64ManagedRegister::FromXRegister(X1))); EXPECT_TRUE(!reg_X0.Equals(Arm64ManagedRegister::FromWRegister(W0))); EXPECT_TRUE(!reg_X0.Equals(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(!reg_X0.Equals(Arm64ManagedRegister::FromDRegister(D0))); - Arm64ManagedRegister reg_X1 = Arm64ManagedRegister::FromCoreRegister(X1); + Arm64ManagedRegister reg_X1 = Arm64ManagedRegister::FromXRegister(X1); EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::NoRegister())); - EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); - EXPECT_TRUE(reg_X1.Equals(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromXRegister(X0))); + EXPECT_TRUE(reg_X1.Equals(Arm64ManagedRegister::FromXRegister(X1))); EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromWRegister(W1))); EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromDRegister(D0))); EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromDRegister(D1))); EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromSRegister(S1))); - Arm64ManagedRegister reg_X31 = Arm64ManagedRegister::FromCoreRegister(X31); - EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::NoRegister())); - EXPECT_TRUE(reg_X31.Equals(Arm64ManagedRegister::FromCoreRegister(SP))); - EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromCoreRegister(XZR))); - EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromWRegister(W31))); - EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromWRegister(WZR))); - EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromSRegister(S0))); - EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromDRegister(D0))); - - Arm64ManagedRegister reg_SP = Arm64ManagedRegister::FromCoreRegister(SP); + Arm64ManagedRegister reg_SP = Arm64ManagedRegister::FromXRegister(SP); EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::NoRegister())); - EXPECT_TRUE(reg_SP.Equals(Arm64ManagedRegister::FromCoreRegister(X31))); - EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromCoreRegister(XZR))); - EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromWRegister(W31))); + EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromXRegister(XZR))); EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromDRegister(D0))); Arm64ManagedRegister reg_W8 = Arm64ManagedRegister::FromWRegister(W8); EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::NoRegister())); - EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); - EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromCoreRegister(X8))); + EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromXRegister(X0))); + EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromXRegister(X8))); EXPECT_TRUE(reg_W8.Equals(Arm64ManagedRegister::FromWRegister(W8))); EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromDRegister(D0))); EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromSRegister(S0))); @@ -323,8 +311,8 @@ TEST(Arm64ManagedRegister, Equals) { Arm64ManagedRegister reg_W12 = Arm64ManagedRegister::FromWRegister(W12); EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::NoRegister())); - EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); - EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromCoreRegister(X8))); + EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromXRegister(X0))); + EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromXRegister(X8))); EXPECT_TRUE(reg_W12.Equals(Arm64ManagedRegister::FromWRegister(W12))); EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromDRegister(D0))); EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromSRegister(S0))); @@ -333,8 +321,8 @@ TEST(Arm64ManagedRegister, Equals) { Arm64ManagedRegister reg_S0 = Arm64ManagedRegister::FromSRegister(S0); EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::NoRegister())); - EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); - EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromXRegister(X0))); + EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromXRegister(X1))); EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromWRegister(W0))); EXPECT_TRUE(reg_S0.Equals(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromSRegister(S1))); @@ -343,8 +331,8 @@ TEST(Arm64ManagedRegister, Equals) { Arm64ManagedRegister reg_S1 = Arm64ManagedRegister::FromSRegister(S1); EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::NoRegister())); - EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); - EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromXRegister(X0))); + EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromXRegister(X1))); EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromWRegister(W0))); EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(reg_S1.Equals(Arm64ManagedRegister::FromSRegister(S1))); @@ -353,8 +341,8 @@ TEST(Arm64ManagedRegister, Equals) { Arm64ManagedRegister reg_S31 = Arm64ManagedRegister::FromSRegister(S31); EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::NoRegister())); - EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); - EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromXRegister(X0))); + EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromXRegister(X1))); EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromWRegister(W0))); EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(reg_S31.Equals(Arm64ManagedRegister::FromSRegister(S31))); @@ -363,7 +351,7 @@ TEST(Arm64ManagedRegister, Equals) { Arm64ManagedRegister reg_D0 = Arm64ManagedRegister::FromDRegister(D0); EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::NoRegister())); - EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); + EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::FromXRegister(X0))); EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::FromWRegister(W1))); EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::FromSRegister(S0))); @@ -373,8 +361,8 @@ TEST(Arm64ManagedRegister, Equals) { Arm64ManagedRegister reg_D15 = Arm64ManagedRegister::FromDRegister(D15); EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::NoRegister())); - EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); - EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromXRegister(X0))); + EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromXRegister(X1))); EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromWRegister(W0))); EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromSRegister(S31))); @@ -384,17 +372,17 @@ TEST(Arm64ManagedRegister, Equals) { } TEST(Arm64ManagedRegister, Overlaps) { - Arm64ManagedRegister reg = Arm64ManagedRegister::FromCoreRegister(X0); + Arm64ManagedRegister reg = Arm64ManagedRegister::FromXRegister(X0); Arm64ManagedRegister reg_o = Arm64ManagedRegister::FromWRegister(W0); - EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X0))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(SP))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromXRegister(X0))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(SP))); EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W0))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); - EXPECT_EQ(X0, reg_o.AsOverlappingWRegisterCore()); - EXPECT_EQ(W0, reg.AsOverlappingCoreRegisterLow()); + EXPECT_EQ(X0, reg_o.AsOverlappingXRegister()); + EXPECT_EQ(W0, reg.AsOverlappingWRegister()); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); @@ -406,17 +394,17 @@ TEST(Arm64ManagedRegister, Overlaps) { EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D7))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15))); - reg = Arm64ManagedRegister::FromCoreRegister(X10); + reg = Arm64ManagedRegister::FromXRegister(X10); reg_o = Arm64ManagedRegister::FromWRegister(W10); - EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X10))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(SP))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromXRegister(X10))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(SP))); EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W10))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); - EXPECT_EQ(X10, reg_o.AsOverlappingWRegisterCore()); - EXPECT_EQ(W10, reg.AsOverlappingCoreRegisterLow()); + EXPECT_EQ(X10, reg_o.AsOverlappingXRegister()); + EXPECT_EQ(W10, reg.AsOverlappingWRegister()); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); @@ -428,17 +416,17 @@ TEST(Arm64ManagedRegister, Overlaps) { EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D7))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15))); - reg = Arm64ManagedRegister::FromCoreRegister(IP1); + reg = Arm64ManagedRegister::FromXRegister(IP1); reg_o = Arm64ManagedRegister::FromWRegister(W17); - EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X17))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(SP))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromXRegister(X17))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(SP))); EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W17))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); - EXPECT_EQ(X17, reg_o.AsOverlappingWRegisterCore()); - EXPECT_EQ(W17, reg.AsOverlappingCoreRegisterLow()); + EXPECT_EQ(X17, reg_o.AsOverlappingXRegister()); + EXPECT_EQ(W17, reg.AsOverlappingWRegister()); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); @@ -450,19 +438,15 @@ TEST(Arm64ManagedRegister, Overlaps) { EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D7))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15))); - reg = Arm64ManagedRegister::FromCoreRegister(XZR); + reg = Arm64ManagedRegister::FromXRegister(XZR); reg_o = Arm64ManagedRegister::FromWRegister(WZR); - EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X31))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); - EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(SP))); - EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(SP))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W19))); - EXPECT_EQ(X31, reg_o.AsOverlappingWRegisterCore()); - EXPECT_EQ(SP, reg_o.AsOverlappingWRegisterCore()); - EXPECT_NE(XZR, reg_o.AsOverlappingWRegisterCore()); - EXPECT_EQ(W31, reg.AsOverlappingCoreRegisterLow()); + EXPECT_NE(SP, reg_o.AsOverlappingXRegister()); + EXPECT_EQ(XZR, reg_o.AsOverlappingXRegister()); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); @@ -474,17 +458,13 @@ TEST(Arm64ManagedRegister, Overlaps) { EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D7))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15))); - reg = Arm64ManagedRegister::FromCoreRegister(SP); + reg = Arm64ManagedRegister::FromXRegister(SP); reg_o = Arm64ManagedRegister::FromWRegister(WZR); - EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X31))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15))); - EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); - EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31))); - EXPECT_EQ(X31, reg_o.AsOverlappingWRegisterCore()); - EXPECT_EQ(W31, reg.AsOverlappingCoreRegisterLow()); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); @@ -497,15 +477,15 @@ TEST(Arm64ManagedRegister, Overlaps) { EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15))); reg = Arm64ManagedRegister::FromWRegister(W1); - reg_o = Arm64ManagedRegister::FromCoreRegister(X1); + reg_o = Arm64ManagedRegister::FromXRegister(X1); EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); - EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X15))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31))); - EXPECT_EQ(W1, reg_o.AsOverlappingCoreRegisterLow()); - EXPECT_EQ(X1, reg.AsOverlappingWRegisterCore()); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W30))); + EXPECT_EQ(W1, reg_o.AsOverlappingWRegister()); + EXPECT_EQ(X1, reg.AsOverlappingXRegister()); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); @@ -518,15 +498,15 @@ TEST(Arm64ManagedRegister, Overlaps) { EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15))); reg = Arm64ManagedRegister::FromWRegister(W21); - reg_o = Arm64ManagedRegister::FromCoreRegister(X21); + reg_o = Arm64ManagedRegister::FromXRegister(X21); EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W21))); - EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X21))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromXRegister(X21))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X15))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31))); - EXPECT_EQ(W21, reg_o.AsOverlappingCoreRegisterLow()); - EXPECT_EQ(X21, reg.AsOverlappingWRegisterCore()); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W30))); + EXPECT_EQ(W21, reg_o.AsOverlappingWRegister()); + EXPECT_EQ(X21, reg.AsOverlappingXRegister()); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); @@ -541,15 +521,15 @@ TEST(Arm64ManagedRegister, Overlaps) { reg = Arm64ManagedRegister::FromSRegister(S1); reg_o = Arm64ManagedRegister::FromDRegister(D1); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X31))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X30))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X15))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31))); - EXPECT_EQ(S1, reg_o.AsOverlappingDRegisterLow()); - EXPECT_EQ(D1, reg.AsOverlappingSRegisterD()); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W30))); + EXPECT_EQ(S1, reg_o.AsOverlappingSRegister()); + EXPECT_EQ(D1, reg.AsOverlappingDRegister()); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); @@ -564,15 +544,15 @@ TEST(Arm64ManagedRegister, Overlaps) { reg = Arm64ManagedRegister::FromSRegister(S15); reg_o = Arm64ManagedRegister::FromDRegister(D15); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X31))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X30))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X15))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31))); - EXPECT_EQ(S15, reg_o.AsOverlappingDRegisterLow()); - EXPECT_EQ(D15, reg.AsOverlappingSRegisterD()); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W30))); + EXPECT_EQ(S15, reg_o.AsOverlappingSRegister()); + EXPECT_EQ(D15, reg.AsOverlappingDRegister()); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromSRegister(S15))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); @@ -587,15 +567,15 @@ TEST(Arm64ManagedRegister, Overlaps) { reg = Arm64ManagedRegister::FromDRegister(D15); reg_o = Arm64ManagedRegister::FromSRegister(S15); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X31))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X30))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromXRegister(X15))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); - EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31))); - EXPECT_EQ(S15, reg.AsOverlappingDRegisterLow()); - EXPECT_EQ(D15, reg_o.AsOverlappingSRegisterD()); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W30))); + EXPECT_EQ(S15, reg.AsOverlappingSRegister()); + EXPECT_EQ(D15, reg_o.AsOverlappingDRegister()); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromSRegister(S15))); EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); @@ -642,9 +622,6 @@ TEST(Arm64ManagedRegister, VixlRegisters) { EXPECT_TRUE(vixl::x28.Is(Arm64Assembler::reg_x(X28))); EXPECT_TRUE(vixl::x29.Is(Arm64Assembler::reg_x(X29))); EXPECT_TRUE(vixl::x30.Is(Arm64Assembler::reg_x(X30))); - // FIXME: Take a look here. - EXPECT_TRUE(vixl::sp.Is(Arm64Assembler::reg_x(X31))); - EXPECT_TRUE(!vixl::x31.Is(Arm64Assembler::reg_x(X31))); EXPECT_TRUE(vixl::x18.Is(Arm64Assembler::reg_x(TR))); EXPECT_TRUE(vixl::ip0.Is(Arm64Assembler::reg_x(IP0))); @@ -686,8 +663,9 @@ TEST(Arm64ManagedRegister, VixlRegisters) { EXPECT_TRUE(vixl::w28.Is(Arm64Assembler::reg_w(W28))); EXPECT_TRUE(vixl::w29.Is(Arm64Assembler::reg_w(W29))); EXPECT_TRUE(vixl::w30.Is(Arm64Assembler::reg_w(W30))); - EXPECT_TRUE(vixl::w31.Is(Arm64Assembler::reg_w(W31))); + EXPECT_TRUE(vixl::w31.Is(Arm64Assembler::reg_w(WZR))); EXPECT_TRUE(vixl::wzr.Is(Arm64Assembler::reg_w(WZR))); + EXPECT_TRUE(vixl::wsp.Is(Arm64Assembler::reg_w(WSP))); // D Registers. EXPECT_TRUE(vixl::d0.Is(Arm64Assembler::reg_d(D0))); diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h index 91b8d8ab9a..2b0c94c9e0 100644 --- a/compiler/utils/assembler.h +++ b/compiler/utils/assembler.h @@ -118,6 +118,7 @@ class Label { friend class arm::ArmAssembler; friend class arm::Arm32Assembler; friend class arm::Thumb2Assembler; + friend class arm64::Arm64Assembler; friend class mips::MipsAssembler; friend class x86::X86Assembler; friend class x86_64::X86_64Assembler; diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h index 37429131ef..91237ae910 100644 --- a/compiler/utils/assembler_test.h +++ b/compiler/utils/assembler_test.h @@ -24,7 +24,6 @@ #include <cstdio> #include <cstdlib> #include <fstream> -#include <iostream> #include <iterator> #include <sys/stat.h> @@ -118,9 +117,8 @@ class AssemblerTest : public testing::Test { std::vector<int64_t> imms = CreateImmediateValues(imm_bytes); for (auto reg : registers) { for (int64_t imm : imms) { - Imm* new_imm = CreateImmediate(imm); - (assembler_.get()->*f)(*reg, *new_imm); - delete new_imm; + Imm new_imm = CreateImmediate(imm); + (assembler_.get()->*f)(*reg, new_imm); std::string base = fmt; size_t reg_index = base.find("{reg}"); @@ -154,9 +152,8 @@ class AssemblerTest : public testing::Test { std::string str; std::vector<int64_t> imms = CreateImmediateValues(imm_bytes); for (int64_t imm : imms) { - Imm* new_imm = CreateImmediate(imm); - (assembler_.get()->*f)(*new_imm); - delete new_imm; + Imm new_imm = CreateImmediate(imm); + (assembler_.get()->*f)(new_imm); std::string base = fmt; size_t imm_index = base.find("{imm}"); @@ -333,7 +330,7 @@ class AssemblerTest : public testing::Test { } // Create an immediate from the specific value. - virtual Imm* CreateImmediate(int64_t imm_value) = 0; + virtual Imm CreateImmediate(int64_t imm_value) = 0; private: // Driver() assembles and compares the results. If the results are not equal and we have a @@ -373,7 +370,7 @@ class AssemblerTest : public testing::Test { } } else { // This will output the assembly. - EXPECT_EQ(*data, *res.code) << "Outputs (and disassembly) not identical."; + EXPECT_EQ(*res.code, *data) << "Outputs (and disassembly) not identical."; } } } diff --git a/compiler/utils/scoped_arena_allocator.cc b/compiler/utils/scoped_arena_allocator.cc index aeb2f768dd..26161501b3 100644 --- a/compiler/utils/scoped_arena_allocator.cc +++ b/compiler/utils/scoped_arena_allocator.cc @@ -115,10 +115,18 @@ ScopedArenaAllocator::ScopedArenaAllocator(ArenaStack* arena_stack) } ScopedArenaAllocator::~ScopedArenaAllocator() { - Reset(); + DoReset(); } void ScopedArenaAllocator::Reset() { + DoReset(); + // If this allocator was Create()d, we need to move the arena_stack_->top_ptr_ past *this. + if (mark_ptr_ == reinterpret_cast<uint8_t*>(this)) { + arena_stack_->top_ptr_ = mark_ptr_ + RoundUp(sizeof(ScopedArenaAllocator), 8); + } +} + +void ScopedArenaAllocator::DoReset() { DebugStackReference::CheckTop(); DebugStackRefCounter::CheckNoRefs(); arena_stack_->UpdatePeakStatsAndRestore(*this); diff --git a/compiler/utils/scoped_arena_allocator.h b/compiler/utils/scoped_arena_allocator.h index 62ea3302a3..523f158969 100644 --- a/compiler/utils/scoped_arena_allocator.h +++ b/compiler/utils/scoped_arena_allocator.h @@ -132,6 +132,8 @@ class ScopedArenaAllocator uint8_t* mark_ptr_; uint8_t* mark_end_; + void DoReset(); + template <typename T> friend class ScopedArenaAllocatorAdapter; diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index c7eada34f5..b5bf31bbd6 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -29,7 +29,7 @@ namespace art { namespace x86 { -class Immediate { +class Immediate : public ValueObject { public: explicit Immediate(int32_t value) : value_(value) {} @@ -47,7 +47,7 @@ class Immediate { }; -class Operand { +class Operand : public ValueObject { public: uint8_t mod() const { return (encoding_at(0) >> 6) & 3; @@ -129,8 +129,6 @@ class Operand { } friend class X86Assembler; - - DISALLOW_COPY_AND_ASSIGN(Operand); }; @@ -168,7 +166,6 @@ class Address : public Operand { } } - Address(Register index, ScaleFactor scale, int32_t disp) { CHECK_NE(index, ESP); // Illegal addressing mode. SetModRM(0, ESP); @@ -205,14 +202,12 @@ class Address : public Operand { private: Address() {} - - DISALLOW_COPY_AND_ASSIGN(Address); }; class X86Assembler FINAL : public Assembler { public: - explicit X86Assembler() {} + explicit X86Assembler() : cfi_cfa_offset_(0), cfi_pc_(0) {} virtual ~X86Assembler() {} /* diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index 75823e336b..f4c9862f93 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -317,7 +317,7 @@ void X86_64Assembler::movaps(XmmRegister dst, XmmRegister src) { EmitOptionalRex32(dst, src); EmitUint8(0x0F); EmitUint8(0x28); - EmitXmmRegisterOperand(src.LowBits(), dst); + EmitXmmRegisterOperand(dst.LowBits(), src); } @@ -354,7 +354,7 @@ void X86_64Assembler::movss(XmmRegister dst, XmmRegister src) { void X86_64Assembler::movd(XmmRegister dst, CpuRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); - EmitOptionalRex32(dst, src); + EmitRex64(dst, src); EmitUint8(0x0F); EmitUint8(0x6E); EmitOperand(dst.LowBits(), Operand(src)); @@ -364,7 +364,7 @@ void X86_64Assembler::movd(XmmRegister dst, CpuRegister src) { void X86_64Assembler::movd(CpuRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); - EmitOptionalRex32(src, dst); + EmitRex64(src, dst); EmitUint8(0x0F); EmitUint8(0x7E); EmitOperand(src.LowBits(), Operand(dst)); @@ -1238,6 +1238,34 @@ void X86_64Assembler::imull(CpuRegister reg, const Address& address) { } +void X86_64Assembler::imulq(CpuRegister dst, CpuRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitRex64(dst, src); + EmitUint8(0x0F); + EmitUint8(0xAF); + EmitRegisterOperand(dst.LowBits(), src.LowBits()); +} + + +void X86_64Assembler::imulq(CpuRegister reg, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + CHECK(imm.is_int32()); // imulq only supports 32b immediate. + EmitRex64(reg); + EmitUint8(0x69); + EmitOperand(reg.LowBits(), Operand(reg)); + EmitImmediate(imm); +} + + +void X86_64Assembler::imulq(CpuRegister reg, const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitRex64(reg, address); + EmitUint8(0x0F); + EmitUint8(0xAF); + EmitOperand(reg.LowBits(), address); +} + + void X86_64Assembler::imull(CpuRegister reg) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitOptionalRex32(reg); @@ -1270,7 +1298,6 @@ void X86_64Assembler::mull(const Address& address) { } - void X86_64Assembler::shll(CpuRegister reg, const Immediate& imm) { EmitGenericShift(false, 4, reg, imm); } @@ -1721,6 +1748,10 @@ void X86_64Assembler::EmitRex64(CpuRegister dst, CpuRegister src) { EmitOptionalRex(false, true, dst.NeedsRex(), false, src.NeedsRex()); } +void X86_64Assembler::EmitRex64(XmmRegister dst, CpuRegister src) { + EmitOptionalRex(false, true, dst.NeedsRex(), false, src.NeedsRex()); +} + void X86_64Assembler::EmitRex64(CpuRegister dst, const Operand& operand) { uint8_t rex = 0x48 | operand.rex(); // REX.W000 if (dst.NeedsRex()) { diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 1d9eba446a..92b81ec2e7 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -36,7 +36,7 @@ namespace x86_64 { // // Note: As we support cross-compilation, the value type must be int64_t. Please be aware of // conversion rules in expressions regarding negation, especially size_t on 32b. -class Immediate { +class Immediate : public ValueObject { public: explicit Immediate(int64_t value) : value_(value) {} @@ -54,12 +54,10 @@ class Immediate { private: const int64_t value_; - - DISALLOW_COPY_AND_ASSIGN(Immediate); }; -class Operand { +class Operand : public ValueObject { public: uint8_t mod() const { return (encoding_at(0) >> 6) & 3; @@ -157,8 +155,6 @@ class Operand { } friend class X86_64Assembler; - - DISALLOW_COPY_AND_ASSIGN(Operand); }; @@ -247,8 +243,6 @@ class Address : public Operand { private: Address() {} - - DISALLOW_COPY_AND_ASSIGN(Address); }; @@ -436,6 +430,10 @@ class X86_64Assembler FINAL : public Assembler { void imull(CpuRegister reg, const Immediate& imm); void imull(CpuRegister reg, const Address& address); + void imulq(CpuRegister dst, CpuRegister src); + void imulq(CpuRegister reg, const Immediate& imm); + void imulq(CpuRegister reg, const Address& address); + void imull(CpuRegister reg); void imull(const Address& address); @@ -662,6 +660,7 @@ class X86_64Assembler FINAL : public Assembler { void EmitRex64(CpuRegister reg); void EmitRex64(CpuRegister dst, CpuRegister src); void EmitRex64(CpuRegister dst, const Operand& operand); + void EmitRex64(XmmRegister dst, CpuRegister src); // Emit a REX prefix to normalize byte registers plus necessary register bit encodings. void EmitOptionalByteRegNormalizingRex32(CpuRegister dst, CpuRegister src); @@ -688,7 +687,7 @@ inline void X86_64Assembler::EmitInt64(int64_t value) { inline void X86_64Assembler::EmitRegisterOperand(uint8_t rm, uint8_t reg) { CHECK_GE(rm, 0); CHECK_LT(rm, 8); - buffer_.Emit<uint8_t>(0xC0 + (rm << 3) + reg); + buffer_.Emit<uint8_t>((0xC0 | (reg & 7)) + (rm << 3)); } inline void X86_64Assembler::EmitXmmRegisterOperand(uint8_t rm, XmmRegister reg) { diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index 7a48b638e7..18c5cbcbc6 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -72,8 +72,8 @@ class AssemblerX86_64Test : public AssemblerTest<x86_64::X86_64Assembler, x86_64 return registers_; } - x86_64::Immediate* CreateImmediate(int64_t imm_value) OVERRIDE { - return new x86_64::Immediate(imm_value); + x86_64::Immediate CreateImmediate(int64_t imm_value) OVERRIDE { + return x86_64::Immediate(imm_value); } private: @@ -112,6 +112,9 @@ TEST_F(AssemblerX86_64Test, AddqImm) { DriverStr(RepeatRI(&x86_64::X86_64Assembler::addq, 4U, "addq ${imm}, %{reg}"), "addqi"); } +TEST_F(AssemblerX86_64Test, ImulqRegs) { + DriverStr(RepeatRR(&x86_64::X86_64Assembler::imulq, "imulq %{reg2}, %{reg1}"), "imulq"); +} TEST_F(AssemblerX86_64Test, SubqRegs) { DriverStr(RepeatRR(&x86_64::X86_64Assembler::subq, "subq %{reg2}, %{reg1}"), "subq"); @@ -131,6 +134,32 @@ TEST_F(AssemblerX86_64Test, XorqImm) { DriverStr(RepeatRI(&x86_64::X86_64Assembler::xorq, 4U, "xorq ${imm}, %{reg}"), "xorqi"); } +TEST_F(AssemblerX86_64Test, Movaps) { + GetAssembler()->movaps(x86_64::XmmRegister(x86_64::XMM0), x86_64::XmmRegister(x86_64::XMM8)); + DriverStr("movaps %xmm8, %xmm0", "movaps"); +} + +TEST_F(AssemblerX86_64Test, Movd) { + GetAssembler()->movd(x86_64::XmmRegister(x86_64::XMM0), x86_64::CpuRegister(x86_64::R11)); + GetAssembler()->movd(x86_64::XmmRegister(x86_64::XMM0), x86_64::CpuRegister(x86_64::RAX)); + GetAssembler()->movd(x86_64::XmmRegister(x86_64::XMM8), x86_64::CpuRegister(x86_64::R11)); + GetAssembler()->movd(x86_64::XmmRegister(x86_64::XMM8), x86_64::CpuRegister(x86_64::RAX)); + GetAssembler()->movd(x86_64::CpuRegister(x86_64::R11), x86_64::XmmRegister(x86_64::XMM0)); + GetAssembler()->movd(x86_64::CpuRegister(x86_64::RAX), x86_64::XmmRegister(x86_64::XMM0)); + GetAssembler()->movd(x86_64::CpuRegister(x86_64::R11), x86_64::XmmRegister(x86_64::XMM8)); + GetAssembler()->movd(x86_64::CpuRegister(x86_64::RAX), x86_64::XmmRegister(x86_64::XMM8)); + const char* expected = + "movd %r11, %xmm0\n" + "movd %rax, %xmm0\n" + "movd %r11, %xmm8\n" + "movd %rax, %xmm8\n" + "movd %xmm0, %r11\n" + "movd %xmm0, %rax\n" + "movd %xmm8, %r11\n" + "movd %xmm8, %rax\n"; + DriverStr(expected, "movd"); +} + TEST_F(AssemblerX86_64Test, Movl) { GetAssembler()->movl(x86_64::CpuRegister(x86_64::R8), x86_64::CpuRegister(x86_64::R11)); GetAssembler()->movl(x86_64::CpuRegister(x86_64::RAX), x86_64::CpuRegister(x86_64::R11)); diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk index a06b5c5254..0ef20d60de 100644 --- a/dalvikvm/Android.mk +++ b/dalvikvm/Android.mk @@ -27,7 +27,9 @@ LOCAL_CPP_EXTENSION := cc LOCAL_SRC_FILES := dalvikvm.cc LOCAL_CFLAGS := $(dalvikvm_cflags) LOCAL_C_INCLUDES := art/runtime -LOCAL_SHARED_LIBRARIES := libdl libnativehelper +LOCAL_SHARED_LIBRARIES := libdl liblog libnativehelper +LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain +LOCAL_LDFLAGS := -Wl,--version-script,art/sigchainlib/version-script.txt -Wl,--export-dynamic LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common.mk LOCAL_MULTILIB := both @@ -54,7 +56,12 @@ LOCAL_SRC_FILES := dalvikvm.cc LOCAL_CFLAGS := $(dalvikvm_cflags) LOCAL_C_INCLUDES := art/runtime LOCAL_SHARED_LIBRARIES := libnativehelper +LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain LOCAL_LDFLAGS := -ldl -lpthread +# Mac OS linker doesn't understand --export-dynamic. +ifneq ($(HOST_OS),darwin) + LOCAL_LDFLAGS += -Wl,--export-dynamic +endif LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common.mk LOCAL_IS_HOST_MODULE := true diff --git a/dalvikvm/dalvikvm.cc b/dalvikvm/dalvikvm.cc index 67794c8e05..7839aa8d4c 100644 --- a/dalvikvm/dalvikvm.cc +++ b/dalvikvm/dalvikvm.cc @@ -152,7 +152,7 @@ static int dalvikvm(int argc, char** argv) { } if (curr_opt > option_count) { - fprintf(stderr, "curr_opt(%d) >= option_count(%d)\n", curr_opt, option_count); + fprintf(stderr, "curr_opt(%d) > option_count(%d)\n", curr_opt, option_count); abort(); return EXIT_FAILURE; } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index d782aeb108..98712cd9cc 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -30,6 +30,10 @@ #include <sys/utsname.h> #endif +#define ATRACE_TAG ATRACE_TAG_DALVIK +#include "cutils/trace.h" + +#include "base/dumpable.h" #include "base/stl_util.h" #include "base/stringpiece.h" #include "base/timing_logger.h" @@ -90,8 +94,7 @@ static void UsageError(const char* fmt, ...) { va_end(ap); } -static void Usage(const char* fmt, ...) NO_RETURN; -static void Usage(const char* fmt, ...) { +[[noreturn]] static void Usage(const char* fmt, ...) { va_list ap; va_start(ap, fmt); UsageErrorV(fmt, ap); @@ -252,13 +255,23 @@ class Dex2Oat { const CompilerOptions& compiler_options, Compiler::Kind compiler_kind, InstructionSet instruction_set, - InstructionSetFeatures instruction_set_features, + const InstructionSetFeatures* instruction_set_features, VerificationResults* verification_results, DexFileToMethodInlinerMap* method_inliner_map, size_t thread_count) SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) { CHECK(verification_results != nullptr); CHECK(method_inliner_map != nullptr); + if (instruction_set == kRuntimeISA) { + std::unique_ptr<const InstructionSetFeatures> runtime_features( + InstructionSetFeatures::FromCppDefines()); + if (!instruction_set_features->Equals(runtime_features.get())) { + LOG(WARNING) << "Mismatch between dex2oat instruction set features (" + << *instruction_set_features << ") and those of dex2oat executable (" + << *runtime_features <<") for the command line:\n" + << CommandLine(); + } + } std::unique_ptr<Dex2Oat> dex2oat(new Dex2Oat(&compiler_options, compiler_kind, instruction_set, @@ -483,7 +496,7 @@ class Dex2Oat { explicit Dex2Oat(const CompilerOptions* compiler_options, Compiler::Kind compiler_kind, InstructionSet instruction_set, - InstructionSetFeatures instruction_set_features, + const InstructionSetFeatures* instruction_set_features, VerificationResults* verification_results, DexFileToMethodInlinerMap* method_inliner_map, size_t thread_count) @@ -528,7 +541,7 @@ class Dex2Oat { static void OpenClassPathFiles(const std::string& class_path, std::vector<const DexFile*>& dex_files) { std::vector<std::string> parsed; - Split(class_path, ':', parsed); + Split(class_path, ':', &parsed); // Take Locks::mutator_lock_ so that lock ordering on the ClassLinker::dex_lock_ is maintained. ScopedObjectAccess soa(Thread::Current()); for (size_t i = 0; i < parsed.size(); ++i) { @@ -557,7 +570,7 @@ class Dex2Oat { const Compiler::Kind compiler_kind_; const InstructionSet instruction_set_; - const InstructionSetFeatures instruction_set_features_; + const InstructionSetFeatures* const instruction_set_features_; VerificationResults* const verification_results_; DexFileToMethodInlinerMap* const method_inliner_map_; @@ -663,7 +676,7 @@ class WatchDog { Message('W', message); } - static void Fatal(const std::string& message) NO_RETURN { + [[noreturn]] static void Fatal(const std::string& message) { Message('F', message); exit(1); } @@ -729,38 +742,6 @@ class WatchDog { const unsigned int WatchDog::kWatchDogWarningSeconds; const unsigned int WatchDog::kWatchDogTimeoutSeconds; -// Given a set of instruction features from the build, parse it. The -// input 'str' is a comma separated list of feature names. Parse it and -// return the InstructionSetFeatures object. -static InstructionSetFeatures ParseFeatureList(std::string str) { - InstructionSetFeatures result; - typedef std::vector<std::string> FeatureList; - FeatureList features; - Split(str, ',', features); - for (FeatureList::iterator i = features.begin(); i != features.end(); i++) { - std::string feature = Trim(*i); - if (feature == "default") { - // Nothing to do. - } else if (feature == "div") { - // Supports divide instruction. - result.SetHasDivideInstruction(true); - } else if (feature == "nodiv") { - // Turn off support for divide instruction. - result.SetHasDivideInstruction(false); - } else if (feature == "lpae") { - // Supports Large Physical Address Extension. - result.SetHasLpae(true); - } else if (feature == "nolpae") { - // Turn off support for Large Physical Address Extension. - result.SetHasLpae(false); - } else { - Usage("Unknown instruction set feature: '%s'", feature.c_str()); - } - } - // others... - return result; -} - void ParseStringAfterChar(const std::string& s, char c, std::string* parsed_value) { std::string::size_type colon = s.find(c); if (colon == std::string::npos) { @@ -849,17 +830,20 @@ static int dex2oat(int argc, char** argv) { ? Compiler::kPortable : Compiler::kQuick; const char* compiler_filter_string = nullptr; + bool compile_pic = false; int huge_method_threshold = CompilerOptions::kDefaultHugeMethodThreshold; int large_method_threshold = CompilerOptions::kDefaultLargeMethodThreshold; int small_method_threshold = CompilerOptions::kDefaultSmallMethodThreshold; int tiny_method_threshold = CompilerOptions::kDefaultTinyMethodThreshold; int num_dex_methods_threshold = CompilerOptions::kDefaultNumDexMethodsThreshold; + std::vector<std::string> verbose_methods; - // Take the default set of instruction features from the build. - InstructionSetFeatures instruction_set_features = - ParseFeatureList(Runtime::GetDefaultInstructionSetFeatures()); - + // Initialize ISA and ISA features to default values. InstructionSet instruction_set = kRuntimeISA; + std::string error_msg; + std::unique_ptr<const InstructionSetFeatures> instruction_set_features( + InstructionSetFeatures::FromFeatureString(kNone, "default", &error_msg)); + CHECK(instruction_set_features.get() != nullptr) << error_msg; // Profile file to use std::string profile_file; @@ -962,9 +946,20 @@ static int dex2oat(int argc, char** argv) { } else if (instruction_set_str == "x86_64") { instruction_set = kX86_64; } + } else if (option.starts_with("--instruction-set-variant=")) { + StringPiece str = option.substr(strlen("--instruction-set-variant=")).data(); + instruction_set_features.reset( + InstructionSetFeatures::FromVariant(instruction_set, str.as_string(), &error_msg)); + if (instruction_set_features.get() == nullptr) { + Usage("%s", error_msg.c_str()); + } } else if (option.starts_with("--instruction-set-features=")) { StringPiece str = option.substr(strlen("--instruction-set-features=")).data(); - instruction_set_features = ParseFeatureList(str.as_string()); + instruction_set_features.reset( + InstructionSetFeatures::FromFeatureString(instruction_set, str.as_string(), &error_msg)); + if (instruction_set_features.get() == nullptr) { + Usage("%s", error_msg.c_str()); + } } else if (option.starts_with("--compiler-backend=")) { StringPiece backend_str = option.substr(strlen("--compiler-backend=")).data(); if (backend_str == "Quick") { @@ -976,6 +971,8 @@ static int dex2oat(int argc, char** argv) { } } else if (option.starts_with("--compiler-filter=")) { compiler_filter_string = option.substr(strlen("--compiler-filter=")).data(); + } else if (option == "--compile-pic") { + compile_pic = true; } else if (option.starts_with("--huge-method-max=")) { const char* threshold = option.substr(strlen("--huge-method-max=")).data(); if (!ParseInt(threshold, &huge_method_threshold)) { @@ -1066,6 +1063,11 @@ static int dex2oat(int argc, char** argv) { include_patch_information = true; } else if (option == "--no-include-patch-information") { include_patch_information = false; + } else if (option.starts_with("--verbose-methods=")) { + // TODO: rather than switch off compiler logging, make all VLOG(compiler) messages conditional + // on having verbost methods. + gLogVerbosity.compiler = false; + Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods); } else { Usage("Unknown argument %s", option.data()); } @@ -1160,6 +1162,13 @@ static int dex2oat(int argc, char** argv) { oat_unstripped += oat_filename; } + // If no instruction set feature was given, use the default one for the target + // instruction set. + if (instruction_set_features->GetInstructionSet() == kNone) { + instruction_set_features.reset( + InstructionSetFeatures::FromFeatureString(instruction_set, "default", &error_msg)); + } + if (compiler_filter_string == nullptr) { if (instruction_set == kMips64) { // TODO: fix compiler for Mips64. @@ -1214,24 +1223,25 @@ static int dex2oat(int argc, char** argv) { PassDriverMEOpts::PrintPassOptions(); } - std::unique_ptr<CompilerOptions> compiler_options(new CompilerOptions(compiler_filter, - huge_method_threshold, - large_method_threshold, - small_method_threshold, - tiny_method_threshold, - num_dex_methods_threshold, - generate_gdb_information, - include_patch_information, - top_k_profile_threshold, - include_debug_symbols, - implicit_null_checks, - implicit_so_checks, - implicit_suspend_checks + std::unique_ptr<CompilerOptions> compiler_options( + new CompilerOptions(compiler_filter, + huge_method_threshold, + large_method_threshold, + small_method_threshold, + tiny_method_threshold, + num_dex_methods_threshold, + generate_gdb_information, + include_patch_information, + top_k_profile_threshold, + include_debug_symbols, + implicit_null_checks, + implicit_so_checks, + implicit_suspend_checks, + compile_pic, #ifdef ART_SEA_IR_MODE - , compiler_options.sea_ir_ = - true; + true, #endif - )); // NOLINT(whitespace/parens) + verbose_methods.empty() ? nullptr : &verbose_methods)); // Done with usage checks, enable watchdog if requested WatchDog watch_dog(watch_dog_enabled); @@ -1263,6 +1273,7 @@ static int dex2oat(int argc, char** argv) { RuntimeOptions runtime_options; std::vector<const DexFile*> boot_class_path; + art::MemMap::Init(); // For ZipEntry::ExtractToMemMap. if (boot_image_option.empty()) { size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, boot_class_path); if (failure_count > 0) { @@ -1283,8 +1294,7 @@ static int dex2oat(int argc, char** argv) { QuickCompilerCallbacks callbacks(verification_results.get(), &method_inliner_map); runtime_options.push_back(std::make_pair("compilercallbacks", &callbacks)); runtime_options.push_back( - std::make_pair("imageinstructionset", - reinterpret_cast<const void*>(GetInstructionSetString(instruction_set)))); + std::make_pair("imageinstructionset", GetInstructionSetString(instruction_set))); Dex2Oat* p_dex2oat; if (!Dex2Oat::Create(&p_dex2oat, @@ -1292,7 +1302,7 @@ static int dex2oat(int argc, char** argv) { *compiler_options, compiler_kind, instruction_set, - instruction_set_features, + instruction_set_features.get(), verification_results.get(), &method_inliner_map, thread_count)) { diff --git a/disassembler/Android.mk b/disassembler/Android.mk index d67c16943e..eb3b024934 100644 --- a/disassembler/Android.mk +++ b/disassembler/Android.mk @@ -63,6 +63,7 @@ define build-libart-disassembler $(call set-target-local-cflags-vars,$(2)) else # host LOCAL_CLANG := $(ART_HOST_CLANG) + LOCAL_LDLIBS := $(ART_HOST_LDLIBS) LOCAL_CFLAGS += $(ART_HOST_CFLAGS) ifeq ($$(art_ndebug_or_debug),debug) LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS) diff --git a/disassembler/disassembler.cc b/disassembler/disassembler.cc index c97bf64c5d..bf68204d75 100644 --- a/disassembler/disassembler.cc +++ b/disassembler/disassembler.cc @@ -16,7 +16,7 @@ #include "disassembler.h" -#include <iostream> +#include <ostream> #include "base/logging.h" #include "base/stringprintf.h" diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc index ac883fe4e3..ee652b34f7 100644 --- a/disassembler/disassembler_arm.cc +++ b/disassembler/disassembler_arm.cc @@ -18,7 +18,8 @@ #include <inttypes.h> -#include <iostream> +#include <ostream> +#include <sstream> #include "base/logging.h" #include "base/stringprintf.h" diff --git a/disassembler/disassembler_arm64.cc b/disassembler/disassembler_arm64.cc index 5d0c2189cc..229ac9755f 100644 --- a/disassembler/disassembler_arm64.cc +++ b/disassembler/disassembler_arm64.cc @@ -18,7 +18,7 @@ #include <inttypes.h> -#include <iostream> +#include <ostream> #include "base/logging.h" #include "base/stringprintf.h" @@ -27,15 +27,11 @@ namespace art { namespace arm64 { -static uint32_t ReadU32(const uint8_t* ptr) { - return *((const uint32_t*)ptr); -} - size_t DisassemblerArm64::Dump(std::ostream& os, const uint8_t* begin) { - uint32_t instruction = ReadU32(begin); - decoder.Decode(reinterpret_cast<vixl::Instruction*>(&instruction)); + const vixl::Instruction* instr = reinterpret_cast<const vixl::Instruction*>(begin); + decoder.Decode(instr); os << FormatInstructionPointer(begin) - << StringPrintf(": %08x\t%s\n", instruction, disasm.GetOutput()); + << StringPrintf(": %08x\t%s\n", instr->InstructionBits(), disasm.GetOutput()); return vixl::kInstructionSize; } diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc index bd5fac7377..97c06f178d 100644 --- a/disassembler/disassembler_mips.cc +++ b/disassembler/disassembler_mips.cc @@ -16,7 +16,8 @@ #include "disassembler_mips.h" -#include <iostream> +#include <ostream> +#include <sstream> #include "base/logging.h" #include "base/stringprintf.h" diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc index 63a74c7240..e12559f010 100644 --- a/disassembler/disassembler_x86.cc +++ b/disassembler/disassembler_x86.cc @@ -16,12 +16,14 @@ #include "disassembler_x86.h" -#include <iostream> +#include <inttypes.h> + +#include <ostream> +#include <sstream> #include "base/logging.h" #include "base/stringprintf.h" #include "thread.h" -#include <inttypes.h> namespace art { namespace x86 { diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 1f2c0aa1cc..14accac26c 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -396,8 +396,13 @@ class OatDumper { os << "INSTRUCTION SET:\n"; os << oat_header.GetInstructionSet() << "\n\n"; - os << "INSTRUCTION SET FEATURES:\n"; - os << oat_header.GetInstructionSetFeatures().GetFeatureString() << "\n\n"; + { + std::unique_ptr<const InstructionSetFeatures> features( + InstructionSetFeatures::FromBitmap(oat_header.GetInstructionSet(), + oat_header.GetInstructionSetFeaturesBitmap())); + os << "INSTRUCTION SET FEATURES:\n"; + os << features->GetFeatureString() << "\n\n"; + } os << "DEX FILE COUNT:\n"; os << oat_header.GetDexFileCount() << "\n\n"; @@ -1493,7 +1498,7 @@ class ImageDumper { const void* GetQuickOatCodeBegin(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const void* quick_code = m->GetEntryPointFromQuickCompiledCode(); - if (quick_code == Runtime::Current()->GetClassLinker()->GetQuickResolutionTrampoline()) { + if (Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(quick_code)) { quick_code = oat_dumper_->GetQuickOatCode(m); } if (oat_dumper_->GetInstructionSet() == kThumb2) { @@ -2225,6 +2230,8 @@ static int oatdump(int argc, char** argv) { runtime.reset(StartRuntime(args.boot_image_location_, args.image_location_, args.instruction_set_)); + } else { + MemMap::Init(); } if (args.oat_filename_ != nullptr) { diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 2d165b05da..8e5af53e66 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -24,9 +24,11 @@ #include <string> #include <vector> +#include "base/dumpable.h" #include "base/scoped_flock.h" #include "base/stringpiece.h" #include "base/stringprintf.h" +#include "base/unix_file/fd_file.h" #include "elf_utils.h" #include "elf_file.h" #include "elf_file_impl.h" @@ -513,7 +515,7 @@ bool PatchOat::PatchOatHeader(ElfFileImpl* oat_file) { } bool PatchOat::PatchElf() { - if (oat_file_->is_elf64_) + if (oat_file_->Is64Bit()) return PatchElf<ElfFileImpl64>(oat_file_->GetImpl64()); else return PatchElf<ElfFileImpl32>(oat_file_->GetImpl32()); @@ -531,13 +533,12 @@ bool PatchOat::PatchElf(ElfFileImpl* oat_file) { } bool need_fixup = false; - for (unsigned int i = 0; i < oat_file->GetProgramHeaderNum(); i++) { + for (unsigned int i = 0; i < oat_file->GetProgramHeaderNum(); ++i) { auto hdr = oat_file->GetProgramHeader(i); - if (hdr->p_vaddr != 0 && hdr->p_vaddr != hdr->p_offset) { - need_fixup = true; - } - if (hdr->p_paddr != 0 && hdr->p_paddr != hdr->p_offset) { + if ((hdr->p_vaddr != 0 && hdr->p_vaddr != hdr->p_offset) || + (hdr->p_paddr != 0 && hdr->p_paddr != hdr->p_offset)) { need_fixup = true; + break; } } if (!need_fixup) { @@ -644,8 +645,7 @@ static void UsageError(const char* fmt, ...) { va_end(ap); } -static void Usage(const char *fmt, ...) NO_RETURN; -static void Usage(const char *fmt, ...) { +[[noreturn]] static void Usage(const char *fmt, ...) { va_list ap; va_start(ap, fmt); UsageErrorV(fmt, ap); @@ -759,6 +759,7 @@ static File* CreateOrOpen(const char* name, bool* created) { static int patchoat(int argc, char **argv) { InitLogging(argv); + MemMap::Init(); const bool debug = kIsDebugBuild; orig_argc = argc; orig_argv = argv; @@ -801,7 +802,7 @@ static int patchoat(int argc, char **argv) { bool dump_timings = kIsDebugBuild; bool lock_output = true; - for (int i = 0; i < argc; i++) { + for (int i = 0; i < argc; ++i) { const StringPiece option(argv[i]); const bool log_options = false; if (log_options) { diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h index 7dd95f5d6b..fd36ad59a3 100644 --- a/patchoat/patchoat.h +++ b/patchoat/patchoat.h @@ -52,7 +52,8 @@ class PatchOat { private: // Takes ownership only of the ElfFile. All other pointers are only borrowed. PatchOat(ElfFile* oat_file, off_t delta, TimingLogger* timings) - : oat_file_(oat_file), delta_(delta), timings_(timings) {} + : oat_file_(oat_file), image_(nullptr), bitmap_(nullptr), heap_(nullptr), delta_(delta), + timings_(timings) {} PatchOat(MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap, MemMap* heap, off_t delta, TimingLogger* timings) : image_(image), bitmap_(bitmap), heap_(heap), @@ -106,21 +107,22 @@ class PatchOat { void operator() (mirror::Class* cls, mirror::Reference* ref) const EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); private: - PatchOat* patcher_; - mirror::Object* copy_; + PatchOat* const patcher_; + mirror::Object* const copy_; }; // The elf file we are patching. std::unique_ptr<ElfFile> oat_file_; // A mmap of the image we are patching. This is modified. - const MemMap* image_; + const MemMap* const image_; + // The bitmap over the image within the heap we are patching. This is not modified. + gc::accounting::ContinuousSpaceBitmap* const bitmap_; // The heap we are patching. This is not modified. - gc::accounting::ContinuousSpaceBitmap* bitmap_; - // The heap we are patching. This is not modified. - const MemMap* heap_; + const MemMap* const heap_; // The amount we are changing the offset by. - off_t delta_; - TimingLogger* timings_; + const off_t delta_; + // Timing splits. + TimingLogger* const timings_; DISALLOW_IMPLICIT_CONSTRUCTORS(PatchOat); }; diff --git a/runtime/Android.mk b/runtime/Android.mk index e9544761bd..0ef0fefd61 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -209,7 +209,6 @@ LIBART_HOST_LDFLAGS := LIBART_TARGET_SRC_FILES := \ $(LIBART_COMMON_SRC_FILES) \ - base/logging_android.cc \ jdwp/jdwp_adb.cc \ monitor_android.cc \ runtime_android.cc \ @@ -218,11 +217,11 @@ LIBART_TARGET_SRC_FILES := \ LIBART_TARGET_SRC_FILES_arm := \ arch/arm/context_arm.cc.arm \ arch/arm/entrypoints_init_arm.cc \ + arch/arm/instruction_set_features_arm.S \ arch/arm/jni_entrypoints_arm.S \ arch/arm/memcmp16_arm.S \ arch/arm/portable_entrypoints_arm.S \ arch/arm/quick_entrypoints_arm.S \ - arch/arm/arm_sdiv.S \ arch/arm/thread_arm.cc \ arch/arm/fault_handler_arm.cc @@ -282,7 +281,6 @@ endif # TARGET_ARCH != mips64 LIBART_HOST_SRC_FILES := \ $(LIBART_COMMON_SRC_FILES) \ - base/logging_linux.cc \ monitor_linux.cc \ runtime_linux.cc \ thread_linux.cc @@ -317,7 +315,7 @@ LIBART_ENUM_OPERATOR_OUT_HEADER_FILES := \ thread_state.h \ verifier/method_verifier.h -LIBART_CFLAGS := +LIBART_CFLAGS := -DBUILDING_LIBART=1 ifeq ($(ART_USE_PORTABLE_COMPILER),true) LIBART_CFLAGS += -DART_USE_PORTABLE_COMPILER=1 endif @@ -328,6 +326,29 @@ else LIBART_CFLAGS += -DUSE_JEMALLOC endif +# Default dex2oat instruction set features. +LIBART_HOST_DEFAULT_INSTRUCTION_SET_FEATURES := default +LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES := default +2ND_LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES := default +ifeq ($(DEX2OAT_TARGET_ARCH),arm) + ifneq (,$(filter $(DEX2OAT_TARGET_CPU_VARIANT),cortex-a15 krait denver)) + LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES := lpae,div + else + ifneq (,$(filter $(DEX2OAT_TARGET_CPU_VARIANT),cortex-a7)) + LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES := div + endif + endif +endif +ifeq ($(2ND_DEX2OAT_TARGET_ARCH),arm) + ifneq (,$(filter $(DEX2OAT_TARGET_CPU_VARIANT),cortex-a15 krait denver)) + 2ND_LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES := lpae,div + else + ifneq (,$(filter $(DEX2OAT_TARGET_CPU_VARIANT),cortex-a7)) + 2ND_LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES := div + endif + endif +endif + # $(1): target or host # $(2): ndebug or debug define build-libart @@ -346,6 +367,13 @@ define build-libart art_ndebug_or_debug := $(2) include $$(CLEAR_VARS) + # Clang assembler has problem with macros in asm_support_x86.S, http://b/17443165, + # on linux. Yet sdk on mac needs integrated assembler. + ifeq ($$(HOST_OS),darwin) + LOCAL_CLANG_ASFLAGS += -integrated-as + else + LOCAL_CLANG_ASFLAGS += -no-integrated-as + endif LOCAL_CPP_EXTENSION := $$(ART_CPP_EXTENSION) ifeq ($$(art_ndebug_or_debug),ndebug) LOCAL_MODULE := libart @@ -393,6 +421,9 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT ifeq ($$(art_target_or_host),target) $$(eval $$(call set-target-local-clang-vars)) $$(eval $$(call set-target-local-cflags-vars,$(2))) + LOCAL_CFLAGS_$(DEX2OAT_TARGET_ARCH) += -DART_DEFAULT_INSTRUCTION_SET_FEATURES="$(LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES)" + LOCAL_CFLAGS_$(2ND_DEX2OAT_TARGET_ARCH) += -DART_DEFAULT_INSTRUCTION_SET_FEATURES="$(2ND_LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES)" + # TODO: Loop with ifeq, ART_TARGET_CLANG ifneq ($$(ART_TARGET_CLANG_$$(TARGET_ARCH)),true) LOCAL_SRC_FILES_$$(TARGET_ARCH) += $$(LIBART_GCC_ONLY_SRC_FILES) @@ -401,18 +432,25 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT LOCAL_SRC_FILES_$$(TARGET_2ND_ARCH) += $$(LIBART_GCC_ONLY_SRC_FILES) endif else # host - LOCAL_CLANG := $$(ART_HOST_CLANG) - ifeq ($$(ART_HOST_CLANG),false) + ifneq ($$(ART_HOST_CLANG),true) + # Add files only built with GCC on the host. LOCAL_SRC_FILES += $$(LIBART_GCC_ONLY_SRC_FILES) endif + LOCAL_CLANG := $$(ART_HOST_CLANG) + LOCAL_LDLIBS := $$(ART_HOST_LDLIBS) + LOCAL_LDLIBS += -ldl -lpthread + ifeq ($$(HOST_OS),linux) + LOCAL_LDLIBS += -lrt + endif LOCAL_CFLAGS += $$(ART_HOST_CFLAGS) + LOCAL_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES="$(LIBART_HOST_DEFAULT_INSTRUCTION_SET_FEATURES)" + ifeq ($$(art_ndebug_or_debug),debug) LOCAL_CFLAGS += $$(ART_HOST_DEBUG_CFLAGS) - LOCAL_LDLIBS += $$(ART_HOST_DEBUG_LDLIBS) - LOCAL_STATIC_LIBRARIES := libgtest_host else LOCAL_CFLAGS += $$(ART_HOST_NON_DEBUG_CFLAGS) endif + LOCAL_MULTILIB := both endif LOCAL_C_INCLUDES += $$(ART_C_INCLUDES) @@ -427,11 +465,6 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT else # host LOCAL_STATIC_LIBRARIES += libcutils libziparchive-host libz libutils LOCAL_SHARED_LIBRARIES += libsigchain - LOCAL_LDLIBS += -ldl -lpthread - ifeq ($$(HOST_OS),linux) - LOCAL_LDLIBS += -lrt - endif - LOCAL_MULTILIB := both endif ifeq ($$(ART_USE_PORTABLE_COMPILER),true) include $$(LLVM_GEN_INTRINSICS_MK) @@ -442,7 +475,7 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT endif endif LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk -# LOCAL_ADDITIONAL_DEPENDENCIES += $$(LOCAL_PATH)/Android.mk + LOCAL_ADDITIONAL_DEPENDENCIES += $$(LOCAL_PATH)/Android.mk ifeq ($$(art_target_or_host),target) LOCAL_MODULE_TARGET_ARCH := $$(ART_TARGET_SUPPORTED_ARCH) @@ -488,6 +521,9 @@ endif LOCAL_PATH := LIBART_COMMON_SRC_FILES := LIBART_GCC_ONLY_SRC_FILES := +LIBART_HOST_DEFAULT_INSTRUCTION_SET_FEATURES := +LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES := +2ND_LIBART_TARGET_DEFAULT_INSTRUCTION_SET_FEATURES := LIBART_TARGET_LDFLAGS := LIBART_HOST_LDFLAGS := LIBART_TARGET_SRC_FILES := diff --git a/runtime/arch/arch_test.cc b/runtime/arch/arch_test.cc index 5220dc3ca9..42bf8fb124 100644 --- a/runtime/arch/arch_test.cc +++ b/runtime/arch/arch_test.cc @@ -43,398 +43,98 @@ class ArchTest : public CommonRuntimeTest { } }; +// Common tests are declared next to the constants. +#define ADD_TEST_EQ(x, y) EXPECT_EQ(x, y); +#include "asm_support.h" -TEST_F(ArchTest, ARM) { -#include "arch/arm/asm_support_arm.h" -#undef ART_RUNTIME_ARCH_ARM_ASM_SUPPORT_ARM_H_ - - -#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE - CheckFrameSize(InstructionSet::kArm, Runtime::kSaveAll, FRAME_SIZE_SAVE_ALL_CALLEE_SAVE); -#else - LOG(WARNING) << "No frame size for SaveAll"; -#endif -#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE - CheckFrameSize(InstructionSet::kArm, Runtime::kRefsOnly, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE); -#else - LOG(WARNING) << "No frame size for RefsOnly"; -#endif -#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - CheckFrameSize(InstructionSet::kArm, Runtime::kRefsAndArgs, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE); -#else - LOG(WARNING) << "No frame size for RefsAndArgs"; -#endif - +TEST_F(ArchTest, CheckCommonOffsetsAndSizes) { + CheckAsmSupportOffsetsAndSizes(); +} -#ifdef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET -#undef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET -#endif -#ifdef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET -#undef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET -#endif -#ifdef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET -#undef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET -#endif -#ifdef THREAD_SELF_OFFSET -#undef THREAD_SELF_OFFSET -#endif -#ifdef THREAD_CARD_TABLE_OFFSET -#undef THREAD_CARD_TABLE_OFFSET -#endif -#ifdef THREAD_EXCEPTION_OFFSET -#undef THREAD_EXCEPTION_OFFSET -#endif -#ifdef THREAD_ID_OFFSET -#undef THREAD_ID_OFFSET -#endif -#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE +// Grab architecture specific constants. +namespace arm { +#include "arch/arm/asm_support_arm.h" +static constexpr size_t kFrameSizeSaveAllCalleeSave = FRAME_SIZE_SAVE_ALL_CALLEE_SAVE; #undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE -#endif -#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE +static constexpr size_t kFrameSizeRefsOnlyCalleeSave = FRAME_SIZE_REFS_ONLY_CALLEE_SAVE; #undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE -#endif -#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE +static constexpr size_t kFrameSizeRefsAndArgsCalleeSave = FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE; #undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE -#endif -#ifdef HEAP_REFERENCE_SIZE -#undef HEAP_REFERENCE_SIZE -#endif } - -TEST_F(ArchTest, ARM64) { +namespace arm64 { #include "arch/arm64/asm_support_arm64.h" -#undef ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_H_ - - -#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE - CheckFrameSize(InstructionSet::kArm64, Runtime::kSaveAll, FRAME_SIZE_SAVE_ALL_CALLEE_SAVE); -#else - LOG(WARNING) << "No frame size for SaveAll"; -#endif -#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE - CheckFrameSize(InstructionSet::kArm64, Runtime::kRefsOnly, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE); -#else - LOG(WARNING) << "No frame size for RefsOnly"; -#endif -#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - CheckFrameSize(InstructionSet::kArm64, Runtime::kRefsAndArgs, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE); -#else - LOG(WARNING) << "No frame size for RefsAndArgs"; -#endif - - -#ifdef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET -#undef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET -#endif -#ifdef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET -#undef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET -#endif -#ifdef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET -#undef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET -#endif -#ifdef THREAD_SELF_OFFSET -#undef THREAD_SELF_OFFSET -#endif -#ifdef THREAD_CARD_TABLE_OFFSET -#undef THREAD_CARD_TABLE_OFFSET -#endif -#ifdef THREAD_EXCEPTION_OFFSET -#undef THREAD_EXCEPTION_OFFSET -#endif -#ifdef THREAD_ID_OFFSET -#undef THREAD_ID_OFFSET -#endif -#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE +static constexpr size_t kFrameSizeSaveAllCalleeSave = FRAME_SIZE_SAVE_ALL_CALLEE_SAVE; #undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE -#endif -#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE +static constexpr size_t kFrameSizeRefsOnlyCalleeSave = FRAME_SIZE_REFS_ONLY_CALLEE_SAVE; #undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE -#endif -#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE +static constexpr size_t kFrameSizeRefsAndArgsCalleeSave = FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE; #undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE -#endif -#ifdef HEAP_REFERENCE_SIZE -#undef HEAP_REFERENCE_SIZE -#endif } - -TEST_F(ArchTest, MIPS) { +namespace mips { #include "arch/mips/asm_support_mips.h" -#undef ART_RUNTIME_ARCH_MIPS_ASM_SUPPORT_MIPS_H_ - - -#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE - CheckFrameSize(InstructionSet::kMips, Runtime::kSaveAll, FRAME_SIZE_SAVE_ALL_CALLEE_SAVE); -#else - LOG(WARNING) << "No frame size for SaveAll"; -#endif -#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE - CheckFrameSize(InstructionSet::kMips, Runtime::kRefsOnly, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE); -#else - LOG(WARNING) << "No frame size for RefsOnly"; -#endif -#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - CheckFrameSize(InstructionSet::kMips, Runtime::kRefsAndArgs, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE); -#else - LOG(WARNING) << "No frame size for RefsAndArgs"; -#endif - - -#ifdef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET -#undef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET -#endif -#ifdef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET -#undef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET -#endif -#ifdef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET -#undef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET -#endif -#ifdef THREAD_SELF_OFFSET -#undef THREAD_SELF_OFFSET -#endif -#ifdef THREAD_CARD_TABLE_OFFSET -#undef THREAD_CARD_TABLE_OFFSET -#endif -#ifdef THREAD_EXCEPTION_OFFSET -#undef THREAD_EXCEPTION_OFFSET -#endif -#ifdef THREAD_ID_OFFSET -#undef THREAD_ID_OFFSET -#endif -#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE +static constexpr size_t kFrameSizeSaveAllCalleeSave = FRAME_SIZE_SAVE_ALL_CALLEE_SAVE; #undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE -#endif -#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE +static constexpr size_t kFrameSizeRefsOnlyCalleeSave = FRAME_SIZE_REFS_ONLY_CALLEE_SAVE; #undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE -#endif -#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE +static constexpr size_t kFrameSizeRefsAndArgsCalleeSave = FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE; #undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE -#endif -#ifdef HEAP_REFERENCE_SIZE -#undef HEAP_REFERENCE_SIZE -#endif } - -TEST_F(ArchTest, X86) { +namespace x86 { #include "arch/x86/asm_support_x86.h" -#undef ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_ - - -#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE - CheckFrameSize(InstructionSet::kX86, Runtime::kSaveAll, FRAME_SIZE_SAVE_ALL_CALLEE_SAVE); -#else - LOG(WARNING) << "No frame size for SaveAll"; -#endif -#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE - CheckFrameSize(InstructionSet::kX86, Runtime::kRefsOnly, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE); -#else - LOG(WARNING) << "No frame size for RefsOnly"; -#endif -#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - CheckFrameSize(InstructionSet::kX86, Runtime::kRefsAndArgs, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE); -#else - LOG(WARNING) << "No frame size for RefsAndArgs"; -#endif - - -#ifdef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET -#undef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET -#endif -#ifdef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET -#undef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET -#endif -#ifdef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET -#undef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET -#endif -#ifdef THREAD_SELF_OFFSET -#undef THREAD_SELF_OFFSET -#endif -#ifdef THREAD_CARD_TABLE_OFFSET -#undef THREAD_CARD_TABLE_OFFSET -#endif -#ifdef THREAD_EXCEPTION_OFFSET -#undef THREAD_EXCEPTION_OFFSET -#endif -#ifdef THREAD_ID_OFFSET -#undef THREAD_ID_OFFSET -#endif -#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE +static constexpr size_t kFrameSizeSaveAllCalleeSave = FRAME_SIZE_SAVE_ALL_CALLEE_SAVE; #undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE -#endif -#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE +static constexpr size_t kFrameSizeRefsOnlyCalleeSave = FRAME_SIZE_REFS_ONLY_CALLEE_SAVE; #undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE -#endif -#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE +static constexpr size_t kFrameSizeRefsAndArgsCalleeSave = FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE; #undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE -#endif -#ifdef HEAP_REFERENCE_SIZE -#undef HEAP_REFERENCE_SIZE -#endif } - -TEST_F(ArchTest, X86_64) { +namespace x86_64 { #include "arch/x86_64/asm_support_x86_64.h" -#undef ART_RUNTIME_ARCH_X86_64_ASM_SUPPORT_X86_64_H_ - - -#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE - CheckFrameSize(InstructionSet::kX86_64, Runtime::kSaveAll, FRAME_SIZE_SAVE_ALL_CALLEE_SAVE); -#else - LOG(WARNING) << "No frame size for SaveAll"; -#endif -#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE - CheckFrameSize(InstructionSet::kX86_64, Runtime::kRefsOnly, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE); -#else - LOG(WARNING) << "No frame size for RefsOnly"; -#endif -#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - CheckFrameSize(InstructionSet::kX86_64, Runtime::kRefsAndArgs, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE); -#else - LOG(WARNING) << "No frame size for RefsAndArgs"; -#endif - - -#ifdef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET -#undef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET -#endif -#ifdef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET -#undef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET -#endif -#ifdef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET -#undef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET -#endif -#ifdef THREAD_SELF_OFFSET -#undef THREAD_SELF_OFFSET -#endif -#ifdef THREAD_CARD_TABLE_OFFSET -#undef THREAD_CARD_TABLE_OFFSET -#endif -#ifdef THREAD_EXCEPTION_OFFSET -#undef THREAD_EXCEPTION_OFFSET -#endif -#ifdef THREAD_ID_OFFSET -#undef THREAD_ID_OFFSET -#endif -#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE +static constexpr size_t kFrameSizeSaveAllCalleeSave = FRAME_SIZE_SAVE_ALL_CALLEE_SAVE; #undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE -#endif -#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE +static constexpr size_t kFrameSizeRefsOnlyCalleeSave = FRAME_SIZE_REFS_ONLY_CALLEE_SAVE; #undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE -#endif -#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE +static constexpr size_t kFrameSizeRefsAndArgsCalleeSave = FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE; #undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE -#endif -#ifdef HEAP_REFERENCE_SIZE -#undef HEAP_REFERENCE_SIZE -#endif } - -// The following tests are all for the running architecture. So we get away -// with just including it and not undefining it every time. - -#if defined(__arm__) -#include "arch/arm/asm_support_arm.h" -#elif defined(__aarch64__) -#include "arch/arm64/asm_support_arm64.h" -#elif defined(__mips__) -#include "arch/mips/asm_support_mips.h" -#elif defined(__i386__) -#include "arch/x86/asm_support_x86.h" -#elif defined(__x86_64__) -#include "arch/x86_64/asm_support_x86_64.h" -#else - // This happens for the host test. -#ifdef __LP64__ -#include "arch/x86_64/asm_support_x86_64.h" -#else -#include "arch/x86/asm_support_x86.h" -#endif -#endif - - -TEST_F(ArchTest, ThreadOffsets) { - // Ugly hack, change when possible. -#ifdef __LP64__ -#define POINTER_SIZE 8 -#else -#define POINTER_SIZE 4 -#endif - -#if defined(THREAD_SELF_OFFSET) - ThreadOffset<POINTER_SIZE> self_offset = Thread::SelfOffset<POINTER_SIZE>(); - EXPECT_EQ(self_offset.Int32Value(), THREAD_SELF_OFFSET); -#else - LOG(INFO) << "No Thread Self Offset found."; -#endif - -#if defined(THREAD_CARD_TABLE_OFFSET) - ThreadOffset<POINTER_SIZE> card_offset = Thread::CardTableOffset<POINTER_SIZE>(); - EXPECT_EQ(card_offset.Int32Value(), THREAD_CARD_TABLE_OFFSET); -#else - LOG(INFO) << "No Thread Card Table Offset found."; -#endif - -#if defined(THREAD_EXCEPTION_OFFSET) - ThreadOffset<POINTER_SIZE> exc_offset = Thread::ExceptionOffset<POINTER_SIZE>(); - EXPECT_EQ(exc_offset.Int32Value(), THREAD_EXCEPTION_OFFSET); -#else - LOG(INFO) << "No Thread Exception Offset found."; -#endif - -#if defined(THREAD_ID_OFFSET) - ThreadOffset<POINTER_SIZE> id_offset = Thread::ThinLockIdOffset<POINTER_SIZE>(); - EXPECT_EQ(id_offset.Int32Value(), THREAD_ID_OFFSET); -#else - LOG(INFO) << "No Thread ID Offset found."; -#endif +// Check architecture specific constants are sound. +TEST_F(ArchTest, ARM) { + CheckFrameSize(InstructionSet::kArm, Runtime::kSaveAll, arm::kFrameSizeSaveAllCalleeSave); + CheckFrameSize(InstructionSet::kArm, Runtime::kRefsOnly, arm::kFrameSizeRefsOnlyCalleeSave); + CheckFrameSize(InstructionSet::kArm, Runtime::kRefsAndArgs, arm::kFrameSizeRefsAndArgsCalleeSave); } -TEST_F(ArchTest, CalleeSaveMethodOffsets) { -#if defined(RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET) - EXPECT_EQ(Runtime::GetCalleeSaveMethodOffset(Runtime::kSaveAll), - static_cast<size_t>(RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET)); -#else - LOG(INFO) << "No Runtime Save-all Offset found."; -#endif - -#if defined(RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET) - EXPECT_EQ(Runtime::GetCalleeSaveMethodOffset(Runtime::kRefsOnly), - static_cast<size_t>(RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET)); -#else - LOG(INFO) << "No Runtime Refs-only Offset found."; -#endif - -#if defined(RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET) - EXPECT_EQ(Runtime::GetCalleeSaveMethodOffset(Runtime::kRefsAndArgs), - static_cast<size_t>(RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET)); -#else - LOG(INFO) << "No Runtime Refs-and-Args Offset found."; -#endif +TEST_F(ArchTest, ARM64) { + CheckFrameSize(InstructionSet::kArm64, Runtime::kSaveAll, arm64::kFrameSizeSaveAllCalleeSave); + CheckFrameSize(InstructionSet::kArm64, Runtime::kRefsOnly, arm64::kFrameSizeRefsOnlyCalleeSave); + CheckFrameSize(InstructionSet::kArm64, Runtime::kRefsAndArgs, + arm64::kFrameSizeRefsAndArgsCalleeSave); } +TEST_F(ArchTest, MIPS) { + CheckFrameSize(InstructionSet::kMips, Runtime::kSaveAll, mips::kFrameSizeSaveAllCalleeSave); + CheckFrameSize(InstructionSet::kMips, Runtime::kRefsOnly, mips::kFrameSizeRefsOnlyCalleeSave); + CheckFrameSize(InstructionSet::kMips, Runtime::kRefsAndArgs, + mips::kFrameSizeRefsAndArgsCalleeSave); +} -TEST_F(ArchTest, HeapReferenceSize) { -#if defined(HEAP_REFERENCE_SIZE) - EXPECT_EQ(sizeof(mirror::HeapReference<mirror::Object>), - static_cast<size_t>(HEAP_REFERENCE_SIZE)); -#else - LOG(INFO) << "No expected HeapReference Size found."; -#endif +TEST_F(ArchTest, X86) { + CheckFrameSize(InstructionSet::kX86, Runtime::kSaveAll, x86::kFrameSizeSaveAllCalleeSave); + CheckFrameSize(InstructionSet::kX86, Runtime::kRefsOnly, x86::kFrameSizeRefsOnlyCalleeSave); + CheckFrameSize(InstructionSet::kX86, Runtime::kRefsAndArgs, x86::kFrameSizeRefsAndArgsCalleeSave); } -TEST_F(ArchTest, StackReferenceSize) { -#if defined(STACK_REFERENCE_SIZE) - EXPECT_EQ(sizeof(StackReference<mirror::Object>), - static_cast<size_t>(STACK_REFERENCE_SIZE)); -#else - LOG(INFO) << "No expected StackReference Size #define found."; -#endif +TEST_F(ArchTest, X86_64) { + CheckFrameSize(InstructionSet::kX86_64, Runtime::kSaveAll, x86_64::kFrameSizeSaveAllCalleeSave); + CheckFrameSize(InstructionSet::kX86_64, Runtime::kRefsOnly, x86_64::kFrameSizeRefsOnlyCalleeSave); + CheckFrameSize(InstructionSet::kX86_64, Runtime::kRefsAndArgs, + x86_64::kFrameSizeRefsAndArgsCalleeSave); } } // namespace art diff --git a/runtime/arch/arm/asm_support_arm.S b/runtime/arch/arm/asm_support_arm.S index a3e3b21c7e..2af636e4b5 100644 --- a/runtime/arch/arm/asm_support_arm.S +++ b/runtime/arch/arm/asm_support_arm.S @@ -30,63 +30,92 @@ .arch armv7-a .thumb -.macro ENTRY name - .thumb_func - .type \name, #function - .hidden \name // Hide this as a global symbol, so we do not incur plt calls. - .global \name - /* Cache alignment for function entry */ - .balign 16 -\name: - .cfi_startproc - .fnstart +// Macro to generate the value of Runtime::Current into rDest clobbering rTemp. As it uses labels +// then the labels need to be unique. We bind these to the function name in the ENTRY macros. +.macro RUNTIME_CURRENT name, num, rDest, rTemp + .if .Lruntime_current\num\()_used + .error + .endif + .set .Lruntime_current\num\()_used, 1 + ldr \rDest, .Lgot_\name\()_\num @ Load offset of the GOT. + ldr \rTemp, .Lruntime_instance_\name\()_\num @ Load GOT offset of Runtime::instance_. +.Lload_got_\name\()_\num\(): + add \rDest, pc @ Fixup GOT address. + ldr \rDest, [\rDest, \rTemp] @ Load address of Runtime::instance_. + ldr \rDest, [\rDest] @ Load Runtime::instance_. .endm -.macro ENTRY_NO_HIDE name - .thumb_func +// Common ENTRY declaration code for ARM and thumb, an ENTRY should always be paired with an END. +// Declares the RUNTIME_CURRENT[123] macros that can be used within an ENTRY and will have literals +// generated at END. +.macro DEF_ENTRY thumb_or_arm, name + \thumb_or_arm .type \name, #function + .hidden \name // Hide this as a global symbol, so we do not incur plt calls. .global \name - /* Cache alignment for function entry */ + // Cache alignment for function entry. .balign 16 \name: .cfi_startproc .fnstart + // Track whether RUNTIME_CURRENT was used. + .set .Lruntime_current1_used, 0 + .set .Lruntime_current2_used, 0 + .set .Lruntime_current3_used, 0 + // The RUNTIME_CURRENT macros that are bound to the \name argument of DEF_ENTRY to ensure + // that label names are unique. + .macro RUNTIME_CURRENT1 rDest, rTemp + RUNTIME_CURRENT \name, 1, \rDest, \rTemp + .endm + .macro RUNTIME_CURRENT2 rDest, rTemp + RUNTIME_CURRENT \name, 2, \rDest, \rTemp + .endm + .macro RUNTIME_CURRENT3 rDest, rTemp + RUNTIME_CURRENT \name, 3, \rDest, \rTemp + .endm .endm - -.macro ARM_ENTRY name - .arm - .type \name, #function - .hidden \name // Hide this as a global symbol, so we do not incur plt calls. - .global \name - /* Cache alignment for function entry */ - .balign 16 -\name: - .cfi_startproc - /* Ensure we get a sane starting CFA. */ - .cfi_def_cfa sp,0 - .fnstart +// A thumb2 style ENTRY. +.macro ENTRY name + DEF_ENTRY .thumb_func, \name .endm -.macro ARM_ENTRY_NO_HIDE name - .arm - .type \name, #function - .global \name - /* Cache alignment for function entry */ - .balign 16 -\name: - .cfi_startproc - /* Ensure we get a sane starting CFA. */ - .cfi_def_cfa sp,0 - .fnstart +// A ARM style ENTRY. +.macro ARM_ENTRY name + DEF_ENTRY .arm, \name .endm +// Terminate an ENTRY and generate GOT references. .macro END name + // Generate offsets of GOT and Runtime::instance_ used in RUNTIME_CURRENT. + .if .Lruntime_current1_used + .Lgot_\name\()_1: + .word _GLOBAL_OFFSET_TABLE_-(.Lload_got_\name\()_1+4) + .Lruntime_instance_\name\()_1: + .word _ZN3art7Runtime9instance_E(GOT) + .endif + .if .Lruntime_current2_used + .Lgot_\name\()_2: + .word _GLOBAL_OFFSET_TABLE_-(.Lload_got_\name\()_2+4) + .Lruntime_instance_\name\()_2: + .word _ZN3art7Runtime9instance_E(GOT) + .endif + .if .Lruntime_current3_used + .Lgot_\name\()_3: + .word _GLOBAL_OFFSET_TABLE_-(.Lload_got_\name\()_3+4) + .Lruntime_instance_\name\()_3: + .word _ZN3art7Runtime9instance_E(GOT) + .endif + // Remove the RUNTIME_CURRENTx macros so they get rebound in the next function entry. + .purgem RUNTIME_CURRENT1 + .purgem RUNTIME_CURRENT2 + .purgem RUNTIME_CURRENT3 .fnend .cfi_endproc .size \name, .-\name .endm +// Declare an unimplemented ENTRY that will halt a debugger. .macro UNIMPLEMENTED name ENTRY \name bkpt diff --git a/runtime/arch/arm/asm_support_arm.h b/runtime/arch/arm/asm_support_arm.h index 330924ef0f..5388cc0daf 100644 --- a/runtime/arch/arm/asm_support_arm.h +++ b/runtime/arch/arm/asm_support_arm.h @@ -19,21 +19,10 @@ #include "asm_support.h" -// Offset of field Thread::tls32_.state_and_flags verified in InitCpu -#define THREAD_FLAGS_OFFSET 0 -// Offset of field Thread::tls32_.thin_lock_thread_id verified in InitCpu -#define THREAD_ID_OFFSET 12 -// Offset of field Thread::tlsPtr_.card_table verified in InitCpu -#define THREAD_CARD_TABLE_OFFSET 120 -// Offset of field Thread::tlsPtr_.exception verified in InitCpu -#define THREAD_EXCEPTION_OFFSET 124 - #define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 176 #define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 32 #define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 48 -// Expected size of a heap reference -#define HEAP_REFERENCE_SIZE 4 // Flag for enabling R4 optimization in arm runtime #define ARM_R4_SUSPEND_FLAG diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc index 2780d1b5c9..ff0eb4ae45 100644 --- a/runtime/arch/arm/entrypoints_init_arm.cc +++ b/runtime/arch/arm/entrypoints_init_arm.cc @@ -21,17 +21,11 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/entrypoint_utils.h" #include "entrypoints/math_entrypoints.h" +#include "entrypoints/runtime_asm_entrypoints.h" +#include "interpreter/interpreter.h" namespace art { -// Interpreter entrypoints. -extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result); -extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result); - // Portable entrypoints. extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*); extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*); diff --git a/runtime/arch/arm/arm_sdiv.S b/runtime/arch/arm/instruction_set_features_arm.S index babdbf5526..c26f2cd003 100644 --- a/runtime/arch/arm/arm_sdiv.S +++ b/runtime/arch/arm/instruction_set_features_arm.S @@ -1,15 +1,29 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "asm_support_arm.S" + +.section .text // This function is used to check for the CPU's support for the sdiv // instruction at runtime. It will either return the value 1 or // will cause an invalid instruction trap (SIGILL signal). The // caller must arrange for the signal handler to set the r0 // register to 0 and move the pc forward by 4 bytes (to skip // the invalid instruction). - - -#include "asm_support_arm.S" - -.section .text -ENTRY_NO_HIDE CheckForARMSDIVInstruction +ENTRY artCheckForARMSDIVInstruction mov r1,#1 // depending on the architecture, the assembler will not allow an // sdiv instruction, so we will have to output the bytes directly. @@ -21,4 +35,4 @@ ENTRY_NO_HIDE CheckForARMSDIVInstruction // It will have 0 otherwise (set by the signal handler) // the value is just returned from this function. bx lr - END CheckForARMSDIVInstruction +END artCheckForARMSDIVInstruction diff --git a/runtime/arch/arm/portable_entrypoints_arm.S b/runtime/arch/arm/portable_entrypoints_arm.S index 3491c18c37..d37e7604eb 100644 --- a/runtime/arch/arm/portable_entrypoints_arm.S +++ b/runtime/arch/arm/portable_entrypoints_arm.S @@ -53,7 +53,7 @@ ENTRY art_portable_invoke_stub mov ip, #0 @ set ip to 0 str ip, [sp] @ store NULL for method* at bottom of frame add sp, #16 @ first 4 args are not passed on stack for portable - ldr ip, [r0, #METHOD_PORTABLE_CODE_OFFSET] @ get pointer to the code + ldr ip, [r0, #MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET] @ get pointer to the code blx ip @ call the method mov sp, r11 @ restore the stack pointer ldr ip, [sp, #24] @ load the result pointer @@ -138,7 +138,7 @@ ENTRY art_portable_resolution_trampoline END art_portable_resolution_trampoline .extern artPortableToInterpreterBridge -ENTRY_NO_HIDE art_portable_to_interpreter_bridge +ENTRY art_portable_to_interpreter_bridge @ Fake callee save ref and args frame set up, note portable doesn't use callee save frames. @ TODO: just save the registers that are needed in artPortableToInterpreterBridge. push {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves @@ -165,3 +165,5 @@ ENTRY_NO_HIDE art_portable_to_interpreter_bridge .cfi_adjust_cfa_offset -48 bx lr @ return END art_portable_to_interpreter_bridge + +UNIMPLEMENTED art_portable_imt_conflict_trampoline diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 466e9eb098..aae0c94994 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -27,8 +27,8 @@ * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kSaveAll) */ -.macro SETUP_SAVE_ALL_CALLEE_SAVE_FRAME - push {r4-r11, lr} @ 9 words of callee saves +.macro SETUP_SAVE_ALL_CALLEE_SAVE_FRAME rTemp1, rTemp2 + push {r4-r11, lr} @ 9 words (36 bytes) of callee saves. .save {r4-r11, lr} .cfi_adjust_cfa_offset 36 .cfi_rel_offset r4, 0 @@ -40,12 +40,17 @@ .cfi_rel_offset r10, 24 .cfi_rel_offset r11, 28 .cfi_rel_offset lr, 32 - vpush {s0-s31} + vpush {s0-s31} @ 32 words (128 bytes) of floats. .pad #128 .cfi_adjust_cfa_offset 128 - sub sp, #12 @ 3 words of space, bottom word will hold Method* + sub sp, #12 @ 3 words of space, bottom word will hold Method*. .pad #12 .cfi_adjust_cfa_offset 12 + RUNTIME_CURRENT1 \rTemp1, \rTemp2 @ Load Runtime::Current into rTemp1. + THIS_LOAD_REQUIRES_READ_BARRIER + ldr \rTemp1, [\rTemp1, #RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET] @ rTemp1 is kSaveAll Method*. + str \rTemp1, [sp, #0] @ Place Method* at bottom of stack. + str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. // Ugly compile-time check, but we only have the preprocessor. #if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 36 + 128 + 12) @@ -57,8 +62,8 @@ * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kRefsOnly). */ -.macro SETUP_REF_ONLY_CALLEE_SAVE_FRAME - push {r5-r8, r10-r11, lr} @ 7 words of callee saves +.macro SETUP_REFS_ONLY_CALLEE_SAVE_FRAME rTemp1, rTemp2 + push {r5-r8, r10-r11, lr} @ 7 words of callee saves .save {r5-r8, r10-r11, lr} .cfi_adjust_cfa_offset 28 .cfi_rel_offset r5, 0 @@ -68,9 +73,14 @@ .cfi_rel_offset r10, 16 .cfi_rel_offset r11, 20 .cfi_rel_offset lr, 24 - sub sp, #4 @ bottom word will hold Method* + sub sp, #4 @ bottom word will hold Method* .pad #4 .cfi_adjust_cfa_offset 4 + RUNTIME_CURRENT2 \rTemp1, \rTemp2 @ Load Runtime::Current into rTemp1. + THIS_LOAD_REQUIRES_READ_BARRIER + ldr \rTemp1, [\rTemp1, #RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET] @ rTemp1 is kRefsOnly Method*. + str \rTemp1, [sp, #0] @ Place Method* at bottom of stack. + str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. // Ugly compile-time check, but we only have the preprocessor. #if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 28 + 4) @@ -78,7 +88,7 @@ #endif .endm -.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME +.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME add sp, #4 @ bottom word holds Method* pop {r5-r8, r10-r11, lr} @ 7 words of callee saves .cfi_restore r5 @@ -87,10 +97,10 @@ .cfi_restore r8 .cfi_restore r10 .cfi_restore r11 - .cfi_adjust_cfa_offset -32 + .cfi_adjust_cfa_offset -FRAME_SIZE_REFS_ONLY_CALLEE_SAVE .endm -.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN +.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN add sp, #4 @ bottom word holds Method* pop {r5-r8, r10-r11, lr} @ 7 words of callee saves .cfi_restore r5 @@ -99,7 +109,7 @@ .cfi_restore r8 .cfi_restore r10 .cfi_restore r11 - .cfi_adjust_cfa_offset -32 + .cfi_adjust_cfa_offset -FRAME_SIZE_REFS_ONLY_CALLEE_SAVE bx lr @ return .endm @@ -107,8 +117,8 @@ * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kRefsAndArgs). */ -.macro SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME - push {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves +.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME rTemp1, rTemp2 + push {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves .save {r1-r3, r5-r8, r10-r11, lr} .cfi_rel_offset r1, 0 .cfi_rel_offset r2, 4 @@ -121,9 +131,15 @@ .cfi_rel_offset r11, 32 .cfi_rel_offset lr, 36 .cfi_adjust_cfa_offset 40 - sub sp, #8 @ 2 words of space, bottom word will hold Method* + sub sp, #8 @ 2 words of space, bottom word will hold Method* .pad #8 .cfi_adjust_cfa_offset 8 + RUNTIME_CURRENT3 \rTemp1, \rTemp2 @ Load Runtime::Current into rTemp1. + THIS_LOAD_REQUIRES_READ_BARRIER + @ rTemp1 is kRefsAndArgs Method*. + ldr \rTemp1, [\rTemp1, #RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET] + str \rTemp1, [sp, #0] @ Place Method* at bottom of stack. + str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. // Ugly compile-time check, but we only have the preprocessor. #if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 40 + 8) @@ -131,7 +147,29 @@ #endif .endm -.macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME +.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_R0 + push {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves + .save {r1-r3, r5-r8, r10-r11, lr} + .cfi_rel_offset r1, 0 + .cfi_rel_offset r2, 4 + .cfi_rel_offset r3, 8 + .cfi_rel_offset r5, 12 + .cfi_rel_offset r6, 16 + .cfi_rel_offset r7, 20 + .cfi_rel_offset r8, 24 + .cfi_rel_offset r10, 28 + .cfi_rel_offset r11, 32 + .cfi_rel_offset lr, 36 + .cfi_adjust_cfa_offset 40 + sub sp, #8 @ 2 words of space, bottom word will hold Method* + .pad #8 + .cfi_adjust_cfa_offset 8 + + str r0, [sp, #0] @ Store ArtMethod* to bottom of stack. + str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. +.endm + +.macro RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME add sp, #8 @ rewind sp pop {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves .cfi_restore r1 @@ -146,6 +184,7 @@ .cfi_adjust_cfa_offset -48 .endm + .macro RETURN_IF_RESULT_IS_ZERO cbnz r0, 1f @ result non-zero branch over bx lr @ return @@ -165,41 +204,35 @@ .macro DELIVER_PENDING_EXCEPTION .fnend .fnstart - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME @ save callee saves for throw + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0, r1 @ save callee saves for throw mov r0, r9 @ pass Thread::Current - mov r1, sp @ pass SP - b artDeliverPendingExceptionFromCode @ artDeliverPendingExceptionFromCode(Thread*, SP) + b artDeliverPendingExceptionFromCode @ artDeliverPendingExceptionFromCode(Thread*) .endm .macro NO_ARG_RUNTIME_EXCEPTION c_name, cxx_name .extern \cxx_name ENTRY \c_name - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0, r1 // save all registers as basis for long jump context mov r0, r9 @ pass Thread::Current - mov r1, sp @ pass SP - b \cxx_name @ \cxx_name(Thread*, SP) + b \cxx_name @ \cxx_name(Thread*) END \c_name .endm .macro ONE_ARG_RUNTIME_EXCEPTION c_name, cxx_name .extern \cxx_name ENTRY \c_name - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r1, r2 // save all registers as basis for long jump context mov r1, r9 @ pass Thread::Current - mov r2, sp @ pass SP - b \cxx_name @ \cxx_name(Thread*, SP) - bkpt + b \cxx_name @ \cxx_name(Thread*) END \c_name .endm .macro TWO_ARG_RUNTIME_EXCEPTION c_name, cxx_name .extern \cxx_name ENTRY \c_name - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r2, r3 // save all registers as basis for long jump context mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - b \cxx_name @ \cxx_name(Thread*, SP) - bkpt + b \cxx_name @ \cxx_name(Thread*) END \c_name .endm @@ -224,12 +257,11 @@ END \c_name .macro ONE_ARG_REF_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - ldr r1, [sp, #32] @ pass referrer + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 @ save callee saves in case of GC + ldr r1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - bl \entrypoint @ (uint32_t field_idx, const Method* referrer, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + bl \entrypoint @ (uint32_t field_idx, const Method* referrer, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME \return END \name .endm @@ -237,17 +269,11 @@ END \name .macro TWO_ARG_REF_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - ldr r2, [sp, #32] @ pass referrer + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC + ldr r2, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP - .pad #16 - .cfi_adjust_cfa_offset 16 - bl \entrypoint @ (field_idx, Object*, referrer, Thread*, SP) - add sp, #16 @ strip the extra frame - .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + bl \entrypoint @ (field_idx, Object*, referrer, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME \return END \name .endm @@ -255,21 +281,15 @@ END \name .macro THREE_ARG_REF_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - ldr r3, [sp, #32] @ pass referrer - mov r12, sp @ save SP - sub sp, #8 @ grow frame for alignment with stack args - .pad #8 - .cfi_adjust_cfa_offset 8 - push {r9, r12} @ pass Thread::Current and SP - .save {r9, r12} - .cfi_adjust_cfa_offset 8 - .cfi_rel_offset r9, 0 - .cfi_rel_offset r12, 4 - bl \entrypoint @ (field_idx, Object*, new_val, referrer, Thread*, SP) + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12 @ save callee saves in case of GC + ldr r3, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer + str r9, [sp, #-16]! @ expand the frame and pass Thread::Current + .pad #16 + .cfi_adjust_cfa_offset 16 + bl \entrypoint @ (field_idx, Object*, new_val, referrer, Thread*) add sp, #16 @ release out args .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME @ TODO: we can clearly save an add here + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME @ TODO: we can clearly save an add here \return END \name .endm @@ -325,8 +345,8 @@ ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFr .macro INVOKE_TRAMPOLINE c_name, cxx_name .extern \cxx_name ENTRY \c_name - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME @ save callee saves in case allocation triggers GC - ldr r2, [sp, #48] @ pass caller Method* + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case allocation triggers GC + ldr r2, [sp, #FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE] @ pass caller Method* mov r3, r9 @ pass Thread::Current mov r12, sp str r12, [sp, #-16]! @ expand the frame and pass SP @@ -336,7 +356,7 @@ ENTRY \c_name add sp, #16 @ strip the extra frame .cfi_adjust_cfa_offset -16 mov r12, r1 @ save Method*->code_ - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME cbz r0, 1f @ did we find the target? if not go to exception delivery bx r12 @ tail call to target 1: @@ -393,7 +413,7 @@ ENTRY art_quick_invoke_stub ldr r3, [sp, #12] @ copy arg value for r3 mov ip, #0 @ set ip to 0 str ip, [sp] @ store NULL for method* at bottom of frame - ldr ip, [r0, #METHOD_QUICK_CODE_OFFSET] @ get pointer to the code + ldr ip, [r0, #MIRROR_ART_METHOD_QUICK_CODE_OFFSET] @ get pointer to the code blx ip @ call the method mov sp, r11 @ restore the stack pointer ldr ip, [sp, #24] @ load the result pointer @@ -437,10 +457,10 @@ ENTRY art_quick_lock_object cbz r0, .Lslow_lock .Lretry_lock: ldr r2, [r9, #THREAD_ID_OFFSET] - ldrex r1, [r0, #LOCK_WORD_OFFSET] + ldrex r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] cbnz r1, .Lnot_unlocked @ already thin locked @ unlocked case - r2 holds thread id with count of 0 - strex r3, r2, [r0, #LOCK_WORD_OFFSET] + strex r3, r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] cbnz r3, .Lstrex_fail @ store failed, retry dmb ish @ full (LoadLoad|LoadStore) memory barrier bx lr @@ -456,14 +476,13 @@ ENTRY art_quick_lock_object add r2, r1, #65536 @ increment count in lock word placing in r2 for storing lsr r1, r2, 30 @ if either of the top two bits are set, we overflowed. cbnz r1, .Lslow_lock @ if we overflow the count go slow path - str r2, [r0, #LOCK_WORD_OFFSET] @ no need for strex as we hold the lock + str r2, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] @ no need for strex as we hold the lock bx lr .Lslow_lock: - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case we block + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 @ save callee saves in case we block mov r1, r9 @ pass Thread::Current - mov r2, sp @ pass SP - bl artLockObjectFromCode @ (Object* obj, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + bl artLockObjectFromCode @ (Object* obj, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME RETURN_IF_RESULT_IS_ZERO DELIVER_PENDING_EXCEPTION END art_quick_lock_object @@ -475,7 +494,7 @@ END art_quick_lock_object .extern artUnlockObjectFromCode ENTRY art_quick_unlock_object cbz r0, .Lslow_unlock - ldr r1, [r0, #LOCK_WORD_OFFSET] + ldr r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] lsr r2, r1, 30 cbnz r2, .Lslow_unlock @ if either of the top two bits are set, go slow path ldr r2, [r9, #THREAD_ID_OFFSET] @@ -486,18 +505,18 @@ ENTRY art_quick_unlock_object bpl .Lrecursive_thin_unlock @ transition to unlocked, r3 holds 0 dmb ish @ full (LoadStore|StoreStore) memory barrier - str r3, [r0, #LOCK_WORD_OFFSET] + str r3, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] bx lr .Lrecursive_thin_unlock: sub r1, r1, #65536 - str r1, [r0, #LOCK_WORD_OFFSET] + str r1, [r0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] bx lr .Lslow_unlock: - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case exception allocation triggers GC + @ save callee saves in case exception allocation triggers GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 mov r1, r9 @ pass Thread::Current - mov r2, sp @ pass SP - bl artUnlockObjectFromCode @ (Object* obj, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + bl artUnlockObjectFromCode @ (Object* obj, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME RETURN_IF_RESULT_IS_ZERO DELIVER_PENDING_EXCEPTION END art_quick_unlock_object @@ -528,10 +547,9 @@ ENTRY art_quick_check_cast pop {r0-r1, lr} .cfi_restore r0 .cfi_restore r1 - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r2, r3 // save all registers as basis for long jump context mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - b artThrowClassCastException @ (Class*, Class*, Thread*, SP) + b artThrowClassCastException @ (Class*, Class*, Thread*) bkpt END art_quick_check_cast @@ -548,7 +566,7 @@ END art_quick_aput_obj_with_null_and_bound_check .hidden art_quick_aput_obj_with_bound_check ENTRY art_quick_aput_obj_with_bound_check - ldr r3, [r0, #ARRAY_LENGTH_OFFSET] + ldr r3, [r0, #MIRROR_ARRAY_LENGTH_OFFSET] cmp r3, r1 bhi art_quick_aput_obj mov r0, r1 @@ -559,20 +577,20 @@ END art_quick_aput_obj_with_bound_check .hidden art_quick_aput_obj ENTRY art_quick_aput_obj cbz r2, .Ldo_aput_null - ldr r3, [r0, #CLASS_OFFSET] - ldr ip, [r2, #CLASS_OFFSET] - ldr r3, [r3, #CLASS_COMPONENT_TYPE_OFFSET] + ldr r3, [r0, #MIRROR_OBJECT_CLASS_OFFSET] + ldr ip, [r2, #MIRROR_OBJECT_CLASS_OFFSET] + ldr r3, [r3, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET] cmp r3, ip @ value's type == array's component type - trivial assignability bne .Lcheck_assignability .Ldo_aput: - add r3, r0, #OBJECT_ARRAY_DATA_OFFSET + add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET str r2, [r3, r1, lsl #2] ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET] lsr r0, r0, #7 strb r3, [r3, r0] blx lr .Ldo_aput_null: - add r3, r0, #OBJECT_ARRAY_DATA_OFFSET + add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET str r2, [r3, r1, lsl #2] blx lr .Lcheck_assignability: @@ -593,7 +611,7 @@ ENTRY art_quick_aput_obj .cfi_restore r2 .cfi_restore lr .cfi_adjust_cfa_offset -16 - add r3, r0, #OBJECT_ARRAY_DATA_OFFSET + add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET str r2, [r3, r1, lsl #2] ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET] lsr r0, r0, #7 @@ -606,12 +624,11 @@ ENTRY art_quick_aput_obj .cfi_restore r2 .cfi_restore lr .cfi_adjust_cfa_offset -16 - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r3, ip mov r1, r2 - mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - b artThrowArrayStoreException @ (Class*, Class*, Thread*, SP) - bkpt @ unreached + mov r2, r9 @ pass Thread::Current + b artThrowArrayStoreException @ (Class*, Class*, Thread*) + bkpt @ unreached END art_quick_aput_obj /* @@ -621,12 +638,11 @@ END art_quick_aput_obj */ .extern artInitializeStaticStorageFromCode ENTRY art_quick_initialize_static_storage - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - @ artInitializeStaticStorageFromCode(uint32_t type_idx, Method* referrer, Thread*, SP) + @ artInitializeStaticStorageFromCode(uint32_t type_idx, Method* referrer, Thread*) bl artInitializeStaticStorageFromCode - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME RETURN_IF_RESULT_IS_NON_ZERO DELIVER_PENDING_EXCEPTION END art_quick_initialize_static_storage @@ -636,12 +652,11 @@ END art_quick_initialize_static_storage */ .extern artInitializeTypeFromCode ENTRY art_quick_initialize_type - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - @ artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*, SP) + @ artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*) bl artInitializeTypeFromCode - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME RETURN_IF_RESULT_IS_NON_ZERO DELIVER_PENDING_EXCEPTION END art_quick_initialize_type @@ -652,12 +667,11 @@ END art_quick_initialize_type */ .extern artInitializeTypeAndVerifyAccessFromCode ENTRY art_quick_initialize_type_and_verify_access - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - @ artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx, Method* referrer, Thread*, SP) + @ artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx, Method* referrer, Thread*) bl artInitializeTypeAndVerifyAccessFromCode - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME RETURN_IF_RESULT_IS_NON_ZERO DELIVER_PENDING_EXCEPTION END art_quick_initialize_type_and_verify_access @@ -676,13 +690,12 @@ ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCode, RETURN_O */ .extern artGet64StaticFromCode ENTRY art_quick_get64_static - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - ldr r1, [sp, #32] @ pass referrer + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC + ldr r1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - bl artGet64StaticFromCode @ (uint32_t field_idx, const Method* referrer, Thread*, SP) + bl artGet64StaticFromCode @ (uint32_t field_idx, const Method* referrer, Thread*) ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME cbnz r2, 1f @ success if no exception pending bx lr @ return on success 1: @@ -703,18 +716,12 @@ TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCode, RETU */ .extern artGet64InstanceFromCode ENTRY art_quick_get64_instance - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - ldr r2, [sp, #32] @ pass referrer + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC + ldr r2, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP - .pad #16 - .cfi_adjust_cfa_offset 16 - bl artGet64InstanceFromCode @ (field_idx, Object*, referrer, Thread*, SP) - add sp, #16 @ strip the extra frame - .cfi_adjust_cfa_offset -16 + bl artGet64InstanceFromCode @ (field_idx, Object*, referrer, Thread*) ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME cbnz r2, 1f @ success if no exception pending bx lr @ return on success 1: @@ -734,22 +741,17 @@ TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCode, RETURN_I */ .extern artSet64StaticFromCode ENTRY art_quick_set64_static - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12 @ save callee saves in case of GC mov r3, r2 @ pass one half of wide argument mov r2, r1 @ pass other half of wide argument - ldr r1, [sp, #32] @ pass referrer - mov r12, sp @ save SP - sub sp, #8 @ grow frame for alignment with stack args - .pad #8 - .cfi_adjust_cfa_offset 8 - push {r9, r12} @ pass Thread::Current and SP - .save {r9, r12} - .cfi_adjust_cfa_offset 8 - .cfi_rel_offset r9, 0 - bl artSet64StaticFromCode @ (field_idx, referrer, new_val, Thread*, SP) + ldr r1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer + str r9, [sp, #-16]! @ expand the frame and pass Thread::Current + .pad #16 + .cfi_adjust_cfa_offset 16 + bl artSet64StaticFromCode @ (field_idx, referrer, new_val, Thread*) add sp, #16 @ release out args .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME @ TODO: we can clearly save an add here + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME @ TODO: we can clearly save an add here RETURN_IF_RESULT_IS_ZERO DELIVER_PENDING_EXCEPTION END art_quick_set64_static @@ -766,19 +768,18 @@ THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCode, RE */ .extern artSet64InstanceFromCode ENTRY art_quick_set64_instance - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r12, sp @ save SP - sub sp, #8 @ grow frame for alignment with stack args - .pad #8 - .cfi_adjust_cfa_offset 8 - push {r9, r12} @ pass Thread::Current and SP - .save {r9, r12} - .cfi_adjust_cfa_offset 8 - .cfi_rel_offset r9, 0 - bl artSet64InstanceFromCode @ (field_idx, Object*, new_val, Thread*, SP) + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r12, lr @ save callee saves in case of GC + ldr r12, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer + str r9, [sp, #-12]! @ expand the frame and pass Thread::Current + .pad #12 + .cfi_adjust_cfa_offset 12 + str r12, [sp, #-4]! @ expand the frame and pass the referrer + .pad #4 + .cfi_adjust_cfa_offset 4 + bl artSet64InstanceFromCode @ (field_idx, Object*, new_val, Method* referrer, Thread*) add sp, #16 @ release out args .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME @ TODO: we can clearly save an add here + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME @ TODO: we can clearly save an add here RETURN_IF_RESULT_IS_ZERO DELIVER_PENDING_EXCEPTION END art_quick_set64_instance @@ -791,12 +792,11 @@ END art_quick_set64_instance */ .extern artResolveStringFromCode ENTRY art_quick_resolve_string - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - @ artResolveStringFromCode(Method* referrer, uint32_t string_idx, Thread*, SP) + @ artResolveStringFromCode(Method* referrer, uint32_t string_idx, Thread*) bl artResolveStringFromCode - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME RETURN_IF_RESULT_IS_NON_ZERO DELIVER_PENDING_EXCEPTION END art_quick_resolve_string @@ -805,11 +805,10 @@ END art_quick_resolve_string .macro TWO_ARG_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - bl \entrypoint @ (uint32_t type_idx, Method* method, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + bl \entrypoint @ (uint32_t type_idx, Method* method, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME \return DELIVER_PENDING_EXCEPTION END \name @@ -819,17 +818,11 @@ END \name .macro THREE_ARG_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12 @ save callee saves in case of GC mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP - .pad #16 - .cfi_adjust_cfa_offset 16 - @ (uint32_t type_idx, Method* method, int32_t component_count, Thread*, SP) + @ (uint32_t type_idx, Method* method, int32_t component_count, Thread*) bl \entrypoint - add sp, #16 @ strip the extra frame - .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME \return DELIVER_PENDING_EXCEPTION END \name @@ -844,25 +837,24 @@ GENERATE_ALL_ALLOC_ENTRYPOINTS .extern artTestSuspendFromCode ENTRY art_quick_test_suspend #ifdef ARM_R4_SUSPEND_FLAG - ldrh r0, [rSELF, #THREAD_FLAGS_OFFSET] + ldrh r0, [rSELF, #THREAD_FLAGS_OFFSET] mov rSUSPEND, #SUSPEND_CHECK_INTERVAL @ reset rSUSPEND to SUSPEND_CHECK_INTERVAL cbnz r0, 1f @ check Thread::Current()->suspend_count_ == 0 bx lr @ return if suspend_count_ == 0 1: #endif mov r0, rSELF - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves for stack crawl - mov r1, sp - bl artTestSuspendFromCode @ (Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 @ save callee saves for GC stack crawl + @ TODO: save FPRs to enable access in the debugger? + bl artTestSuspendFromCode @ (Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN END art_quick_test_suspend ENTRY art_quick_implicit_suspend mov r0, rSELF - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves for stack crawl - mov r1, sp - bl artTestSuspendFromCode @ (Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 @ save callee saves for stack crawl + bl artTestSuspendFromCode @ (Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN END art_quick_implicit_suspend /* @@ -872,8 +864,7 @@ END art_quick_implicit_suspend */ .extern artQuickProxyInvokeHandler ENTRY art_quick_proxy_invoke_handler - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME - str r0, [sp, #0] @ place proxy method at bottom of frame + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_R0 mov r2, r9 @ pass Thread::Current mov r3, sp @ pass SP blx artQuickProxyInvokeHandler @ (Method* proxy method, receiver, Thread*, SP) @@ -881,10 +872,10 @@ ENTRY art_quick_proxy_invoke_handler add sp, #16 @ skip r1-r3, 4 bytes padding. .cfi_adjust_cfa_offset -16 cbnz r2, 1f @ success if no exception is pending - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME bx lr @ return on success 1: - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME DELIVER_PENDING_EXCEPTION END art_quick_proxy_invoke_handler @@ -894,34 +885,33 @@ END art_quick_proxy_invoke_handler */ ENTRY art_quick_imt_conflict_trampoline ldr r0, [sp, #0] @ load caller Method* - ldr r0, [r0, #METHOD_DEX_CACHE_METHODS_OFFSET] @ load dex_cache_resolved_methods - add r0, #OBJECT_ARRAY_DATA_OFFSET @ get starting address of data + ldr r0, [r0, #MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET] @ load dex_cache_resolved_methods + add r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET @ get starting address of data ldr r0, [r0, r12, lsl 2] @ load the target method b art_quick_invoke_interface_trampoline END art_quick_imt_conflict_trampoline .extern artQuickResolutionTrampoline ENTRY art_quick_resolution_trampoline - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2, r3 mov r2, r9 @ pass Thread::Current mov r3, sp @ pass SP blx artQuickResolutionTrampoline @ (Method* called, receiver, Thread*, SP) cbz r0, 1f @ is code pointer null? goto exception mov r12, r0 ldr r0, [sp, #0] @ load resolved method in r0 - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME bx r12 @ tail-call into actual code 1: - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME DELIVER_PENDING_EXCEPTION END art_quick_resolution_trampoline /* * Called to do a generic JNI down-call */ -ENTRY_NO_HIDE art_quick_generic_jni_trampoline - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME - str r0, [sp, #0] // Store native ArtMethod* to bottom of stack. +ENTRY art_quick_generic_jni_trampoline + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_R0 // Save rSELF mov r11, rSELF @@ -1008,21 +998,21 @@ ENTRY_NO_HIDE art_quick_generic_jni_trampoline .cfi_def_cfa_register sp mov r9, r11 .Lexception_in_native: - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME DELIVER_PENDING_EXCEPTION END art_quick_generic_jni_trampoline .extern artQuickToInterpreterBridge -ENTRY_NO_HIDE art_quick_to_interpreter_bridge - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME +ENTRY art_quick_to_interpreter_bridge + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r1, r2 mov r1, r9 @ pass Thread::Current mov r2, sp @ pass SP blx artQuickToInterpreterBridge @ (Method* method, Thread*, SP) ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ add sp, #16 @ skip r1-r3, 4 bytes padding. .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME cbnz r2, 1f @ success if no exception is pending bx lr @ return on success 1: @@ -1035,30 +1025,23 @@ END art_quick_to_interpreter_bridge .extern artInstrumentationMethodEntryFromCode .extern artInstrumentationMethodExitFromCode ENTRY art_quick_instrumentation_entry - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME - str r0, [sp, #4] @ preserve r0 - mov r12, sp @ remember sp - str lr, [sp, #-16]! @ expand the frame and pass LR - .pad #16 - .cfi_adjust_cfa_offset 16 - .cfi_rel_offset lr, 0 + @ Make stack crawlable and clobber r2 and r3 (post saving) + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2, r3 + @ preserve r0 (not normally an arg) knowing there is a spare slot in kRefsAndArgs. + str r0, [sp, #4] mov r2, r9 @ pass Thread::Current - mov r3, r12 @ pass SP - blx artInstrumentationMethodEntryFromCode @ (Method*, Object*, Thread*, SP, LR) - add sp, #16 @ remove out argument and padding from stack - .cfi_adjust_cfa_offset -16 + mov r3, lr @ pass LR + blx artInstrumentationMethodEntryFromCode @ (Method*, Object*, Thread*, LR) mov r12, r0 @ r12 holds reference to code ldr r0, [sp, #4] @ restore r0 - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME blx r12 @ call method with lr set to art_quick_instrumentation_exit -END art_quick_instrumentation_entry +@ Deliberate fall-through into art_quick_instrumentation_exit. .type art_quick_instrumentation_exit, #function .global art_quick_instrumentation_exit art_quick_instrumentation_exit: - .cfi_startproc - .fnstart mov lr, #0 @ link register is to here, so clobber with 0 for later checks - SETUP_REF_ONLY_CALLEE_SAVE_FRAME + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ set up frame knowing r2 and r3 must be dead on exit mov r12, sp @ remember bottom of caller's frame push {r0-r1} @ save return value .save {r0-r1} @@ -1085,7 +1068,7 @@ art_quick_instrumentation_exit: add sp, #32 @ remove callee save frame .cfi_adjust_cfa_offset -32 bx r2 @ return -END art_quick_instrumentation_exit +END art_quick_instrumentation_entry /* * Instrumentation has requested that we deoptimize into the interpreter. The deoptimization @@ -1093,10 +1076,9 @@ END art_quick_instrumentation_exit */ .extern artDeoptimize ENTRY art_quick_deoptimize - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0, r1 mov r0, r9 @ Set up args. - mov r1, sp - blx artDeoptimize @ artDeoptimize(Thread*, SP) + blx artDeoptimize @ artDeoptimize(Thread*) END art_quick_deoptimize /* @@ -1219,9 +1201,9 @@ ENTRY art_quick_indexof .cfi_rel_offset r10, 4 .cfi_rel_offset r11, 8 .cfi_rel_offset lr, 12 - ldr r3, [r0, #STRING_COUNT_OFFSET] - ldr r12, [r0, #STRING_OFFSET_OFFSET] - ldr r0, [r0, #STRING_VALUE_OFFSET] + ldr r3, [r0, #MIRROR_STRING_COUNT_OFFSET] + ldr r12, [r0, #MIRROR_STRING_OFFSET_OFFSET] + ldr r0, [r0, #MIRROR_STRING_VALUE_OFFSET] /* Clamp start to [0..count] */ cmp r2, #0 @@ -1232,7 +1214,7 @@ ENTRY art_quick_indexof movgt r2, r3 /* Build a pointer to the start of string data */ - add r0, #STRING_DATA_OFFSET + add r0, #MIRROR_CHAR_ARRAY_DATA_OFFSET add r0, r0, r12, lsl #1 /* Save a copy in r12 to later compute result */ @@ -1341,12 +1323,12 @@ ENTRY art_quick_string_compareto .cfi_rel_offset r12, 24 .cfi_rel_offset lr, 28 - ldr r4, [r2, #STRING_OFFSET_OFFSET] - ldr r9, [r1, #STRING_OFFSET_OFFSET] - ldr r7, [r2, #STRING_COUNT_OFFSET] - ldr r10, [r1, #STRING_COUNT_OFFSET] - ldr r2, [r2, #STRING_VALUE_OFFSET] - ldr r1, [r1, #STRING_VALUE_OFFSET] + ldr r4, [r2, #MIRROR_STRING_OFFSET_OFFSET] + ldr r9, [r1, #MIRROR_STRING_OFFSET_OFFSET] + ldr r7, [r2, #MIRROR_STRING_COUNT_OFFSET] + ldr r10, [r1, #MIRROR_STRING_COUNT_OFFSET] + ldr r2, [r2, #MIRROR_STRING_VALUE_OFFSET] + ldr r1, [r1, #MIRROR_STRING_VALUE_OFFSET] /* * At this point, we have: @@ -1368,8 +1350,8 @@ ENTRY art_quick_string_compareto * Note: data pointers point to previous element so we can use pre-index * mode with base writeback. */ - add r2, #STRING_DATA_OFFSET-2 @ offset to contents[-1] - add r1, #STRING_DATA_OFFSET-2 @ offset to contents[-1] + add r2, #MIRROR_CHAR_ARRAY_DATA_OFFSET-2 @ offset to contents[-1] + add r1, #MIRROR_CHAR_ARRAY_DATA_OFFSET-2 @ offset to contents[-1] /* * At this point we have: diff --git a/runtime/arch/arm64/asm_support_arm64.S b/runtime/arch/arm64/asm_support_arm64.S index fb49460364..b3e9242c5c 100644 --- a/runtime/arch/arm64/asm_support_arm64.S +++ b/runtime/arch/arm64/asm_support_arm64.S @@ -52,15 +52,6 @@ .cfi_startproc .endm -.macro ENTRY_NO_HIDE name - .type \name, #function - .global \name - /* Cache alignment for function entry */ - .balign 16 -\name: - .cfi_startproc -.endm - .macro END name .cfi_endproc .size \name, .-\name @@ -72,10 +63,4 @@ END \name .endm -.macro UNIMPLEMENTED_NO_HIDE name - ENTRY_NO_HIDE \name - brk 0 - END \name -.endm - #endif // ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_S_ diff --git a/runtime/arch/arm64/asm_support_arm64.h b/runtime/arch/arm64/asm_support_arm64.h index a926449964..989ecc6c5b 100644 --- a/runtime/arch/arm64/asm_support_arm64.h +++ b/runtime/arch/arm64/asm_support_arm64.h @@ -19,30 +19,8 @@ #include "asm_support.h" -// Note: these callee save methods loads require read barriers. -// Offset of field Runtime::callee_save_methods_[kSaveAll] verified in InitCpu -#define RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET 0 -// Offset of field Runtime::callee_save_methods_[kRefsOnly] verified in InitCpu -#define RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET 8 -// Offset of field Runtime::callee_save_methods_[kRefsAndArgs] verified in InitCpu -#define RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET 16 - -// Offset of field Thread::suspend_count_ -#define THREAD_FLAGS_OFFSET 0 -// Offset of field Thread::card_table_ -#define THREAD_CARD_TABLE_OFFSET 120 -// Offset of field Thread::exception_ -#define THREAD_EXCEPTION_OFFSET 128 -// Offset of field Thread::thin_lock_thread_id_ -#define THREAD_ID_OFFSET 12 - #define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 176 #define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 96 #define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 224 -// Expected size of a heap reference -#define HEAP_REFERENCE_SIZE 4 -// Expected size of a stack reference -#define STACK_REFERENCE_SIZE 4 - #endif // ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_H_ diff --git a/runtime/arch/arm64/context_arm64.cc b/runtime/arch/arm64/context_arm64.cc index 3eb92c8556..6aacda4d96 100644 --- a/runtime/arch/arm64/context_arm64.cc +++ b/runtime/arch/arm64/context_arm64.cc @@ -31,7 +31,7 @@ namespace arm64 { static constexpr uint64_t gZero = 0; void Arm64Context::Reset() { - for (size_t i = 0; i < kNumberOfCoreRegisters; i++) { + for (size_t i = 0; i < kNumberOfXRegisters; i++) { gprs_[i] = nullptr; } for (size_t i = 0; i < kNumberOfDRegisters; i++) { @@ -52,7 +52,7 @@ void Arm64Context::FillCalleeSaves(const StackVisitor& fr) { if (spill_count > 0) { // Lowest number spill is farthest away, walk registers and fill into context. int j = 1; - for (size_t i = 0; i < kNumberOfCoreRegisters; i++) { + for (size_t i = 0; i < kNumberOfXRegisters; i++) { if (((frame_info.CoreSpillMask() >> i) & 1) != 0) { gprs_[i] = fr.CalleeSaveAddress(spill_count - j, frame_info.FrameSizeInBytes()); j++; @@ -74,7 +74,8 @@ void Arm64Context::FillCalleeSaves(const StackVisitor& fr) { } bool Arm64Context::SetGPR(uint32_t reg, uintptr_t value) { - DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters)); + DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfXRegisters)); + DCHECK_NE(reg, static_cast<uint32_t>(XZR)); DCHECK_NE(gprs_[reg], &gZero); // Can't overwrite this static value since they are never reset. if (gprs_[reg] != nullptr) { *gprs_[reg] = value; @@ -146,11 +147,13 @@ void Arm64Context::SmashCallerSaves() { extern "C" void art_quick_do_long_jump(uint64_t*, uint64_t*); void Arm64Context::DoLongJump() { - uint64_t gprs[32]; + uint64_t gprs[kNumberOfXRegisters]; uint64_t fprs[kNumberOfDRegisters]; - // Do not use kNumberOfCoreRegisters, as this is with the distinction of SP and XZR - for (size_t i = 0; i < 32; ++i) { + // The long jump routine called below expects to find the value for SP at index 31. + DCHECK_EQ(SP, 31); + + for (size_t i = 0; i < kNumberOfXRegisters; ++i) { gprs[i] = gprs_[i] != nullptr ? *gprs_[i] : Arm64Context::kBadGprBase + i; } for (size_t i = 0; i < kNumberOfDRegisters; ++i) { diff --git a/runtime/arch/arm64/context_arm64.h b/runtime/arch/arm64/context_arm64.h index 1f69869099..d9a433bbce 100644 --- a/runtime/arch/arm64/context_arm64.h +++ b/runtime/arch/arm64/context_arm64.h @@ -34,7 +34,7 @@ class Arm64Context : public Context { void Reset() OVERRIDE; - void FillCalleeSaves(const StackVisitor& fr) OVERRIDE; + void FillCalleeSaves(const StackVisitor& fr) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void SetSP(uintptr_t new_sp) OVERRIDE { bool success = SetGPR(SP, new_sp); @@ -47,12 +47,12 @@ class Arm64Context : public Context { } uintptr_t* GetGPRAddress(uint32_t reg) OVERRIDE { - DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters)); + DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfXRegisters)); return gprs_[reg]; } bool GetGPR(uint32_t reg, uintptr_t* val) OVERRIDE { - DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters)); + DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfXRegisters)); if (gprs_[reg] == nullptr) { return false; } else { @@ -82,7 +82,7 @@ class Arm64Context : public Context { private: // Pointers to register locations, initialized to NULL or the specific registers below. - uintptr_t* gprs_[kNumberOfCoreRegisters]; + uintptr_t* gprs_[kNumberOfXRegisters]; uint64_t * fprs_[kNumberOfDRegisters]; // Hold values for sp and pc if they are not located within a stack frame. uintptr_t sp_, pc_; diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc index 70e93b3051..871e1d1b04 100644 --- a/runtime/arch/arm64/entrypoints_init_arm64.cc +++ b/runtime/arch/arm64/entrypoints_init_arm64.cc @@ -20,17 +20,11 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/entrypoint_utils.h" #include "entrypoints/math_entrypoints.h" +#include "entrypoints/runtime_asm_entrypoints.h" +#include "interpreter/interpreter.h" namespace art { -// Interpreter entrypoints. -extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result); -extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result); - // Portable entrypoints. extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*); extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*); diff --git a/runtime/arch/arm64/portable_entrypoints_arm64.S b/runtime/arch/arm64/portable_entrypoints_arm64.S index 41711b5c66..9e2c030d71 100644 --- a/runtime/arch/arm64/portable_entrypoints_arm64.S +++ b/runtime/arch/arm64/portable_entrypoints_arm64.S @@ -25,4 +25,6 @@ UNIMPLEMENTED art_portable_proxy_invoke_handler UNIMPLEMENTED art_portable_resolution_trampoline -UNIMPLEMENTED_NO_HIDE art_portable_to_interpreter_bridge +UNIMPLEMENTED art_portable_to_interpreter_bridge + +UNIMPLEMENTED art_portable_imt_conflict_trampoline diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 52a2a881f2..147d4348be 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -79,13 +79,16 @@ // Loads appropriate callee-save-method str xIP0, [sp] // Store ArtMethod* Runtime::callee_save_methods_[kRefsAndArgs] + // Place sp in Thread::Current()->top_quick_frame. + mov xIP0, sp + str xIP0, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET] .endm /* * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kRefsOnly). */ -.macro SETUP_REF_ONLY_CALLEE_SAVE_FRAME +.macro SETUP_REFS_ONLY_CALLEE_SAVE_FRAME adrp xIP0, :got:_ZN3art7Runtime9instance_E ldr xIP0, [xIP0, #:got_lo12:_ZN3art7Runtime9instance_E] @@ -133,11 +136,14 @@ mov xETR, xSELF // Loads appropriate callee-save-method - str xIP0, [sp] // Store ArtMethod* Runtime::callee_save_methods_[kRefsAndArgs] + str xIP0, [sp] // Store ArtMethod* Runtime::callee_save_methods_[kRefsOnly] + // Place sp in Thread::Current()->top_quick_frame. + mov xIP0, sp + str xIP0, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET] .endm // TODO: Probably no need to restore registers preserved by aapcs64. -.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME +.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // Restore xSELF. mov xSELF, xETR @@ -170,7 +176,7 @@ .cfi_adjust_cfa_offset -96 .endm -.macro POP_REF_ONLY_CALLEE_SAVE_FRAME +.macro POP_REFS_ONLY_CALLEE_SAVE_FRAME // Restore xSELF as it might be scratched. mov xSELF, xETR // ETR @@ -181,13 +187,13 @@ .cfi_adjust_cfa_offset -96 .endm -.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME +.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME ret .endm -.macro SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL +.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL sub sp, sp, #224 .cfi_adjust_cfa_offset 224 @@ -251,7 +257,7 @@ * * TODO This is probably too conservative - saving FP & LR. */ -.macro SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME +.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME adrp xIP0, :got:_ZN3art7Runtime9instance_E ldr xIP0, [xIP0, #:got_lo12:_ZN3art7Runtime9instance_E] @@ -260,15 +266,26 @@ // xIP0 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefAndArgs] . THIS_LOAD_REQUIRES_READ_BARRIER - ldr xIP0, [xIP0, RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET ] + ldr xIP0, [xIP0, RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET ] - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL str xIP0, [sp] // Store ArtMethod* Runtime::callee_save_methods_[kRefsAndArgs] + // Place sp in Thread::Current()->top_quick_frame. + mov xIP0, sp + str xIP0, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET] +.endm + +.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_X0 + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL + str x0, [sp, #0] // Store ArtMethod* to bottom of stack. + // Place sp in Thread::Current()->top_quick_frame. + mov xIP0, sp + str xIP0, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET] .endm // TODO: Probably no need to restore registers preserved by aapcs64. -.macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME +.macro RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME // Restore xSELF. mov xSELF, xETR @@ -340,10 +357,9 @@ .macro DELIVER_PENDING_EXCEPTION SETUP_SAVE_ALL_CALLEE_SAVE_FRAME mov x0, xSELF - mov x1, sp // Point of no return. - b artDeliverPendingExceptionFromCode // artDeliverPendingExceptionFromCode(Thread*, SP) + b artDeliverPendingExceptionFromCode // artDeliverPendingExceptionFromCode(Thread*) brk 0 // Unreached .endm @@ -376,8 +392,7 @@ ENTRY \c_name SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context mov x0, xSELF // pass Thread::Current - mov x1, sp // pass SP - b \cxx_name // \cxx_name(Thread*, SP) + b \cxx_name // \cxx_name(Thread*) END \c_name .endm @@ -386,8 +401,7 @@ END \c_name ENTRY \c_name SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context. mov x1, xSELF // pass Thread::Current. - mov x2, sp // pass SP. - b \cxx_name // \cxx_name(arg, Thread*, SP). + b \cxx_name // \cxx_name(arg, Thread*). brk 0 END \c_name .endm @@ -397,8 +411,7 @@ END \c_name ENTRY \c_name SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context mov x2, xSELF // pass Thread::Current - mov x3, sp // pass SP - b \cxx_name // \cxx_name(arg1, arg2, Thread*, SP) + b \cxx_name // \cxx_name(arg1, arg2, Thread*) brk 0 END \c_name .endm @@ -458,7 +471,7 @@ ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFr .macro INVOKE_TRAMPOLINE c_name, cxx_name .extern \cxx_name ENTRY \c_name - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME // save callee saves in case allocation triggers GC + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME // save callee saves in case allocation triggers GC // Helper signature is always // (method_idx, *this_object, *caller_method, *self, sp) @@ -467,7 +480,7 @@ ENTRY \c_name mov x4, sp bl \cxx_name // (method_idx, this, caller, Thread*, SP) mov xIP0, x1 // save Method*->code_ - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME cbz x0, 1f // did we find the target? if not go to exception delivery br xIP0 // tail call to target 1: @@ -551,7 +564,7 @@ SAVE_SIZE_AND_METHOD=SAVE_SIZE+STACK_REFERENCE_SIZE .macro INVOKE_STUB_CALL_AND_RETURN // load method-> METHOD_QUICK_CODE_OFFSET - ldr x9, [x0 , #METHOD_QUICK_CODE_OFFSET] + ldr x9, [x0 , #MIRROR_ART_METHOD_QUICK_CODE_OFFSET] // Branch to method. blr x9 @@ -945,7 +958,7 @@ END art_quick_do_long_jump .extern artLockObjectFromCode ENTRY art_quick_lock_object cbz w0, .Lslow_lock - add x4, x0, #LOCK_WORD_OFFSET // exclusive load/store had no immediate anymore + add x4, x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET // exclusive load/store has no immediate anymore .Lretry_lock: ldr w2, [xSELF, #THREAD_ID_OFFSET] // TODO: Can the thread ID really change during the loop? ldxr w1, [x4] @@ -966,14 +979,13 @@ ENTRY art_quick_lock_object add w2, w1, #65536 // increment count in lock word placing in w2 for storing lsr w1, w2, 30 // if either of the top two bits are set, we overflowed. cbnz w1, .Lslow_lock // if we overflow the count go slow path - str w2, [x0, #LOCK_WORD_OFFSET]// no need for stxr as we hold the lock + str w2, [x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] // no need for stxr as we hold the lock ret .Lslow_lock: - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save callee saves in case we block + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case we block mov x1, xSELF // pass Thread::Current - mov x2, sp // pass SP - bl artLockObjectFromCode // (Object* obj, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + bl artLockObjectFromCode // (Object* obj, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME RETURN_IF_W0_IS_ZERO_OR_DELIVER END art_quick_lock_object @@ -986,7 +998,7 @@ END art_quick_lock_object .extern artUnlockObjectFromCode ENTRY art_quick_unlock_object cbz x0, .Lslow_unlock - ldr w1, [x0, #LOCK_WORD_OFFSET] + ldr w1, [x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] lsr w2, w1, 30 cbnz w2, .Lslow_unlock // if either of the top two bits are set, go slow path ldr w2, [xSELF, #THREAD_ID_OFFSET] @@ -997,18 +1009,17 @@ ENTRY art_quick_unlock_object bpl .Lrecursive_thin_unlock // transition to unlocked, w3 holds 0 dmb ish // full (LoadStore|StoreStore) memory barrier - str w3, [x0, #LOCK_WORD_OFFSET] + str w3, [x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] ret .Lrecursive_thin_unlock: sub w1, w1, #65536 - str w1, [x0, #LOCK_WORD_OFFSET] + str w1, [x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] ret .Lslow_unlock: - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save callee saves in case exception allocation triggers GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case exception allocation triggers GC mov x1, xSELF // pass Thread::Current - mov x2, sp // pass SP - bl artUnlockObjectFromCode // (Object* obj, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + bl artUnlockObjectFromCode // (Object* obj, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME RETURN_IF_W0_IS_ZERO_OR_DELIVER END art_quick_unlock_object @@ -1058,8 +1069,7 @@ ENTRY art_quick_check_cast SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context mov x2, xSELF // pass Thread::Current - mov x3, sp // pass SP - b artThrowClassCastException // (Class*, Class*, Thread*, SP) + b artThrowClassCastException // (Class*, Class*, Thread*) brk 0 // We should not return here... END art_quick_check_cast @@ -1082,7 +1092,7 @@ ENTRY art_quick_aput_obj_with_null_and_bound_check END art_quick_aput_obj_with_null_and_bound_check ENTRY art_quick_aput_obj_with_bound_check - ldr w3, [x0, #ARRAY_LENGTH_OFFSET] + ldr w3, [x0, #MIRROR_ARRAY_LENGTH_OFFSET] cmp w3, w1 bhi art_quick_aput_obj mov x0, x1 @@ -1092,16 +1102,16 @@ END art_quick_aput_obj_with_bound_check ENTRY art_quick_aput_obj cbz x2, .Ldo_aput_null - ldr w3, [x0, #CLASS_OFFSET] // Heap reference = 32b + ldr w3, [x0, #MIRROR_OBJECT_CLASS_OFFSET] // Heap reference = 32b // This also zero-extends to x3 - ldr w4, [x2, #CLASS_OFFSET] // Heap reference = 32b + ldr w4, [x2, #MIRROR_OBJECT_CLASS_OFFSET] // Heap reference = 32b // This also zero-extends to x4 - ldr w3, [x3, #CLASS_COMPONENT_TYPE_OFFSET] // Heap reference = 32b + ldr w3, [x3, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET] // Heap reference = 32b // This also zero-extends to x3 cmp w3, w4 // value's type == array's component type - trivial assignability bne .Lcheck_assignability .Ldo_aput: - add x3, x0, #OBJECT_ARRAY_DATA_OFFSET + add x3, x0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET // "Compress" = do nothing str w2, [x3, x1, lsl #2] // Heap reference = 32b ldr x3, [xSELF, #THREAD_CARD_TABLE_OFFSET] @@ -1109,7 +1119,7 @@ ENTRY art_quick_aput_obj strb w3, [x3, x0] ret .Ldo_aput_null: - add x3, x0, #OBJECT_ARRAY_DATA_OFFSET + add x3, x0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET // "Compress" = do nothing str w2, [x3, x1, lsl #2] // Heap reference = 32b ret @@ -1146,7 +1156,7 @@ ENTRY art_quick_aput_obj add sp, sp, #48 .cfi_adjust_cfa_offset -48 - add x3, x0, #OBJECT_ARRAY_DATA_OFFSET + add x3, x0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET // "Compress" = do nothing str w2, [x3, x1, lsl #2] // Heap reference = 32b ldr x3, [xSELF, #THREAD_CARD_TABLE_OFFSET] @@ -1168,8 +1178,7 @@ ENTRY art_quick_aput_obj SETUP_SAVE_ALL_CALLEE_SAVE_FRAME mov x1, x2 // Pass value. mov x2, xSELF // Pass Thread::Current. - mov x3, sp // Pass SP. - b artThrowArrayStoreException // (Object*, Object*, Thread*, SP). + b artThrowArrayStoreException // (Object*, Object*, Thread*). brk 0 // Unreached. END art_quick_aput_obj @@ -1177,11 +1186,10 @@ END art_quick_aput_obj .macro TWO_ARG_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC mov x2, xSELF // pass Thread::Current - mov x3, sp // pass SP - bl \entrypoint // (uint32_t type_idx, Method* method, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + bl \entrypoint // (uint32_t type_idx, Method* method, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME \return DELIVER_PENDING_EXCEPTION END \name @@ -1191,11 +1199,10 @@ END \name .macro THREE_ARG_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC mov x3, xSELF // pass Thread::Current - mov x4, sp // pass SP bl \entrypoint - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME \return DELIVER_PENDING_EXCEPTION END \name @@ -1205,12 +1212,11 @@ END \name .macro ONE_ARG_REF_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC ldr w1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] // Load referrer mov x2, xSELF // pass Thread::Current - mov x3, sp // pass SP bl \entrypoint // (uint32_t type_idx, Method* method, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME \return END \name .endm @@ -1218,12 +1224,11 @@ END \name .macro TWO_ARG_REF_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC ldr w2, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] // Load referrer mov x3, xSELF // pass Thread::Current - mov x4, sp // pass SP bl \entrypoint - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME \return END \name .endm @@ -1231,12 +1236,11 @@ END \name .macro THREE_ARG_REF_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC ldr w3, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] // Load referrer mov x4, xSELF // pass Thread::Current - mov x5, sp // pass SP bl \entrypoint - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME \return END \name .endm @@ -1281,20 +1285,19 @@ TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCode, RETURN_I THREE_ARG_REF_DOWNCALL art_quick_set8_instance, artSet8InstanceFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER THREE_ARG_REF_DOWNCALL art_quick_set16_instance, artSet16InstanceFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER THREE_ARG_REF_DOWNCALL art_quick_set32_instance, artSet32InstanceFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER -THREE_ARG_DOWNCALL art_quick_set64_instance, artSet64InstanceFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER +THREE_ARG_REF_DOWNCALL art_quick_set64_instance, artSet64InstanceFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER // This is separated out as the argument order is different. .extern artSet64StaticFromCode ENTRY art_quick_set64_static - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC mov x3, x1 // Store value ldr w1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] // Load referrer mov x2, x3 // Put value param mov x3, xSELF // pass Thread::Current - mov x4, sp // pass SP bl artSet64StaticFromCode - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME RETURN_IF_W0_IS_ZERO_OR_DELIVER END art_quick_set64_static @@ -1320,18 +1323,16 @@ ENTRY art_quick_test_suspend ret // return if flags == 0 .Lneed_suspend: mov x0, xSELF - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save callee saves for stack crawl - mov x1, sp - bl artTestSuspendFromCode // (Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves for stack crawl + bl artTestSuspendFromCode // (Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN END art_quick_test_suspend ENTRY art_quick_implicit_suspend mov x0, xSELF - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save callee saves for stack crawl - mov x1, sp - bl artTestSuspendFromCode // (Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves for stack crawl + bl artTestSuspendFromCode // (Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN END art_quick_implicit_suspend /* @@ -1341,19 +1342,18 @@ END art_quick_implicit_suspend */ .extern artQuickProxyInvokeHandler ENTRY art_quick_proxy_invoke_handler - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME - str x0, [sp, #0] // place proxy method at bottom of frame + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_X0 mov x2, xSELF // pass Thread::Current mov x3, sp // pass SP bl artQuickProxyInvokeHandler // (Method* proxy method, receiver, Thread*, SP) // Use xETR as xSELF might be scratched by native function above. ldr x2, [xETR, THREAD_EXCEPTION_OFFSET] cbnz x2, .Lexception_in_proxy // success if no exception is pending - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME // Restore frame + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME // Restore frame fmov d0, x0 // Store result in d0 in case it was float or double ret // return on success .Lexception_in_proxy: - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME DELIVER_PENDING_EXCEPTION END art_quick_proxy_invoke_handler @@ -1363,24 +1363,24 @@ END art_quick_proxy_invoke_handler */ ENTRY art_quick_imt_conflict_trampoline ldr w0, [sp, #0] // load caller Method* - ldr w0, [x0, #METHOD_DEX_CACHE_METHODS_OFFSET] // load dex_cache_resolved_methods - add x0, x0, #OBJECT_ARRAY_DATA_OFFSET // get starting address of data + ldr w0, [x0, #MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET] // load dex_cache_resolved_methods + add x0, x0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET // get starting address of data ldr w0, [x0, xIP1, lsl 2] // load the target method b art_quick_invoke_interface_trampoline END art_quick_imt_conflict_trampoline ENTRY art_quick_resolution_trampoline - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME mov x2, xSELF mov x3, sp bl artQuickResolutionTrampoline // (called, receiver, Thread*, SP) cbz x0, 1f mov xIP0, x0 // Remember returned code pointer in xIP0. ldr w0, [sp, #0] // artQuickResolutionTrampoline puts called method in *SP. - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME br xIP0 1: - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME DELIVER_PENDING_EXCEPTION END art_quick_resolution_trampoline @@ -1438,9 +1438,8 @@ END art_quick_resolution_trampoline /* * Called to do a generic JNI down-call */ -ENTRY_NO_HIDE art_quick_generic_jni_trampoline - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL - str x0, [sp, #0] // Store native ArtMethod* to bottom of stack. +ENTRY art_quick_generic_jni_trampoline + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_X0 // Save SP , so we can have static CFI info. mov x28, sp @@ -1513,7 +1512,7 @@ ENTRY_NO_HIDE art_quick_generic_jni_trampoline cbnz x1, .Lexception_in_native // Tear down the callee-save frame. - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME // store into fpr, for when it's a fpr return... fmov d0, x0 @@ -1523,7 +1522,7 @@ ENTRY_NO_HIDE art_quick_generic_jni_trampoline mov sp, x28 .cfi_def_cfa_register sp .Lexception_in_native: - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME DELIVER_PENDING_EXCEPTION END art_quick_generic_jni_trampoline @@ -1534,8 +1533,8 @@ END art_quick_generic_jni_trampoline * x0 = method being called/to bridge to. * x1..x7, d0..d7 = arguments to that method. */ -ENTRY_NO_HIDE art_quick_to_interpreter_bridge - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME // Set up frame and save arguments. +ENTRY art_quick_to_interpreter_bridge + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME // Set up frame and save arguments. // x0 will contain mirror::ArtMethod* method. mov x1, xSELF // How to get Thread::Current() ??? @@ -1545,7 +1544,7 @@ ENTRY_NO_HIDE art_quick_to_interpreter_bridge // mirror::ArtMethod** sp) bl artQuickToInterpreterBridge - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME // TODO: no need to restore arguments in this case. + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME // TODO: no need to restore arguments in this case. fmov d0, x0 @@ -1558,19 +1557,18 @@ END art_quick_to_interpreter_bridge // .extern artInstrumentationMethodEntryFromCode ENTRY art_quick_instrumentation_entry - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME mov x20, x0 // Preserve method reference in a callee-save. mov x2, xSELF - mov x3, sp - mov x4, xLR - bl artInstrumentationMethodEntryFromCode // (Method*, Object*, Thread*, SP, LR) + mov x3, xLR + bl artInstrumentationMethodEntryFromCode // (Method*, Object*, Thread*, LR) mov xIP0, x0 // x0 = result of call. mov x0, x20 // Reload method reference. - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME // Note: will restore xSELF + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME // Note: will restore xSELF adr xLR, art_quick_instrumentation_exit br xIP0 // Tail-call method with lr set to art_quick_instrumentation_exit. END art_quick_instrumentation_entry @@ -1579,7 +1577,7 @@ END art_quick_instrumentation_entry ENTRY art_quick_instrumentation_exit mov xLR, #0 // Clobber LR for later checks. - SETUP_REF_ONLY_CALLEE_SAVE_FRAME + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // We need to save x0 and d0. We could use a callee-save from SETUP_REF_ONLY, but then // we would need to fully restore it. As there are a lot of callee-save registers, it seems @@ -1602,7 +1600,7 @@ ENTRY art_quick_instrumentation_exit ldr x0, [sp], 16 // Restore integer result, and drop stack area. .cfi_adjust_cfa_offset 16 - POP_REF_ONLY_CALLEE_SAVE_FRAME + POP_REFS_ONLY_CALLEE_SAVE_FRAME br xIP0 // Tail-call out. END art_quick_instrumentation_exit @@ -1615,8 +1613,7 @@ END art_quick_instrumentation_exit ENTRY art_quick_deoptimize SETUP_SAVE_ALL_CALLEE_SAVE_FRAME mov x0, xSELF // Pass thread. - mov x1, sp // Pass SP. - bl artDeoptimize // artDeoptimize(Thread*, SP) + bl artDeoptimize // artDeoptimize(Thread*) brk 0 END art_quick_deoptimize @@ -1631,9 +1628,9 @@ END art_quick_deoptimize * w2: Starting offset in string data */ ENTRY art_quick_indexof - ldr w3, [x0, #STRING_COUNT_OFFSET] - ldr w4, [x0, #STRING_OFFSET_OFFSET] - ldr w0, [x0, #STRING_VALUE_OFFSET] // x0 ? + ldr w3, [x0, #MIRROR_STRING_COUNT_OFFSET] + ldr w4, [x0, #MIRROR_STRING_OFFSET_OFFSET] + ldr w0, [x0, #MIRROR_STRING_VALUE_OFFSET] // x0 ? /* Clamp start to [0..count] */ cmp w2, #0 @@ -1642,7 +1639,7 @@ ENTRY art_quick_indexof csel w2, w3, w2, gt /* Build a pointer to the start of the string data */ - add x0, x0, #STRING_DATA_OFFSET + add x0, x0, #MIRROR_CHAR_ARRAY_DATA_OFFSET add x0, x0, x4, lsl #1 /* Save a copy to compute result */ @@ -1736,12 +1733,12 @@ ENTRY art_quick_string_compareto ret 1: // Different string objects. - ldr w6, [x2, #STRING_OFFSET_OFFSET] - ldr w5, [x1, #STRING_OFFSET_OFFSET] - ldr w4, [x2, #STRING_COUNT_OFFSET] - ldr w3, [x1, #STRING_COUNT_OFFSET] - ldr w2, [x2, #STRING_VALUE_OFFSET] - ldr w1, [x1, #STRING_VALUE_OFFSET] + ldr w6, [x2, #MIRROR_STRING_OFFSET_OFFSET] + ldr w5, [x1, #MIRROR_STRING_OFFSET_OFFSET] + ldr w4, [x2, #MIRROR_STRING_COUNT_OFFSET] + ldr w3, [x1, #MIRROR_STRING_COUNT_OFFSET] + ldr w2, [x2, #MIRROR_STRING_VALUE_OFFSET] + ldr w1, [x1, #MIRROR_STRING_VALUE_OFFSET] /* * Now: CharArray* Offset Count @@ -1761,8 +1758,8 @@ ENTRY art_quick_string_compareto add x1, x1, w5, sxtw #1 // Add offset in CharArray to array. - add x2, x2, #STRING_DATA_OFFSET - add x1, x1, #STRING_DATA_OFFSET + add x2, x2, #MIRROR_CHAR_ARRAY_DATA_OFFSET + add x1, x1, #MIRROR_CHAR_ARRAY_DATA_OFFSET // TODO: Tune this value. // Check for long string, do memcmp16 for them. diff --git a/runtime/arch/arm64/registers_arm64.cc b/runtime/arch/arm64/registers_arm64.cc index 87901e342b..ea4383a8d0 100644 --- a/runtime/arch/arm64/registers_arm64.cc +++ b/runtime/arch/arm64/registers_arm64.cc @@ -32,11 +32,11 @@ static const char* kWRegisterNames[] = { "w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7", "w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15", "w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23", "w24", "w25", "w26", "w27", "w28", "w29", - "w30", "wsp", "wxr" + "w30", "wsp", "wzr" }; -std::ostream& operator<<(std::ostream& os, const Register& rhs) { - if (rhs >= X0 && rhs <= XZR) { +std::ostream& operator<<(std::ostream& os, const XRegister& rhs) { + if (rhs >= X0 && rhs < kNumberOfXRegisters) { os << kRegisterNames[rhs]; } else { os << "XRegister[" << static_cast<int>(rhs) << "]"; @@ -45,7 +45,7 @@ std::ostream& operator<<(std::ostream& os, const Register& rhs) { } std::ostream& operator<<(std::ostream& os, const WRegister& rhs) { - if (rhs >= W0 && rhs <= WZR) { + if (rhs >= W0 && rhs < kNumberOfWRegisters) { os << kWRegisterNames[rhs]; } else { os << "WRegister[" << static_cast<int>(rhs) << "]"; diff --git a/runtime/arch/arm64/registers_arm64.h b/runtime/arch/arm64/registers_arm64.h index 9ccab70bb9..51ae184e7e 100644 --- a/runtime/arch/arm64/registers_arm64.h +++ b/runtime/arch/arm64/registers_arm64.h @@ -23,7 +23,7 @@ namespace art { namespace arm64 { // Values for GP XRegisters - 64bit registers. -enum Register { +enum XRegister { X0 = 0, X1 = 1, X2 = 2, @@ -55,20 +55,20 @@ enum Register { X28 = 28, X29 = 29, X30 = 30, - X31 = 31, - TR = 18, // ART Thread Register - Managed Runtime (Caller Saved Reg) - ETR = 21, // ART Thread Register - External Calls (Callee Saved Reg) - IP0 = 16, // Used as scratch by VIXL. - IP1 = 17, // Used as scratch by ART JNI Assembler. - FP = 29, - LR = 30, - SP = 31, // SP is X31 and overlaps with XRZ but we encode it as a - // special register, due to the different instruction semantics. - XZR = 32, - kNumberOfCoreRegisters = 33, + SP = 31, // SP and XZR are encoded in instructions using the register + XZR = 32, // code `31`, the context deciding which is used. We use a + // different enum value to distinguish between the two. + kNumberOfXRegisters = 33, + // Aliases. + TR = X18, // ART Thread Register - Managed Runtime (Caller Saved Reg) + ETR = X21, // ART Thread Register - External Calls (Callee Saved Reg) + IP0 = X16, // Used as scratch by VIXL. + IP1 = X17, // Used as scratch by ART JNI Assembler. + FP = X29, + LR = X30, kNoRegister = -1, }; -std::ostream& operator<<(std::ostream& os, const Register& rhs); +std::ostream& operator<<(std::ostream& os, const XRegister& rhs); // Values for GP WRegisters - 32bit registers. enum WRegister { @@ -103,9 +103,9 @@ enum WRegister { W28 = 28, W29 = 29, W30 = 30, - W31 = 31, - WZR = 31, - kNumberOfWRegisters = 32, + WSP = 31, + WZR = 32, + kNumberOfWRegisters = 33, kNoWRegister = -1, }; std::ostream& operator<<(std::ostream& os, const WRegister& rhs); diff --git a/runtime/arch/mips/asm_support_mips.S b/runtime/arch/mips/asm_support_mips.S index d8ec9cd6c9..0d18f1a9ba 100644 --- a/runtime/arch/mips/asm_support_mips.S +++ b/runtime/arch/mips/asm_support_mips.S @@ -26,15 +26,31 @@ // Register holding Thread::Current(). #define rSELF $s1 - - /* Cache alignment for function entry */ + // Declare a function called name, sets up $gp. .macro ENTRY name .type \name, %function .global \name + // Cache alignment for function entry. .balign 16 \name: .cfi_startproc - /* Ensure we get a sane starting CFA. */ + // Ensure we get a sane starting CFA. + .cfi_def_cfa $sp,0 + // Load $gp. We expect that ".set noreorder" is in effect. + .cpload $t9 + // Declare a local convenience label to be branched to when $gp is already set up. +.L\name\()_gp_set: +.endm + + // Declare a function called name, doesn't set up $gp. +.macro ENTRY_NO_GP name + .type \name, %function + .global \name + // Cache alignment for function entry. + .balign 16 +\name: + .cfi_startproc + // Ensure we get a sane starting CFA. .cfi_def_cfa $sp,0 .endm @@ -43,11 +59,6 @@ .size \name, .-\name .endm - /* Generates $gp for function calls */ -.macro GENERATE_GLOBAL_POINTER - .cpload $t9 -.endm - .macro UNIMPLEMENTED name ENTRY \name break diff --git a/runtime/arch/mips/asm_support_mips.h b/runtime/arch/mips/asm_support_mips.h index 6add93b40e..5bece18eaf 100644 --- a/runtime/arch/mips/asm_support_mips.h +++ b/runtime/arch/mips/asm_support_mips.h @@ -19,18 +19,8 @@ #include "asm_support.h" -// Offset of field Thread::tls32_.state_and_flags verified in InitCpu -#define THREAD_FLAGS_OFFSET 0 -// Offset of field Thread::tlsPtr_.card_table verified in InitCpu -#define THREAD_CARD_TABLE_OFFSET 120 -// Offset of field Thread::tlsPtr_.exception verified in InitCpu -#define THREAD_EXCEPTION_OFFSET 124 - #define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 64 #define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 64 #define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 64 -// Expected size of a heap reference -#define HEAP_REFERENCE_SIZE 4 - #endif // ART_RUNTIME_ARCH_MIPS_ASM_SUPPORT_MIPS_H_ diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 25e911d765..db0f71fa37 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "atomic.h" #include "entrypoints/interpreter/interpreter_entrypoints.h" #include "entrypoints/jni/jni_entrypoints.h" #include "entrypoints/portable/portable_entrypoints.h" @@ -21,18 +22,11 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/entrypoint_utils.h" #include "entrypoints/math_entrypoints.h" -#include "atomic.h" +#include "entrypoints/runtime_asm_entrypoints.h" +#include "interpreter/interpreter.h" namespace art { -// Interpreter entrypoints. -extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result); -extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result); - // Portable entrypoints. extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*); extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*); diff --git a/runtime/arch/mips/jni_entrypoints_mips.S b/runtime/arch/mips/jni_entrypoints_mips.S index e5f4a79237..9a79467ede 100644 --- a/runtime/arch/mips/jni_entrypoints_mips.S +++ b/runtime/arch/mips/jni_entrypoints_mips.S @@ -24,7 +24,6 @@ */ .extern artFindNativeMethod ENTRY art_jni_dlsym_lookup_stub - GENERATE_GLOBAL_POINTER addiu $sp, $sp, -32 # leave room for $a0, $a1, $a2, $a3, and $ra .cfi_adjust_cfa_offset 32 sw $ra, 16($sp) diff --git a/runtime/arch/mips/memcmp16_mips.S b/runtime/arch/mips/memcmp16_mips.S index 0196edc2c5..aef81afeca 100644 --- a/runtime/arch/mips/memcmp16_mips.S +++ b/runtime/arch/mips/memcmp16_mips.S @@ -20,7 +20,7 @@ #include "asm_support_mips.S" // u4 __memcmp16(const u2*, const u2*, size_t); -ENTRY __memcmp16 +ENTRY_NO_GP __memcmp16 li $t0,0 li $t1,0 beqz $a2,done /* 0 length string */ diff --git a/runtime/arch/mips/portable_entrypoints_mips.S b/runtime/arch/mips/portable_entrypoints_mips.S index 7545ce0d6c..d7e7a8e96b 100644 --- a/runtime/arch/mips/portable_entrypoints_mips.S +++ b/runtime/arch/mips/portable_entrypoints_mips.S @@ -21,7 +21,6 @@ .extern artPortableProxyInvokeHandler ENTRY art_portable_proxy_invoke_handler - GENERATE_GLOBAL_POINTER # Fake callee save ref and args frame set up, note portable doesn't use callee save frames. # TODO: just save the registers that are needed in artPortableProxyInvokeHandler. addiu $sp, $sp, -64 @@ -72,7 +71,6 @@ END art_portable_proxy_invoke_handler * [sp + 20] = result type char */ ENTRY art_portable_invoke_stub - GENERATE_GLOBAL_POINTER sw $a0, 0($sp) # save out a0 addiu $sp, $sp, -16 # spill s0, s1, fp, ra .cfi_adjust_cfa_offset 16 @@ -87,7 +85,7 @@ ENTRY art_portable_invoke_stub move $fp, $sp # save sp in fp .cfi_def_cfa_register 30 move $s1, $a3 # move managed thread pointer into s1 - addiu $s0, $zero, SUSPEND_CHECK_INTERVAL # reset s0 to suspend check interval + addiu $s0, $zero, SUSPEND_CHECK_INTERVAL # reset s0 to suspend check interval. TODO: unused? addiu $t0, $a2, 16 # create space for method pointer in frame srl $t0, $t0, 3 # shift the frame size right 3 sll $t0, $t0, 3 # shift the frame size left 3 to align to 16 bytes @@ -100,7 +98,7 @@ ENTRY art_portable_invoke_stub lw $a1, 4($sp) # copy arg value for a1 lw $a2, 8($sp) # copy arg value for a2 lw $a3, 12($sp) # copy arg value for a3 - lw $t9, METHOD_PORTABLE_CODE_OFFSET($a0) # get pointer to the code + lw $t9, MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET($a0) # get pointer to the code jalr $t9 # call the method sw $zero, 0($sp) # store NULL for method* at bottom of frame move $sp, $fp # restore the stack @@ -131,3 +129,4 @@ END art_portable_invoke_stub UNIMPLEMENTED art_portable_resolution_trampoline UNIMPLEMENTED art_portable_to_interpreter_bridge +UNIMPLEMENTED art_portable_imt_conflict_trampoline diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 609c65a38f..905b8676be 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -29,7 +29,8 @@ /* * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kSaveAll) - * callee-save: $s0-$s8 + $gp + $ra, 11 total + 1 word padding + 4 open words for args + * Callee-save: $s0-$s8 + $gp + $ra, 11 total + 1 word padding + 4 open words for args + * Clobbers $t0 and $gp */ .macro SETUP_SAVE_ALL_CALLEE_SAVE_FRAME addiu $sp, $sp, -64 @@ -63,6 +64,12 @@ sw $s0, 20($sp) .cfi_rel_offset 16, 20 # 1 word for alignment, 4 open words for args $a0-$a3, bottom will hold Method* + + ld $t0, _ZN3art7Runtime9instance_E + THIS_LOAD_REQUIRES_READ_BARRIER + ld $t0, RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET($t0) + sw $t0, 0($sp) # Place Method* at bottom of stack. + sw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame. .endm /* @@ -71,7 +78,7 @@ * Does not include rSUSPEND or rSELF * callee-save: $s2-$s8 + $gp + $ra, 9 total + 3 words padding + 4 open words for args */ -.macro SETUP_REF_ONLY_CALLEE_SAVE_FRAME +.macro SETUP_REFS_ONLY_CALLEE_SAVE_FRAME addiu $sp, $sp, -64 .cfi_adjust_cfa_offset 64 @@ -99,9 +106,15 @@ sw $s2, 28($sp) .cfi_rel_offset 18, 28 # 3 words for alignment and extra args, 4 open words for args $a0-$a3, bottom will hold Method* + + ld $t0, _ZN3art7Runtime9instance_E + THIS_LOAD_REQUIRES_READ_BARRIER + ld $t0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET($t0) + sw $t0, 0($sp) # Place Method* at bottom of stack. + sw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame. .endm -.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME +.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME lw $ra, 60($sp) .cfi_restore 31 lw $s8, 56($sp) @@ -124,7 +137,7 @@ .cfi_adjust_cfa_offset -64 .endm -.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN +.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN lw $ra, 60($sp) .cfi_restore 31 lw $s8, 56($sp) @@ -153,7 +166,7 @@ * Runtime::CreateCalleeSaveMethod(kRefsAndArgs). Restoration assumes non-moving GC. * callee-save: $a1-$a3, $s2-$s8 + $gp + $ra, 12 total + 3 words padding + method* */ -.macro SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME +.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME addiu $sp, $sp, -64 .cfi_adjust_cfa_offset 64 @@ -187,9 +200,15 @@ sw $a1, 4($sp) .cfi_rel_offset 5, 4 # bottom will hold Method* + + ld $t0, _ZN3art7Runtime9instance_E + THIS_LOAD_REQUIRES_READ_BARRIER + ld $t0, RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET($t0) + sw $t0, 0($sp) # Place Method* at bottom of stack. + sw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame. .endm -.macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME +.macro RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME lw $ra, 60($sp) .cfi_restore 31 lw $s8, 56($sp) @@ -224,15 +243,14 @@ */ .macro DELIVER_PENDING_EXCEPTION SETUP_SAVE_ALL_CALLEE_SAVE_FRAME # save callee saves for throw - move $a0, rSELF # pass Thread::Current la $t9, artDeliverPendingExceptionFromCode - jr $t9 # artDeliverPendingExceptionFromCode(Thread*, $sp) - move $a1, $sp # pass $sp + jr $t9 # artDeliverPendingExceptionFromCode(Thread*) + move $a0, rSELF # pass Thread::Current .endm .macro RETURN_IF_NO_EXCEPTION lw $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_ - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME bnez $t0, 1f # success if no exception is pending nop jr $ra @@ -242,7 +260,7 @@ .endm .macro RETURN_IF_ZERO - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME bnez $v0, 1f # success? nop jr $ra # return on success @@ -252,7 +270,7 @@ .endm .macro RETURN_IF_RESULT_IS_NON_ZERO - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME beqz $v0, 1f # success? nop jr $ra # return on success @@ -342,12 +360,10 @@ END art_quick_do_long_jump * the bottom of the thread. On entry r0 holds Throwable* */ ENTRY art_quick_deliver_exception - GENERATE_GLOBAL_POINTER SETUP_SAVE_ALL_CALLEE_SAVE_FRAME - move $a1, rSELF # pass Thread::Current la $t9, artDeliverExceptionFromCode - jr $t9 # artDeliverExceptionFromCode(Throwable*, Thread*, $sp) - move $a2, $sp # pass $sp + jr $t9 # artDeliverExceptionFromCode(Throwable*, Thread*) + move $a1, rSELF # pass Thread::Current END art_quick_deliver_exception /* @@ -355,13 +371,10 @@ END art_quick_deliver_exception */ .extern artThrowNullPointerExceptionFromCode ENTRY art_quick_throw_null_pointer_exception - GENERATE_GLOBAL_POINTER -.Lart_quick_throw_null_pointer_exception_gp_set: SETUP_SAVE_ALL_CALLEE_SAVE_FRAME - move $a0, rSELF # pass Thread::Current la $t9, artThrowNullPointerExceptionFromCode - jr $t9 # artThrowNullPointerExceptionFromCode(Thread*, $sp) - move $a1, $sp # pass $sp + jr $t9 # artThrowNullPointerExceptionFromCode(Thread*) + move $a0, rSELF # pass Thread::Current END art_quick_throw_null_pointer_exception /* @@ -369,12 +382,10 @@ END art_quick_throw_null_pointer_exception */ .extern artThrowDivZeroFromCode ENTRY art_quick_throw_div_zero - GENERATE_GLOBAL_POINTER SETUP_SAVE_ALL_CALLEE_SAVE_FRAME - move $a0, rSELF # pass Thread::Current la $t9, artThrowDivZeroFromCode - jr $t9 # artThrowDivZeroFromCode(Thread*, $sp) - move $a1, $sp # pass $sp + jr $t9 # artThrowDivZeroFromCode(Thread*) + move $a0, rSELF # pass Thread::Current END art_quick_throw_div_zero /* @@ -382,13 +393,10 @@ END art_quick_throw_div_zero */ .extern artThrowArrayBoundsFromCode ENTRY art_quick_throw_array_bounds - GENERATE_GLOBAL_POINTER -.Lart_quick_throw_array_bounds_gp_set: SETUP_SAVE_ALL_CALLEE_SAVE_FRAME - move $a2, rSELF # pass Thread::Current la $t9, artThrowArrayBoundsFromCode - jr $t9 # artThrowArrayBoundsFromCode(index, limit, Thread*, $sp) - move $a3, $sp # pass $sp + jr $t9 # artThrowArrayBoundsFromCode(index, limit, Thread*) + move $a2, rSELF # pass Thread::Current END art_quick_throw_array_bounds /* @@ -396,12 +404,10 @@ END art_quick_throw_array_bounds */ .extern artThrowStackOverflowFromCode ENTRY art_quick_throw_stack_overflow - GENERATE_GLOBAL_POINTER SETUP_SAVE_ALL_CALLEE_SAVE_FRAME - move $a0, rSELF # pass Thread::Current la $t9, artThrowStackOverflowFromCode - jr $t9 # artThrowStackOverflowFromCode(Thread*, $sp) - move $a1, $sp # pass $sp + jr $t9 # artThrowStackOverflowFromCode(Thread*) + move $a0, rSELF # pass Thread::Current END art_quick_throw_stack_overflow /* @@ -409,12 +415,10 @@ END art_quick_throw_stack_overflow */ .extern artThrowNoSuchMethodFromCode ENTRY art_quick_throw_no_such_method - GENERATE_GLOBAL_POINTER SETUP_SAVE_ALL_CALLEE_SAVE_FRAME - move $a1, rSELF # pass Thread::Current la $t9, artThrowNoSuchMethodFromCode - jr $t9 # artThrowNoSuchMethodFromCode(method_idx, Thread*, $sp) - move $a2, $sp # pass $sp + jr $t9 # artThrowNoSuchMethodFromCode(method_idx, Thread*) + move $a1, rSELF # pass Thread::Current END art_quick_throw_no_such_method /* @@ -436,9 +440,8 @@ END art_quick_throw_no_such_method .macro INVOKE_TRAMPOLINE c_name, cxx_name .extern \cxx_name ENTRY \c_name - GENERATE_GLOBAL_POINTER - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME # save callee saves in case allocation triggers GC - lw $a2, 64($sp) # pass caller Method* + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME # save callee saves in case allocation triggers GC + lw $a2, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE($sp) # pass caller Method* move $t0, $sp # save $sp addiu $sp, $sp, -32 # make space for extra args .cfi_adjust_cfa_offset 32 @@ -450,7 +453,7 @@ ENTRY \c_name .cfi_adjust_cfa_offset -32 move $a0, $v0 # save target Method* move $t9, $v1 # save $v0->code_ - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME beqz $v0, 1f nop jr $t9 @@ -479,7 +482,6 @@ INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvo * [sp + 20] = shorty */ ENTRY art_quick_invoke_stub - GENERATE_GLOBAL_POINTER sw $a0, 0($sp) # save out a0 addiu $sp, $sp, -16 # spill s0, s1, fp, ra .cfi_adjust_cfa_offset 16 @@ -507,7 +509,7 @@ ENTRY art_quick_invoke_stub lw $a1, 4($sp) # copy arg value for a1 lw $a2, 8($sp) # copy arg value for a2 lw $a3, 12($sp) # copy arg value for a3 - lw $t9, METHOD_QUICK_CODE_OFFSET($a0) # get pointer to the code + lw $t9, MIRROR_ART_METHOD_QUICK_CODE_OFFSET($a0) # get pointer to the code jalr $t9 # call the method sw $zero, 0($sp) # store NULL for method* at bottom of frame move $sp, $fp # restore the stack @@ -543,12 +545,10 @@ END art_quick_invoke_stub */ .extern artHandleFillArrayDataFromCode ENTRY art_quick_handle_fill_data - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC - lw $a2, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC + lw $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artHandleFillArrayDataFromCode # (payload offset, Array*, method, Thread*) move $a3, rSELF # pass Thread::Current - jal artHandleFillArrayDataFromCode # (payload offset, Array*, method, Thread*, $sp) - sw $sp, 16($sp) # pass $sp RETURN_IF_ZERO END art_quick_handle_fill_data @@ -557,13 +557,11 @@ END art_quick_handle_fill_data */ .extern artLockObjectFromCode ENTRY art_quick_lock_object - GENERATE_GLOBAL_POINTER beqz $a0, .Lart_quick_throw_null_pointer_exception_gp_set nop - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case we block + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case we block + jal artLockObjectFromCode # (Object* obj, Thread*) move $a1, rSELF # pass Thread::Current - jal artLockObjectFromCode # (Object* obj, Thread*, $sp) - move $a2, $sp # pass $sp RETURN_IF_ZERO END art_quick_lock_object @@ -572,13 +570,11 @@ END art_quick_lock_object */ .extern artUnlockObjectFromCode ENTRY art_quick_unlock_object - GENERATE_GLOBAL_POINTER beqz $a0, .Lart_quick_throw_null_pointer_exception_gp_set nop - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC + jal artUnlockObjectFromCode # (Object* obj, Thread*) move $a1, rSELF # pass Thread::Current - jal artUnlockObjectFromCode # (Object* obj, Thread*, $sp) - move $a2, $sp # pass $sp RETURN_IF_ZERO END art_quick_unlock_object @@ -587,7 +583,6 @@ END art_quick_unlock_object */ .extern artThrowClassCastException ENTRY art_quick_check_cast - GENERATE_GLOBAL_POINTER addiu $sp, $sp, -16 .cfi_adjust_cfa_offset 16 sw $ra, 12($sp) @@ -609,10 +604,9 @@ ENTRY art_quick_check_cast addiu $sp, $sp, 16 .cfi_adjust_cfa_offset -16 SETUP_SAVE_ALL_CALLEE_SAVE_FRAME - move $a2, rSELF # pass Thread::Current la $t9, artThrowClassCastException - jr $t9 # artThrowClassCastException (Class*, Class*, Thread*, SP) - move $a3, $sp # pass $sp + jr $t9 # artThrowClassCastException (Class*, Class*, Thread*) + move $a2, rSELF # pass Thread::Current END art_quick_check_cast /* @@ -621,7 +615,6 @@ END art_quick_check_cast * a0 = array, a1 = index, a2 = value */ ENTRY art_quick_aput_obj_with_null_and_bound_check - GENERATE_GLOBAL_POINTER bnez $a0, .Lart_quick_aput_obj_with_bound_check_gp_set nop b .Lart_quick_throw_null_pointer_exception_gp_set @@ -629,9 +622,7 @@ ENTRY art_quick_aput_obj_with_null_and_bound_check END art_quick_aput_obj_with_null_and_bound_check ENTRY art_quick_aput_obj_with_bound_check - GENERATE_GLOBAL_POINTER -.Lart_quick_aput_obj_with_bound_check_gp_set: - lw $t0, ARRAY_LENGTH_OFFSET($a0) + lw $t0, MIRROR_ARRAY_LENGTH_OFFSET($a0) sltu $t1, $a1, $t0 bnez $t1, .Lart_quick_aput_obj_gp_set nop @@ -641,19 +632,17 @@ ENTRY art_quick_aput_obj_with_bound_check END art_quick_aput_obj_with_bound_check ENTRY art_quick_aput_obj - GENERATE_GLOBAL_POINTER -.Lart_quick_aput_obj_gp_set: beqz $a2, .Ldo_aput_null nop - lw $t0, CLASS_OFFSET($a0) - lw $t1, CLASS_OFFSET($a2) - lw $t0, CLASS_COMPONENT_TYPE_OFFSET($t0) + lw $t0, MIRROR_OBJECT_CLASS_OFFSET($a0) + lw $t1, MIRROR_OBJECT_CLASS_OFFSET($a2) + lw $t0, MIRROR_CLASS_COMPONENT_TYPE_OFFSET($t0) bne $t1, $t0, .Lcheck_assignability # value's type == array's component type - trivial assignability nop .Ldo_aput: sll $a1, $a1, 2 add $t0, $a0, $a1 - sw $a2, OBJECT_ARRAY_DATA_OFFSET($t0) + sw $a2, MIRROR_OBJECT_ARRAY_DATA_OFFSET($t0) lw $t0, THREAD_CARD_TABLE_OFFSET(rSELF) srl $t1, $a0, 7 add $t1, $t1, $t0 @@ -663,7 +652,7 @@ ENTRY art_quick_aput_obj .Ldo_aput_null: sll $a1, $a1, 2 add $t0, $a0, $a1 - sw $a2, OBJECT_ARRAY_DATA_OFFSET($t0) + sw $a2, MIRROR_OBJECT_ARRAY_DATA_OFFSET($t0) jr $ra nop .Lcheck_assignability: @@ -690,10 +679,9 @@ ENTRY art_quick_aput_obj nop SETUP_SAVE_ALL_CALLEE_SAVE_FRAME move $a1, $a2 - move $a2, rSELF # pass Thread::Current la $t9, artThrowArrayStoreException - jr $t9 # artThrowArrayStoreException(Class*, Class*, Thread*, SP) - move $a3, $sp # pass $sp + jr $t9 # artThrowArrayStoreException(Class*, Class*, Thread*) + move $a2, rSELF # pass Thread::Current END art_quick_aput_obj /* @@ -703,12 +691,10 @@ END art_quick_aput_obj */ .extern artInitializeStaticStorageFromCode ENTRY art_quick_initialize_static_storage - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - move $a2, rSELF # pass Thread::Current - # artInitializeStaticStorageFromCode(uint32_t type_idx, Method* referrer, Thread*, $sp) + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + # artInitializeStaticStorageFromCode(uint32_t type_idx, Method* referrer, Thread*) jal artInitializeStaticStorageFromCode - move $a3, $sp # pass $sp + move $a2, rSELF # pass Thread::Current RETURN_IF_RESULT_IS_NON_ZERO END art_quick_initialize_static_storage @@ -717,12 +703,10 @@ END art_quick_initialize_static_storage */ .extern artInitializeTypeFromCode ENTRY art_quick_initialize_type - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - move $a2, rSELF # pass Thread::Current - # artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*, $sp) + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + # artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*) jal artInitializeTypeFromCode - move $a3, $sp # pass $sp + move $a2, rSELF # pass Thread::Current RETURN_IF_RESULT_IS_NON_ZERO END art_quick_initialize_type @@ -732,12 +716,10 @@ END art_quick_initialize_type */ .extern artInitializeTypeAndVerifyAccessFromCode ENTRY art_quick_initialize_type_and_verify_access - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - move $a2, rSELF # pass Thread::Current - # artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*, $sp) + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + # artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*) jal artInitializeTypeAndVerifyAccessFromCode - move $a3, $sp # pass $sp + move $a2, rSELF # pass Thread::Current RETURN_IF_RESULT_IS_NON_ZERO END art_quick_initialize_type_and_verify_access /* @@ -745,12 +727,10 @@ END art_quick_initialize_type_and_verify_access */ .extern artGetBooleanStaticFromCode ENTRY art_quick_get_boolean_static - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a1, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artGetBooleanStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*) move $a2, rSELF # pass Thread::Current - jal artGetBooleanStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*, $sp) - move $a3, $sp # pass $sp RETURN_IF_NO_EXCEPTION END art_quick_get_boolean_static /* @@ -758,12 +738,10 @@ END art_quick_get_boolean_static */ .extern artGetByteStaticFromCode ENTRY art_quick_get_byte_static - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a1, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artGetByteStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*) move $a2, rSELF # pass Thread::Current - jal artGetByteStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*, $sp) - move $a3, $sp # pass $sp RETURN_IF_NO_EXCEPTION END art_quick_get_byte_static @@ -772,12 +750,10 @@ END art_quick_get_byte_static */ .extern artGetCharStaticFromCode ENTRY art_quick_get_char_static - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a1, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artGetCharStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*) move $a2, rSELF # pass Thread::Current - jal artGetCharStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*, $sp) - move $a3, $sp # pass $sp RETURN_IF_NO_EXCEPTION END art_quick_get_char_static /* @@ -785,12 +761,10 @@ END art_quick_get_char_static */ .extern artGetShortStaticFromCode ENTRY art_quick_get_short_static - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a1, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artGetShortStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*) move $a2, rSELF # pass Thread::Current - jal artGetShortStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*, $sp) - move $a3, $sp # pass $sp RETURN_IF_NO_EXCEPTION END art_quick_get_short_static @@ -799,12 +773,10 @@ END art_quick_get_short_static */ .extern artGet32StaticFromCode ENTRY art_quick_get32_static - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a1, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artGet32StaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*) move $a2, rSELF # pass Thread::Current - jal artGet32StaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*, $sp) - move $a3, $sp # pass $sp RETURN_IF_NO_EXCEPTION END art_quick_get32_static @@ -813,12 +785,10 @@ END art_quick_get32_static */ .extern artGet64StaticFromCode ENTRY art_quick_get64_static - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a1, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artGet64StaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*) move $a2, rSELF # pass Thread::Current - jal artGet64StaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*, $sp) - move $a3, $sp # pass $sp RETURN_IF_NO_EXCEPTION END art_quick_get64_static @@ -827,12 +797,10 @@ END art_quick_get64_static */ .extern artGetObjStaticFromCode ENTRY art_quick_get_obj_static - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a1, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artGetObjStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*) move $a2, rSELF # pass Thread::Current - jal artGetObjStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*, $sp) - move $a3, $sp # pass $sp RETURN_IF_NO_EXCEPTION END art_quick_get_obj_static @@ -841,12 +809,10 @@ END art_quick_get_obj_static */ .extern artGetBooleanInstanceFromCode ENTRY art_quick_get_boolean_instance - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a2, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artGetBooleanInstanceFromCode # (field_idx, Object*, referrer, Thread*) move $a3, rSELF # pass Thread::Current - jal artGetBooleanInstanceFromCode # (field_idx, Object*, referrer, Thread*, $sp) - sw $sp, 16($sp) # pass $sp RETURN_IF_NO_EXCEPTION END art_quick_get_boolean_instance /* @@ -854,12 +820,10 @@ END art_quick_get_boolean_instance */ .extern artGetByteInstanceFromCode ENTRY art_quick_get_byte_instance - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a2, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artGetByteInstanceFromCode # (field_idx, Object*, referrer, Thread*) move $a3, rSELF # pass Thread::Current - jal artGetByteInstanceFromCode # (field_idx, Object*, referrer, Thread*, $sp) - sw $sp, 16($sp) # pass $sp RETURN_IF_NO_EXCEPTION END art_quick_get_byte_instance @@ -868,12 +832,10 @@ END art_quick_get_byte_instance */ .extern artGetCharInstanceFromCode ENTRY art_quick_get_char_instance - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a2, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artGetCharInstanceFromCode # (field_idx, Object*, referrer, Thread*) move $a3, rSELF # pass Thread::Current - jal artGetCharInstanceFromCode # (field_idx, Object*, referrer, Thread*, $sp) - sw $sp, 16($sp) # pass $sp RETURN_IF_NO_EXCEPTION END art_quick_get_char_instance /* @@ -881,12 +843,10 @@ END art_quick_get_char_instance */ .extern artGetShortInstanceFromCode ENTRY art_quick_get_short_instance - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a2, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artGetShortInstanceFromCode # (field_idx, Object*, referrer, Thread*) move $a3, rSELF # pass Thread::Current - jal artGetShortInstanceFromCode # (field_idx, Object*, referrer, Thread*, $sp) - sw $sp, 16($sp) # pass $sp RETURN_IF_NO_EXCEPTION END art_quick_get_short_instance @@ -895,9 +855,8 @@ END art_quick_get_short_instance */ .extern artGet32InstanceFromCode ENTRY art_quick_get32_instance - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a2, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* move $a3, rSELF # pass Thread::Current jal artGet32InstanceFromCode # (field_idx, Object*, referrer, Thread*, $sp) sw $sp, 16($sp) # pass $sp @@ -909,9 +868,8 @@ END art_quick_get32_instance */ .extern artGet64InstanceFromCode ENTRY art_quick_get64_instance - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a2, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* move $a3, rSELF # pass Thread::Current jal artGet64InstanceFromCode # (field_idx, Object*, referrer, Thread*, $sp) sw $sp, 16($sp) # pass $sp @@ -923,12 +881,10 @@ END art_quick_get64_instance */ .extern artGetObjInstanceFromCode ENTRY art_quick_get_obj_instance - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a2, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artGetObjInstanceFromCode # (field_idx, Object*, referrer, Thread*) move $a3, rSELF # pass Thread::Current - jal artGetObjInstanceFromCode # (field_idx, Object*, referrer, Thread*, $sp) - sw $sp, 16($sp) # pass $sp RETURN_IF_NO_EXCEPTION END art_quick_get_obj_instance @@ -937,12 +893,10 @@ END art_quick_get_obj_instance */ .extern artSet8StaticFromCode ENTRY art_quick_set8_static - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a2, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artSet8StaticFromCode # (field_idx, new_val, referrer, Thread*) move $a3, rSELF # pass Thread::Current - jal artSet8StaticFromCode # (field_idx, new_val, referrer, Thread*, $sp) - sw $sp, 16($sp) # pass $sp RETURN_IF_ZERO END art_quick_set8_static @@ -951,12 +905,10 @@ END art_quick_set8_static */ .extern artSet16StaticFromCode ENTRY art_quick_set16_static - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a2, 64($sp) # pass referrer's Method* - move $a3, rSELF # pass Thread::Current + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* jal artSet16StaticFromCode # (field_idx, new_val, referrer, Thread*, $sp) - sw $sp, 16($sp) # pass $sp + move $a3, rSELF # pass Thread::Current RETURN_IF_ZERO END art_quick_set16_static @@ -965,12 +917,10 @@ END art_quick_set16_static */ .extern artSet32StaticFromCode ENTRY art_quick_set32_static - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a2, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artSet32StaticFromCode # (field_idx, new_val, referrer, Thread*) move $a3, rSELF # pass Thread::Current - jal artSet32StaticFromCode # (field_idx, new_val, referrer, Thread*, $sp) - sw $sp, 16($sp) # pass $sp RETURN_IF_ZERO END art_quick_set32_static @@ -979,12 +929,10 @@ END art_quick_set32_static */ .extern artSet64StaticFromCode ENTRY art_quick_set64_static - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a1, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artSet64StaticFromCode # (field_idx, referrer, new_val, Thread*) sw rSELF, 16($sp) # pass Thread::Current - jal artSet64StaticFromCode # (field_idx, referrer, new_val, Thread*, $sp) - sw $sp, 20($sp) # pass $sp RETURN_IF_ZERO END art_quick_set64_static @@ -993,12 +941,10 @@ END art_quick_set64_static */ .extern artSetObjStaticFromCode ENTRY art_quick_set_obj_static - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a2, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a2, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artSetObjStaticFromCode # (field_idx, new_val, referrer, Thread*) move $a3, rSELF # pass Thread::Current - jal artSetObjStaticFromCode # (field_idx, new_val, referrer, Thread*, $sp) - sw $sp, 16($sp) # pass $sp RETURN_IF_ZERO END art_quick_set_obj_static @@ -1007,12 +953,10 @@ END art_quick_set_obj_static */ .extern artSet8InstanceFromCode ENTRY art_quick_set8_instance - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a3, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a3, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artSet8InstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*) sw rSELF, 16($sp) # pass Thread::Current - jal artSet8InstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*, $sp) - sw $sp, 20($sp) # pass $sp RETURN_IF_ZERO END art_quick_set8_instance @@ -1021,12 +965,10 @@ END art_quick_set8_instance */ .extern artSet16InstanceFromCode ENTRY art_quick_set16_instance - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a3, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a3, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artSet16InstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*) sw rSELF, 16($sp) # pass Thread::Current - jal artSet16InstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*, $sp) - sw $sp, 20($sp) # pass $sp RETURN_IF_ZERO END art_quick_set16_instance @@ -1035,12 +977,10 @@ END art_quick_set16_instance */ .extern artSet32InstanceFromCode ENTRY art_quick_set32_instance - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a3, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a3, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artSet32InstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*) sw rSELF, 16($sp) # pass Thread::Current - jal artSet32InstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*, $sp) - sw $sp, 20($sp) # pass $sp RETURN_IF_ZERO END art_quick_set32_instance @@ -1049,11 +989,11 @@ END art_quick_set32_instance */ .extern artSet64InstanceFromCode ENTRY art_quick_set64_instance - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - sw rSELF, 16($sp) # pass Thread::Current - jal artSet64InstanceFromCode # (field_idx, Object*, new_val, Thread*, $sp) - sw $sp, 20($sp) # pass $sp + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $t0, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # load referrer's Method* + sw rSELF, 20($sp) # pass Thread::Current + jal artSet64InstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*) + sw $t0, 16($sp) # pass referrer's Method* RETURN_IF_ZERO END art_quick_set64_instance @@ -1062,12 +1002,10 @@ END art_quick_set64_instance */ .extern artSetObjInstanceFromCode ENTRY art_quick_set_obj_instance - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - lw $a3, 64($sp) # pass referrer's Method* + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + lw $a3, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method* + jal artSetObjInstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*) sw rSELF, 16($sp) # pass Thread::Current - jal artSetObjInstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*, $sp) - sw $sp, 20($sp) # pass $sp RETURN_IF_ZERO END art_quick_set_obj_instance @@ -1079,12 +1017,10 @@ END art_quick_set_obj_instance */ .extern artResolveStringFromCode ENTRY art_quick_resolve_string - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - move $a2, rSELF # pass Thread::Current - # artResolveStringFromCode(Method* referrer, uint32_t string_idx, Thread*, $sp) + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + # artResolveStringFromCode(Method* referrer, uint32_t string_idx, Thread*) jal artResolveStringFromCode - move $a3, $sp # pass $sp + move $a2, rSELF # pass Thread::Current RETURN_IF_RESULT_IS_NON_ZERO END art_quick_resolve_string @@ -1093,11 +1029,9 @@ END art_quick_resolve_string .macro TWO_ARG_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - move $a2, rSELF # pass Thread::Current + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC jal \entrypoint - move $a3, $sp # pass $sp + move $a2, rSELF # pass Thread::Current \return END \name .endm @@ -1105,11 +1039,9 @@ END \name .macro THREE_ARG_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - move $a3, rSELF # pass Thread::Current + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC jal \entrypoint - sw $sp, 16($sp) # pass $sp + move $a3, rSELF # pass Thread::Current \return END \name .endm @@ -1122,18 +1054,16 @@ GENERATE_ALL_ALLOC_ENTRYPOINTS */ .extern artTestSuspendFromCode ENTRY art_quick_test_suspend - GENERATE_GLOBAL_POINTER lh $a0, THREAD_FLAGS_OFFSET(rSELF) bnez $a0, 1f addi rSUSPEND, $zero, SUSPEND_CHECK_INTERVAL # reset rSUSPEND to SUSPEND_CHECK_INTERVAL jr $ra nop 1: + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves for stack crawl + jal artTestSuspendFromCode # (Thread*) move $a0, rSELF - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves for stack crawl - jal artTestSuspendFromCode # (Thread*, $sp) - move $a1, $sp - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN END art_quick_test_suspend /* @@ -1142,14 +1072,13 @@ END art_quick_test_suspend */ .extern artQuickProxyInvokeHandler ENTRY art_quick_proxy_invoke_handler - GENERATE_GLOBAL_POINTER - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME sw $a0, 0($sp) # place proxy method at bottom of frame move $a2, rSELF # pass Thread::Current jal artQuickProxyInvokeHandler # (Method* proxy method, receiver, Thread*, SP) move $a3, $sp # pass $sp lw $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_ - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME bnez $t0, 1f mtc1 $v0, $f0 # place return value to FP return value jr $ra @@ -1162,32 +1091,30 @@ END art_quick_proxy_invoke_handler * Called to resolve an imt conflict. t0 is a hidden argument that holds the target method's * dex method index. */ -ENTRY art_quick_imt_conflict_trampoline - GENERATE_GLOBAL_POINTER +ENTRY_NO_GP art_quick_imt_conflict_trampoline lw $a0, 0($sp) # load caller Method* - lw $a0, METHOD_DEX_CACHE_METHODS_OFFSET($a0) # load dex_cache_resolved_methods + lw $a0, MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET($a0) # load dex_cache_resolved_methods sll $t0, 2 # convert target method offset to bytes add $a0, $t0 # get address of target method - lw $a0, OBJECT_ARRAY_DATA_OFFSET($a0) # load the target method + lw $a0, MIRROR_OBJECT_ARRAY_DATA_OFFSET($a0) # load the target method la $t9, art_quick_invoke_interface_trampoline jr $t9 END art_quick_imt_conflict_trampoline .extern artQuickResolutionTrampoline ENTRY art_quick_resolution_trampoline - GENERATE_GLOBAL_POINTER - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME move $a2, rSELF # pass Thread::Current jal artQuickResolutionTrampoline # (Method* called, receiver, Thread*, SP) move $a3, $sp # pass $sp beqz $v0, 1f lw $a0, 0($sp) # load resolved method to $a0 - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME move $t9, $v0 # code pointer must be in $t9 to generate the global pointer jr $v0 # tail call to method nop 1: - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME DELIVER_PENDING_EXCEPTION END art_quick_resolution_trampoline @@ -1195,13 +1122,12 @@ UNIMPLEMENTED art_quick_generic_jni_trampoline .extern artQuickToInterpreterBridge ENTRY art_quick_to_interpreter_bridge - GENERATE_GLOBAL_POINTER - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME move $a1, rSELF # pass Thread::Current jal artQuickToInterpreterBridge # (Method* method, Thread*, SP) move $a2, $sp # pass $sp lw $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_ - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME bnez $t0, 1f mtc1 $v0, $f0 # place return value to FP return value jr $ra @@ -1216,21 +1142,19 @@ END art_quick_to_interpreter_bridge .extern artInstrumentationMethodEntryFromCode .extern artInstrumentationMethodExitFromCode ENTRY art_quick_instrumentation_entry - GENERATE_GLOBAL_POINTER - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME move $t0, $sp # remember bottom of caller's frame - addiu $sp, $sp, -32 # space for args, pad (3 words), arguments (5 words) + addiu $sp, $sp, -32 # space for saved a0, pad (2 words), arguments (4 words) .cfi_adjust_cfa_offset 32 sw $a0, 28($sp) # save arg0 - sw $ra, 16($sp) # pass $ra - move $a3, $t0 # pass $sp - jal artInstrumentationMethodEntryFromCode # (Method*, Object*, Thread*, SP, LR) + move $a3, $ra # pass $ra + jal artInstrumentationMethodEntryFromCode # (Method*, Object*, Thread*, LR) move $a2, rSELF # pass Thread::Current move $t9, $v0 # $t9 holds reference to code lw $a0, 28($sp) # restore arg0 addiu $sp, $sp, 32 # remove args .cfi_adjust_cfa_offset -32 - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME jalr $t9 # call method nop END art_quick_instrumentation_entry @@ -1239,9 +1163,9 @@ END art_quick_instrumentation_entry art_quick_instrumentation_exit: .cfi_startproc addiu $t9, $ra, 4 # put current address into $t9 to rebuild $gp - GENERATE_GLOBAL_POINTER + .cpload $t9 move $ra, $zero # link register is to here, so clobber with 0 for later checks - SETUP_REF_ONLY_CALLEE_SAVE_FRAME + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME move $t0, $sp # remember bottom of caller's frame addiu $sp, $sp, -48 # save return values and set up args .cfi_adjust_cfa_offset 48 @@ -1274,14 +1198,11 @@ END art_quick_instrumentation_exit * will long jump to the upcall with a special exception of -1. */ .extern artDeoptimize - .extern artEnterInterpreterFromDeoptimize ENTRY art_quick_deoptimize - GENERATE_GLOBAL_POINTER SETUP_SAVE_ALL_CALLEE_SAVE_FRAME - move $a0, rSELF # pass Thread::current - jal artDeoptimize # artDeoptimize(Thread*, SP) + jal artDeoptimize # artDeoptimize(Thread*) # Returns caller method's frame size. - move $a1, $sp # pass $sp + move $a0, rSELF # pass Thread::current END art_quick_deoptimize /* @@ -1294,7 +1215,7 @@ END art_quick_deoptimize * $a1: high word * $a2: shift count */ -ENTRY art_quick_shl_long +ENTRY_NO_GP art_quick_shl_long /* shl-long vAA, vBB, vCC */ sll $v0, $a0, $a2 # rlo<- alo << (shift&31) not $v1, $a2 # rhi<- 31-shift (shift is 5b) @@ -1318,8 +1239,7 @@ END art_quick_shl_long * $a1: high word * $a2: shift count */ - .global art_quick_shr_long -ENTRY art_quick_shr_long +ENTRY_NO_GP art_quick_shr_long sra $v1, $a1, $a2 # rhi<- ahi >> (shift&31) srl $v0, $a0, $a2 # rlo<- alo >> (shift&31) sra $a3, $a1, 31 # $a3<- sign(ah) @@ -1344,8 +1264,7 @@ END art_quick_shr_long * r2: shift count */ /* ushr-long vAA, vBB, vCC */ - .global art_quick_ushr_long -ENTRY art_quick_ushr_long +ENTRY_NO_GP art_quick_ushr_long srl $v1, $a1, $a2 # rhi<- ahi >> (shift&31) srl $v0, $a0, $a2 # rlo<- alo >> (shift&31) not $a0, $a2 # alo<- 31-shift (shift is 5b) @@ -1358,12 +1277,5 @@ ENTRY art_quick_ushr_long movn $v1, $zero, $a2 # rhi<- 0 (if shift&0x20) END art_quick_ushr_long -ENTRY art_quick_indexof - jr $ra - nop -END art_quick_indexof - -ENTRY art_quick_string_compareto - jr $ra - nop -END art_quick_string_compareto +UNIMPLEMENTED art_quick_indexof +UNIMPLEMENTED art_quick_string_compareto diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index 6b74a1b1d2..c5a0f6c231 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -260,7 +260,7 @@ class StubTest : public CommonRuntimeTest { "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31", "memory"); // clobber. -#elif defined(__x86_64__) && !defined(__APPLE__) +#elif defined(__x86_64__) && !defined(__APPLE__) && defined(__clang__) // Note: Uses the native convention // TODO: Set the thread? __asm__ __volatile__( @@ -485,7 +485,7 @@ class StubTest : public CommonRuntimeTest { "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31", "memory"); // clobber. -#elif defined(__x86_64__) && !defined(__APPLE__) +#elif defined(__x86_64__) && !defined(__APPLE__) && defined(__clang__) // Note: Uses the native convention // TODO: Set the thread? __asm__ __volatile__( @@ -1148,7 +1148,7 @@ TEST_F(StubTest, AllocObjectArray) { // For some reason this does not work, as the type_idx is artificial and outside what the // resolved types of c_obj allow... - if (false) { + if ((false)) { // Use an arbitrary method from c to use as referrer size_t result = Invoke3(static_cast<size_t>(c->GetDexTypeIndex()), // type_idx reinterpret_cast<size_t>(c_obj->GetVirtualMethod(0)), // arbitrary @@ -1221,13 +1221,12 @@ TEST_F(StubTest, StringCompareTo) { // Use array so we can index into it and use a matrix for expected results // Setup: The first half is standard. The second half uses a non-zero offset. // TODO: Shared backing arrays. - static constexpr size_t kBaseStringCount = 8; - const char* c[kBaseStringCount] = { "", "", "a", "aa", "ab", + const char* c[] = { "", "", "a", "aa", "ab", "aacaacaacaacaacaac", // This one's under the default limit to go to __memcmp16. "aacaacaacaacaacaacaacaacaacaacaacaac", // This one's over. "aacaacaacaacaacaacaacaacaacaacaacaaca" }; // As is this one. We need a separate one to // defeat object-equal optimizations. - + static constexpr size_t kBaseStringCount = arraysize(c); static constexpr size_t kStringCount = 2 * kBaseStringCount; StackHandleScope<kStringCount> hs(self); @@ -1337,10 +1336,9 @@ static void GetSetByteStatic(Handle<mirror::Object>* obj, Handle<mirror::ArtFiel mirror::ArtMethod* referrer, StubTest* test) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) - constexpr size_t num_values = 5; - int8_t values[num_values] = { -128, -64, 0, 64, 127 }; + int8_t values[] = { -128, -64, 0, 64, 127 }; - for (size_t i = 0; i < num_values; ++i) { + for (size_t i = 0; i < arraysize(values); ++i) { test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()), static_cast<size_t>(values[i]), 0U, @@ -1367,10 +1365,9 @@ static void GetSetBooleanInstance(Handle<mirror::Object>* obj, Handle<mirror::Ar Thread* self, mirror::ArtMethod* referrer, StubTest* test) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) - constexpr size_t num_values = 5; - uint8_t values[num_values] = { 0, true, 2, 128, 0xFF }; + uint8_t values[] = { 0, true, 2, 128, 0xFF }; - for (size_t i = 0; i < num_values; ++i) { + for (size_t i = 0; i < arraysize(values); ++i) { test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()), reinterpret_cast<size_t>(obj->Get()), static_cast<size_t>(values[i]), @@ -1401,10 +1398,9 @@ static void GetSetByteInstance(Handle<mirror::Object>* obj, Handle<mirror::ArtFi Thread* self, mirror::ArtMethod* referrer, StubTest* test) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) - constexpr size_t num_values = 5; - int8_t values[num_values] = { -128, -64, 0, 64, 127 }; + int8_t values[] = { -128, -64, 0, 64, 127 }; - for (size_t i = 0; i < num_values; ++i) { + for (size_t i = 0; i < arraysize(values); ++i) { test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()), reinterpret_cast<size_t>(obj->Get()), static_cast<size_t>(values[i]), @@ -1435,10 +1431,9 @@ static void GetSetCharStatic(Handle<mirror::Object>* obj, Handle<mirror::ArtFiel mirror::ArtMethod* referrer, StubTest* test) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) - constexpr size_t num_values = 6; - uint16_t values[num_values] = { 0, 1, 2, 255, 32768, 0xFFFF }; + uint16_t values[] = { 0, 1, 2, 255, 32768, 0xFFFF }; - for (size_t i = 0; i < num_values; ++i) { + for (size_t i = 0; i < arraysize(values); ++i) { test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()), static_cast<size_t>(values[i]), 0U, @@ -1464,10 +1459,9 @@ static void GetSetShortStatic(Handle<mirror::Object>* obj, Handle<mirror::ArtFie mirror::ArtMethod* referrer, StubTest* test) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) - constexpr size_t num_values = 6; - int16_t values[num_values] = { -0x7FFF, -32768, 0, 255, 32767, 0x7FFE }; + int16_t values[] = { -0x7FFF, -32768, 0, 255, 32767, 0x7FFE }; - for (size_t i = 0; i < num_values; ++i) { + for (size_t i = 0; i < arraysize(values); ++i) { test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()), static_cast<size_t>(values[i]), 0U, @@ -1494,10 +1488,9 @@ static void GetSetCharInstance(Handle<mirror::Object>* obj, Handle<mirror::ArtFi Thread* self, mirror::ArtMethod* referrer, StubTest* test) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) - constexpr size_t num_values = 6; - uint16_t values[num_values] = { 0, 1, 2, 255, 32768, 0xFFFF }; + uint16_t values[] = { 0, 1, 2, 255, 32768, 0xFFFF }; - for (size_t i = 0; i < num_values; ++i) { + for (size_t i = 0; i < arraysize(values); ++i) { test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()), reinterpret_cast<size_t>(obj->Get()), static_cast<size_t>(values[i]), @@ -1527,10 +1520,9 @@ static void GetSetShortInstance(Handle<mirror::Object>* obj, Handle<mirror::ArtF Thread* self, mirror::ArtMethod* referrer, StubTest* test) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) - constexpr size_t num_values = 6; - int16_t values[num_values] = { -0x7FFF, -32768, 0, 255, 32767, 0x7FFE }; + int16_t values[] = { -0x7FFF, -32768, 0, 255, 32767, 0x7FFE }; - for (size_t i = 0; i < num_values; ++i) { + for (size_t i = 0; i < arraysize(values); ++i) { test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()), reinterpret_cast<size_t>(obj->Get()), static_cast<size_t>(values[i]), @@ -1561,10 +1553,9 @@ static void GetSet32Static(Handle<mirror::Object>* obj, Handle<mirror::ArtField> mirror::ArtMethod* referrer, StubTest* test) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) - constexpr size_t num_values = 7; - uint32_t values[num_values] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF }; + uint32_t values[] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF }; - for (size_t i = 0; i < num_values; ++i) { + for (size_t i = 0; i < arraysize(values); ++i) { test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()), static_cast<size_t>(values[i]), 0U, @@ -1592,10 +1583,9 @@ static void GetSet32Instance(Handle<mirror::Object>* obj, Handle<mirror::ArtFiel Thread* self, mirror::ArtMethod* referrer, StubTest* test) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) - constexpr size_t num_values = 7; - uint32_t values[num_values] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF }; + uint32_t values[] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF }; - for (size_t i = 0; i < num_values; ++i) { + for (size_t i = 0; i < arraysize(values); ++i) { test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()), reinterpret_cast<size_t>(obj->Get()), static_cast<size_t>(values[i]), @@ -1716,10 +1706,9 @@ static void GetSet64Static(Handle<mirror::Object>* obj, Handle<mirror::ArtField> mirror::ArtMethod* referrer, StubTest* test) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { #if (defined(__x86_64__) && !defined(__APPLE__)) || defined(__aarch64__) - constexpr size_t num_values = 8; - uint64_t values[num_values] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF, 0xFFFFFFFFFFFF }; + uint64_t values[] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF, 0xFFFFFFFFFFFF }; - for (size_t i = 0; i < num_values; ++i) { + for (size_t i = 0; i < arraysize(values); ++i) { test->Invoke3UWithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()), values[i], StubTest::GetEntrypoint(self, kQuickSet64Static), @@ -1746,10 +1735,9 @@ static void GetSet64Instance(Handle<mirror::Object>* obj, Handle<mirror::ArtFiel Thread* self, mirror::ArtMethod* referrer, StubTest* test) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { #if (defined(__x86_64__) && !defined(__APPLE__)) || defined(__aarch64__) - constexpr size_t num_values = 8; - uint64_t values[num_values] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF, 0xFFFFFFFFFFFF }; + uint64_t values[] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF, 0xFFFFFFFFFFFF }; - for (size_t i = 0; i < num_values; ++i) { + for (size_t i = 0; i < arraysize(values); ++i) { test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()), reinterpret_cast<size_t>(obj->Get()), static_cast<size_t>(values[i]), @@ -2109,10 +2097,10 @@ TEST_F(StubTest, StringIndexOf) { // Use array so we can index into it and use a matrix for expected results // Setup: The first half is standard. The second half uses a non-zero offset. // TODO: Shared backing arrays. - static constexpr size_t kStringCount = 7; - const char* c_str[kStringCount] = { "", "a", "ba", "cba", "dcba", "edcba", "asdfghjkl" }; - static constexpr size_t kCharCount = 5; - const char c_char[kCharCount] = { 'a', 'b', 'c', 'd', 'e' }; + const char* c_str[] = { "", "a", "ba", "cba", "dcba", "edcba", "asdfghjkl" }; + static constexpr size_t kStringCount = arraysize(c_str); + const char c_char[] = { 'a', 'b', 'c', 'd', 'e' }; + static constexpr size_t kCharCount = arraysize(c_char); StackHandleScope<kStringCount> hs(self); Handle<mirror::String> s[kStringCount]; diff --git a/runtime/arch/x86/asm_support_x86.S b/runtime/arch/x86/asm_support_x86.S index efbbfb3f10..fea16da9fb 100644 --- a/runtime/arch/x86/asm_support_x86.S +++ b/runtime/arch/x86/asm_support_x86.S @@ -135,16 +135,6 @@ VAR(c_name, 0): CFI_DEF_CFA(esp, 4) END_MACRO -MACRO1(DEFINE_FUNCTION_NO_HIDE, c_name) - FUNCTION_TYPE(\c_name, 0) - .globl VAR(c_name, 0) - ALIGN_FUNCTION_ENTRY -VAR(c_name, 0): - CFI_STARTPROC - // Ensure we get a sane starting CFA. - CFI_DEF_CFA(esp, 4) -END_MACRO - MACRO1(END_FUNCTION, c_name) CFI_ENDPROC SIZE(\c_name, 0) @@ -174,10 +164,14 @@ VAR(name, 0): SIZE(\name, 0) END_MACRO -MACRO0(SETUP_GOT_NOSAVE) +MACRO1(SETUP_GOT_NOSAVE, got_reg) #ifndef __APPLE__ - call __x86.get_pc_thunk.bx - addl $_GLOBAL_OFFSET_TABLE_, %ebx + .ifc RAW_VAR(got_reg, 0), ebx + call __x86.get_pc_thunk.bx + addl $_GLOBAL_OFFSET_TABLE_, %ebx + .else + .error "Unknown GOT register \got_reg" + .endif #endif END_MACRO diff --git a/runtime/arch/x86/asm_support_x86.h b/runtime/arch/x86/asm_support_x86.h index c9f5a25eea..5a88f8012e 100644 --- a/runtime/arch/x86/asm_support_x86.h +++ b/runtime/arch/x86/asm_support_x86.h @@ -19,20 +19,8 @@ #include "asm_support.h" -// Offset of field Thread::self_ verified in InitCpu -#define THREAD_SELF_OFFSET 156 -// Offset of field Thread::card_table_ verified in InitCpu -#define THREAD_CARD_TABLE_OFFSET 120 -// Offset of field Thread::exception_ verified in InitCpu -#define THREAD_EXCEPTION_OFFSET 124 -// Offset of field Thread::thin_lock_thread_id_ verified in InitCpu -#define THREAD_ID_OFFSET 12 - #define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 32 #define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 32 #define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 32 -// Expected size of a heap reference -#define HEAP_REFERENCE_SIZE 4 - #endif // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_ diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc index 682c5021ce..f2b91cd81b 100644 --- a/runtime/arch/x86/entrypoints_init_x86.cc +++ b/runtime/arch/x86/entrypoints_init_x86.cc @@ -19,18 +19,11 @@ #include "entrypoints/portable/portable_entrypoints.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/runtime_asm_entrypoints.h" +#include "interpreter/interpreter.h" namespace art { -// Interpreter entrypoints. -extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result); -extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result); - // Portable entrypoints. extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*); extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*); diff --git a/runtime/arch/x86/portable_entrypoints_x86.S b/runtime/arch/x86/portable_entrypoints_x86.S index f5fe869593..a7c4124a47 100644 --- a/runtime/arch/x86/portable_entrypoints_x86.S +++ b/runtime/arch/x86/portable_entrypoints_x86.S @@ -37,7 +37,7 @@ DEFINE_FUNCTION art_portable_invoke_stub andl LITERAL(0xFFFFFFF0), %ebx // align frame size to 16 bytes subl LITERAL(12), %ebx // remove space for return address, ebx, and ebp subl %ebx, %esp // reserve stack space for argument array - SETUP_GOT_NOSAVE // reset ebx to GOT table + SETUP_GOT_NOSAVE ebx // reset ebx to GOT table lea 4(%esp), %eax // use stack pointer + method ptr as dest for memcpy pushl 20(%ebp) // push size of region to memcpy pushl 16(%ebp) // push arg array as source of memcpy @@ -46,7 +46,7 @@ DEFINE_FUNCTION art_portable_invoke_stub addl LITERAL(12), %esp // pop arguments to memcpy mov 12(%ebp), %eax // move method pointer into eax mov %eax, (%esp) // push method pointer onto stack - call *METHOD_PORTABLE_CODE_OFFSET(%eax) // call the method + call *MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET(%eax) // call the method mov %ebp, %esp // restore stack pointer POP ebx // pop ebx POP ebp // pop ebp @@ -111,7 +111,7 @@ DEFINE_FUNCTION art_portable_resolution_trampoline ret END_FUNCTION art_portable_resolution_trampoline -DEFINE_FUNCTION_NO_HIDE art_portable_to_interpreter_bridge +DEFINE_FUNCTION art_portable_to_interpreter_bridge PUSH ebp // Set up frame. movl %esp, %ebp CFI_DEF_CFA_REGISTER(%ebp) @@ -127,3 +127,5 @@ DEFINE_FUNCTION_NO_HIDE art_portable_to_interpreter_bridge CFI_DEF_CFA(%esp, 4) ret END_FUNCTION art_portable_to_interpreter_bridge + +UNIMPLEMENTED art_portable_imt_conflict_trampoline diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 411d273bca..69527cea54 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -22,12 +22,21 @@ * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kSaveAll) */ -MACRO0(SETUP_SAVE_ALL_CALLEE_SAVE_FRAME) +MACRO2(SETUP_SAVE_ALL_CALLEE_SAVE_FRAME, got_reg, temp_reg) PUSH edi // Save callee saves (ebx is saved/restored by the upcall) PUSH esi PUSH ebp - subl MACRO_LITERAL(16), %esp // Grow stack by 4 words, bottom word will hold Method* - CFI_ADJUST_CFA_OFFSET(16) + subl MACRO_LITERAL(12), %esp // Grow stack by 3 words. + CFI_ADJUST_CFA_OFFSET(12) + SETUP_GOT_NOSAVE RAW_VAR(got_reg, 0) + // Load Runtime::instance_ from GOT. + movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg, 0)), REG_VAR(temp_reg, 1) + movl (REG_VAR(temp_reg, 1)), REG_VAR(temp_reg, 1) + // Push save all callee-save method. + pushl RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET(REG_VAR(temp_reg, 1)) + CFI_ADJUST_CFA_OFFSET(4) + // Store esp as the top quick frame. + movl %esp, %fs:THREAD_TOP_QUICK_FRAME_OFFSET // Ugly compile-time check, but we only have the preprocessor. // Last +4: implicit return address pushed on stack when caller made call. #if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 3*4 + 16 + 4) @@ -39,12 +48,21 @@ END_MACRO * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kRefsOnly) */ -MACRO0(SETUP_REF_ONLY_CALLEE_SAVE_FRAME) +MACRO2(SETUP_REFS_ONLY_CALLEE_SAVE_FRAME, got_reg, temp_reg) PUSH edi // Save callee saves (ebx is saved/restored by the upcall) PUSH esi PUSH ebp - subl MACRO_LITERAL(16), %esp // Grow stack by 4 words, bottom word will hold Method* - CFI_ADJUST_CFA_OFFSET(16) + subl MACRO_LITERAL(12), %esp // Grow stack by 3 words. + CFI_ADJUST_CFA_OFFSET(12) + SETUP_GOT_NOSAVE VAR(got_reg, 0) + // Load Runtime::instance_ from GOT. + movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg, 0)), REG_VAR(temp_reg, 1) + movl (REG_VAR(temp_reg, 1)), REG_VAR(temp_reg, 1) + // Push save all callee-save method. + pushl RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET(REG_VAR(temp_reg, 1)) + CFI_ADJUST_CFA_OFFSET(4) + // Store esp as the top quick frame. + movl %esp, %fs:THREAD_TOP_QUICK_FRAME_OFFSET // Ugly compile-time check, but we only have the preprocessor. // Last +4: implicit return address pushed on stack when caller made call. @@ -53,7 +71,7 @@ MACRO0(SETUP_REF_ONLY_CALLEE_SAVE_FRAME) #endif END_MACRO -MACRO0(RESTORE_REF_ONLY_CALLEE_SAVE_FRAME) +MACRO0(RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME) addl MACRO_LITERAL(16), %esp // Unwind stack up to saved values CFI_ADJUST_CFA_OFFSET(-16) POP ebp // Restore callee saves (ebx is saved/restored by the upcall) @@ -65,14 +83,22 @@ END_MACRO * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kRefsAndArgs) */ -MACRO0(SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME) +MACRO2(SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME, got_reg, temp_reg) PUSH edi // Save callee saves PUSH esi PUSH ebp PUSH ebx // Save args PUSH edx PUSH ecx - PUSH eax // Align stack, eax will be clobbered by Method* + SETUP_GOT_NOSAVE VAR(got_reg, 0) + // Load Runtime::instance_ from GOT. + movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg, 0)), REG_VAR(temp_reg, 1) + movl (REG_VAR(temp_reg, 1)), REG_VAR(temp_reg, 1) + // Push save all callee-save method. + pushl RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET(REG_VAR(temp_reg, 1)) + CFI_ADJUST_CFA_OFFSET(4) + // Store esp as the stop quick frame. + movl %esp, %fs:THREAD_TOP_QUICK_FRAME_OFFSET // Ugly compile-time check, but we only have the preprocessor. // Last +4: implicit return address pushed on stack when caller made call. @@ -81,7 +107,23 @@ MACRO0(SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME) #endif END_MACRO -MACRO0(RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME) + /* + * Macro that sets up the callee save frame to conform with + * Runtime::CreateCalleeSaveMethod(kRefsAndArgs) where the method is passed in EAX. + */ +MACRO0(SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_EAX) + PUSH edi // Save callee saves + PUSH esi + PUSH ebp + PUSH ebx // Save args + PUSH edx + PUSH ecx + PUSH eax // Store the ArtMethod reference at the bottom of the stack. + // Store esp as the stop quick frame. + movl %esp, %fs:THREAD_TOP_QUICK_FRAME_OFFSET +END_MACRO + +MACRO0(RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME) addl MACRO_LITERAL(4), %esp // Remove padding CFI_ADJUST_CFA_OFFSET(-4) POP ecx // Restore args except eax @@ -97,59 +139,54 @@ END_MACRO * exception is Thread::Current()->exception_. */ MACRO0(DELIVER_PENDING_EXCEPTION) - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save callee saves for throw - mov %esp, %ecx + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME ebx, ebx // save callee saves for throw // Outgoing argument set up - subl MACRO_LITERAL(8), %esp // Alignment padding - CFI_ADJUST_CFA_OFFSET(8) - PUSH ecx // pass SP - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() + subl MACRO_LITERAL(12), %esp // Alignment padding + CFI_ADJUST_CFA_OFFSET(12) + pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) - call SYMBOL(artDeliverPendingExceptionFromCode) // artDeliverPendingExceptionFromCode(Thread*, SP) - int3 // unreached + call SYMBOL(artDeliverPendingExceptionFromCode) // artDeliverPendingExceptionFromCode(Thread*) + int3 // unreached END_MACRO MACRO2(NO_ARG_RUNTIME_EXCEPTION, c_name, cxx_name) DEFINE_FUNCTION RAW_VAR(c_name, 0) - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context - mov %esp, %ecx + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME ebx, ebx // save all registers as basis for long jump context // Outgoing argument set up - subl MACRO_LITERAL(8), %esp // alignment padding - CFI_ADJUST_CFA_OFFSET(8) - PUSH ecx // pass SP + subl MACRO_LITERAL(12), %esp // alignment padding + CFI_ADJUST_CFA_OFFSET(12) pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) - call VAR(cxx_name, 1) // cxx_name(Thread*, SP) + call VAR(cxx_name, 1) // cxx_name(Thread*) int3 // unreached END_FUNCTION RAW_VAR(c_name, 0) END_MACRO MACRO2(ONE_ARG_RUNTIME_EXCEPTION, c_name, cxx_name) DEFINE_FUNCTION RAW_VAR(c_name, 0) - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME ebx, ebx // save all registers as basis for long jump context mov %esp, %ecx // Outgoing argument set up - PUSH eax // alignment padding - PUSH ecx // pass SP + subl MACRO_LITERAL(8), %esp // alignment padding + CFI_ADJUST_CFA_OFFSET(8) pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) PUSH eax // pass arg1 - call VAR(cxx_name, 1) // cxx_name(arg1, Thread*, SP) + call VAR(cxx_name, 1) // cxx_name(arg1, Thread*) int3 // unreached END_FUNCTION RAW_VAR(c_name, 0) END_MACRO MACRO2(TWO_ARG_RUNTIME_EXCEPTION, c_name, cxx_name) DEFINE_FUNCTION RAW_VAR(c_name, 0) - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context - mov %esp, %edx + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME ebx, ebx // save all registers as basis for long jump context // Outgoing argument set up - PUSH edx // pass SP + PUSH eax // alignment padding pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) PUSH ecx // pass arg2 PUSH eax // pass arg1 - call VAR(cxx_name, 1) // cxx_name(arg1, arg2, Thread*, SP) + call VAR(cxx_name, 1) // cxx_name(arg1, arg2, Thread*) int3 // unreached END_FUNCTION RAW_VAR(c_name, 0) END_MACRO @@ -204,15 +241,7 @@ TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromC */ MACRO2(INVOKE_TRAMPOLINE, c_name, cxx_name) DEFINE_FUNCTION RAW_VAR(c_name, 0) - // Set up the callee save frame to conform with Runtime::CreateCalleeSaveMethod(kRefsAndArgs) - // return address - PUSH edi - PUSH esi - PUSH ebp - PUSH ebx // Save args - PUSH edx - PUSH ecx - PUSH eax // <-- callee save Method* to go here + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME ebx, ebx movl %esp, %edx // remember SP // Outgoing argument set up subl MACRO_LITERAL(12), %esp // alignment padding @@ -224,7 +253,7 @@ MACRO2(INVOKE_TRAMPOLINE, c_name, cxx_name) CFI_ADJUST_CFA_OFFSET(4) PUSH ecx // pass arg2 PUSH eax // pass arg1 - call VAR(cxx_name, 1) // cxx_name(arg1, arg2, arg3, Thread*, SP) + call VAR(cxx_name, 1) // cxx_name(arg1, arg2, arg3, Thread*, SP) movl %edx, %edi // save code pointer in EDI addl MACRO_LITERAL(36), %esp // Pop arguments skip eax CFI_ADJUST_CFA_OFFSET(-36) @@ -275,7 +304,7 @@ DEFINE_FUNCTION art_quick_invoke_stub andl LITERAL(0xFFFFFFF0), %ebx // align frame size to 16 bytes subl LITERAL(12), %ebx // remove space for return address, ebx, and ebp subl %ebx, %esp // reserve stack space for argument array - SETUP_GOT_NOSAVE // clobbers ebx (harmless here) + SETUP_GOT_NOSAVE ebx // clobbers ebx (harmless here) lea 4(%esp), %eax // use stack pointer + method ptr as dest for memcpy pushl 20(%ebp) // push size of region to memcpy pushl 16(%ebp) // push arg array as source of memcpy @@ -287,7 +316,7 @@ DEFINE_FUNCTION art_quick_invoke_stub mov 4(%esp), %ecx // copy arg1 into ecx mov 8(%esp), %edx // copy arg2 into edx mov 12(%esp), %ebx // copy arg3 into ebx - call *METHOD_QUICK_CODE_OFFSET(%eax) // call the method + call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET(%eax) // call the method mov %ebp, %esp // restore stack pointer CFI_DEF_CFA_REGISTER(esp) POP ebx // pop ebx @@ -311,121 +340,128 @@ END_FUNCTION art_quick_invoke_stub MACRO3(NO_ARG_DOWNCALL, c_name, cxx_name, return_macro) DEFINE_FUNCTION RAW_VAR(c_name, 0) - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %edx // remember SP + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC // Outgoing argument set up - subl MACRO_LITERAL(8), %esp // push padding - CFI_ADJUST_CFA_OFFSET(8) - PUSH edx // pass SP + subl MACRO_LITERAL(12), %esp // push padding + CFI_ADJUST_CFA_OFFSET(12) pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) - call VAR(cxx_name, 1) // cxx_name(Thread*, SP) + call VAR(cxx_name, 1) // cxx_name(Thread*) addl MACRO_LITERAL(16), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-16) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address CALL_MACRO(return_macro, 2) // return or deliver exception END_FUNCTION RAW_VAR(c_name, 0) END_MACRO MACRO3(ONE_ARG_DOWNCALL, c_name, cxx_name, return_macro) DEFINE_FUNCTION RAW_VAR(c_name, 0) - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %edx // remember SP + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC // Outgoing argument set up - PUSH eax // push padding - PUSH edx // pass SP + subl MACRO_LITERAL(8), %esp // push padding + CFI_ADJUST_CFA_OFFSET(8) pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) PUSH eax // pass arg1 - call VAR(cxx_name, 1) // cxx_name(arg1, Thread*, SP) + call VAR(cxx_name, 1) // cxx_name(arg1, Thread*) addl MACRO_LITERAL(16), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-16) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address CALL_MACRO(return_macro, 2) // return or deliver exception END_FUNCTION RAW_VAR(c_name, 0) END_MACRO MACRO3(TWO_ARG_DOWNCALL, c_name, cxx_name, return_macro) DEFINE_FUNCTION RAW_VAR(c_name, 0) - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %edx // remember SP + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC // Outgoing argument set up - PUSH edx // pass SP + PUSH eax // push padding pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) PUSH ecx // pass arg2 PUSH eax // pass arg1 - call VAR(cxx_name, 1) // cxx_name(arg1, arg2, Thread*, SP) + call VAR(cxx_name, 1) // cxx_name(arg1, arg2, Thread*) addl MACRO_LITERAL(16), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-16) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address CALL_MACRO(return_macro, 2) // return or deliver exception END_FUNCTION RAW_VAR(c_name, 0) END_MACRO MACRO3(THREE_ARG_DOWNCALL, c_name, cxx_name, return_macro) DEFINE_FUNCTION RAW_VAR(c_name, 0) - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %ebx // remember SP + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC // Outgoing argument set up - subl MACRO_LITERAL(12), %esp // alignment padding - CFI_ADJUST_CFA_OFFSET(12) - PUSH ebx // pass SP pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) PUSH edx // pass arg3 PUSH ecx // pass arg2 PUSH eax // pass arg1 - call VAR(cxx_name, 1) // cxx_name(arg1, arg2, arg3, Thread*, SP) - addl MACRO_LITERAL(32), %esp // pop arguments - CFI_ADJUST_CFA_OFFSET(-32) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + call VAR(cxx_name, 1) // cxx_name(arg1, arg2, arg3, Thread*) + addl MACRO_LITERAL(16), %esp // pop arguments + CFI_ADJUST_CFA_OFFSET(-16) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address CALL_MACRO(return_macro, 2) // return or deliver exception END_FUNCTION RAW_VAR(c_name, 0) END_MACRO MACRO3(ONE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) DEFINE_FUNCTION RAW_VAR(c_name, 0) - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %edx // remember SP - mov 32(%esp), %ecx // get referrer + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC // Outgoing argument set up - PUSH edx // pass SP + mov FRAME_SIZE_REFS_ONLY_CALLEE_SAVE(%esp), %ecx // get referrer + PUSH eax // push padding pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) PUSH ecx // pass referrer PUSH eax // pass arg1 - call VAR(cxx_name, 1) // cxx_name(arg1, referrer, Thread*, SP) + call VAR(cxx_name, 1) // cxx_name(arg1, referrer, Thread*) addl MACRO_LITERAL(16), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-16) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address CALL_MACRO(return_macro, 2) // return or deliver exception END_FUNCTION RAW_VAR(c_name, 0) END_MACRO MACRO3(TWO_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) DEFINE_FUNCTION RAW_VAR(c_name, 0) - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %ebx // remember SP - mov 32(%esp), %edx // get referrer + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC + // Outgoing argument set up + mov FRAME_SIZE_REFS_ONLY_CALLEE_SAVE(%esp), %edx // get referrer + pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() + CFI_ADJUST_CFA_OFFSET(4) + PUSH edx // pass referrer + PUSH ecx // pass arg2 + PUSH eax // pass arg1 + call VAR(cxx_name, 1) // cxx_name(arg1, arg2, referrer, Thread*) + addl MACRO_LITERAL(16), %esp // pop arguments + CFI_ADJUST_CFA_OFFSET(-16) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + CALL_MACRO(return_macro, 2) // return or deliver exception + END_FUNCTION RAW_VAR(c_name, 0) +END_MACRO + +MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) + DEFINE_FUNCTION RAW_VAR(c_name, 0) + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC + // Outgoing argument set up + mov FRAME_SIZE_REFS_ONLY_CALLEE_SAVE(%esp), %ebx // get referrer subl MACRO_LITERAL(12), %esp // alignment padding CFI_ADJUST_CFA_OFFSET(12) - PUSH ebx // pass SP pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) - // Outgoing argument set up - PUSH edx // pass referrer + PUSH ebx // pass referrer + PUSH edx // pass arg3 PUSH ecx // pass arg2 PUSH eax // pass arg1 - call VAR(cxx_name, 1) // cxx_name(arg1, arg2, referrer, Thread*, SP) - addl MACRO_LITERAL(32), %esp // pop arguments + call VAR(cxx_name, 1) // cxx_name(arg1, arg2, arg3, referrer, Thread*) + addl LITERAL(32), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-32) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address CALL_MACRO(return_macro, 2) // return or deliver exception END_FUNCTION RAW_VAR(c_name, 0) END_MACRO - MACRO0(RETURN_IF_RESULT_IS_NON_ZERO) testl %eax, %eax // eax == 0 ? jz 1f // if eax == 0 goto 1 @@ -443,9 +479,8 @@ MACRO0(RETURN_IF_EAX_ZERO) END_MACRO MACRO0(RETURN_OR_DELIVER_PENDING_EXCEPTION) - mov %fs:THREAD_EXCEPTION_OFFSET, %ebx // get exception field - testl %ebx, %ebx // ebx == 0 ? - jnz 1f // if ebx != 0 goto 1 + cmpl MACRO_LITERAL(0),%fs:THREAD_EXCEPTION_OFFSET // exception field == 0 ? + jne 1f // if exception field != 0 goto 1 ret // return 1: // deliver exception on current thread DELIVER_PENDING_EXCEPTION @@ -566,7 +601,7 @@ DEFINE_FUNCTION art_quick_lock_object testl %eax, %eax // null check object/eax jz .Lslow_lock .Lretry_lock: - movl LOCK_WORD_OFFSET(%eax), %ecx // ecx := lock word + movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%eax), %ecx // ecx := lock word test LITERAL(0xC0000000), %ecx // test the 2 high bits. jne .Lslow_lock // slow path if either of the two high bits are set. movl %fs:THREAD_ID_OFFSET, %edx // edx := thread id @@ -575,11 +610,11 @@ DEFINE_FUNCTION art_quick_lock_object // unlocked case - %edx holds thread id with count of 0 movl %eax, %ecx // remember object in case of retry xor %eax, %eax // eax == 0 for comparison with lock word in cmpxchg - lock cmpxchg %edx, LOCK_WORD_OFFSET(%ecx) + lock cmpxchg %edx, MIRROR_OBJECT_LOCK_WORD_OFFSET(%ecx) jnz .Lcmpxchg_fail // cmpxchg failed retry ret .Lcmpxchg_fail: - movl %ecx, %eax // restore eax + movl %ecx, %eax // restore eax jmp .Lretry_lock .Lalready_thin: cmpw %cx, %dx // do we hold the lock already? @@ -587,28 +622,28 @@ DEFINE_FUNCTION art_quick_lock_object addl LITERAL(65536), %ecx // increment recursion count test LITERAL(0xC0000000), %ecx // overflowed if either of top two bits are set jne .Lslow_lock // count overflowed so go slow - movl %ecx, LOCK_WORD_OFFSET(%eax) // update lockword, cmpxchg not necessary as we hold lock + // update lockword, cmpxchg not necessary as we hold lock + movl %ecx, MIRROR_OBJECT_LOCK_WORD_OFFSET(%eax) ret .Lslow_lock: - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %edx // remember SP + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC // Outgoing argument set up - PUSH eax // push padding - PUSH edx // pass SP + subl LITERAL(8), %esp // alignment padding + CFI_ADJUST_CFA_OFFSET(8) pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) PUSH eax // pass object - call SYMBOL(artLockObjectFromCode) // artLockObjectFromCode(object, Thread*, SP) + call SYMBOL(artLockObjectFromCode) // artLockObjectFromCode(object, Thread*) addl LITERAL(16), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-16) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address RETURN_IF_EAX_ZERO END_FUNCTION art_quick_lock_object DEFINE_FUNCTION art_quick_unlock_object testl %eax, %eax // null check object/eax jz .Lslow_unlock - movl LOCK_WORD_OFFSET(%eax), %ecx // ecx := lock word + movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%eax), %ecx // ecx := lock word movl %fs:THREAD_ID_OFFSET, %edx // edx := thread id test LITERAL(0xC0000000), %ecx jnz .Lslow_unlock // lock word contains a monitor @@ -616,25 +651,24 @@ DEFINE_FUNCTION art_quick_unlock_object jne .Lslow_unlock cmpl LITERAL(65536), %ecx jae .Lrecursive_thin_unlock - movl LITERAL(0), LOCK_WORD_OFFSET(%eax) + movl LITERAL(0), MIRROR_OBJECT_LOCK_WORD_OFFSET(%eax) ret .Lrecursive_thin_unlock: subl LITERAL(65536), %ecx - mov %ecx, LOCK_WORD_OFFSET(%eax) + mov %ecx, MIRROR_OBJECT_LOCK_WORD_OFFSET(%eax) ret .Lslow_unlock: - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %edx // remember SP + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC // Outgoing argument set up - PUSH eax // push padding - PUSH edx // pass SP + subl LITERAL(8), %esp // alignment padding + CFI_ADJUST_CFA_OFFSET(8) pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) PUSH eax // pass object - call SYMBOL(artUnlockObjectFromCode) // artUnlockObjectFromCode(object, Thread*, SP) + call SYMBOL(artUnlockObjectFromCode) // artUnlockObjectFromCode(object, Thread*) addl LITERAL(16), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-16) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address RETURN_IF_EAX_ZERO END_FUNCTION art_quick_unlock_object @@ -663,15 +697,14 @@ DEFINE_FUNCTION art_quick_check_cast POP ecx addl LITERAL(4), %esp CFI_ADJUST_CFA_OFFSET(-12) - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context - mov %esp, %edx + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME ebx, ebx // save all registers as basis for long jump context // Outgoing argument set up - PUSH edx // pass SP + PUSH eax // alignment padding pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) PUSH ecx // pass arg2 PUSH eax // pass arg1 - call SYMBOL(artThrowClassCastException) // (Class* a, Class* b, Thread*, SP) + call SYMBOL(artThrowClassCastException) // (Class* a, Class* b, Thread*) int3 // unreached END_FUNCTION art_quick_check_cast @@ -687,7 +720,7 @@ DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check END_FUNCTION art_quick_aput_obj_with_null_and_bound_check DEFINE_FUNCTION art_quick_aput_obj_with_bound_check - movl ARRAY_LENGTH_OFFSET(%eax), %ebx + movl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ebx cmpl %ebx, %ecx jb SYMBOL(art_quick_aput_obj) mov %ecx, %eax @@ -698,18 +731,19 @@ END_FUNCTION art_quick_aput_obj_with_bound_check DEFINE_FUNCTION art_quick_aput_obj test %edx, %edx // store of null jz .Ldo_aput_null - movl CLASS_OFFSET(%eax), %ebx - movl CLASS_COMPONENT_TYPE_OFFSET(%ebx), %ebx - cmpl CLASS_OFFSET(%edx), %ebx // value's type == array's component type - trivial assignability + movl MIRROR_OBJECT_CLASS_OFFSET(%eax), %ebx + movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%ebx), %ebx + // value's type == array's component type - trivial assignability + cmpl MIRROR_OBJECT_CLASS_OFFSET(%edx), %ebx jne .Lcheck_assignability .Ldo_aput: - movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4) + movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4) movl %fs:THREAD_CARD_TABLE_OFFSET, %edx shrl LITERAL(7), %eax movb %dl, (%edx, %eax) ret .Ldo_aput_null: - movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4) + movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4) ret .Lcheck_assignability: PUSH eax // save arguments @@ -717,7 +751,7 @@ DEFINE_FUNCTION art_quick_aput_obj PUSH edx subl LITERAL(8), %esp // alignment padding CFI_ADJUST_CFA_OFFSET(8) - pushl CLASS_OFFSET(%edx) // pass arg2 - type of the value to be stored + pushl MIRROR_OBJECT_CLASS_OFFSET(%edx) // pass arg2 - type of the value to be stored CFI_ADJUST_CFA_OFFSET(4) PUSH ebx // pass arg1 - component type of the array call SYMBOL(artIsAssignableFromCode) // (Class* a, Class* b) @@ -728,7 +762,7 @@ DEFINE_FUNCTION art_quick_aput_obj POP edx POP ecx POP eax - movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4) // do the aput + movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4) // do the aput movl %fs:THREAD_CARD_TABLE_OFFSET, %edx shrl LITERAL(7), %eax movb %dl, (%edx, %eax) @@ -737,20 +771,19 @@ DEFINE_FUNCTION art_quick_aput_obj POP edx POP ecx POP eax - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context - mov %esp, %ecx + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME ebx, ebx // save all registers as basis for long jump context // Outgoing argument set up - PUSH ecx // pass SP + PUSH eax // alignment padding pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) PUSH edx // pass arg2 - value PUSH eax // pass arg1 - array - call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*, SP) + call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*) int3 // unreached END_FUNCTION art_quick_aput_obj DEFINE_FUNCTION art_quick_memcpy - SETUP_GOT_NOSAVE // clobbers EBX + SETUP_GOT_NOSAVE ebx // clobbers EBX PUSH edx // pass arg3 PUSH ecx // pass arg2 PUSH eax // pass arg1 @@ -856,236 +889,76 @@ DEFINE_FUNCTION art_quick_lushr ret END_FUNCTION art_quick_lushr -DEFINE_FUNCTION art_quick_set8_instance - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %ebx // remember SP - subl LITERAL(8), %esp // alignment padding - CFI_ADJUST_CFA_OFFSET(8) - PUSH ebx // pass SP - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() - CFI_ADJUST_CFA_OFFSET(4) - mov 32(%ebx), %ebx // get referrer - PUSH ebx // pass referrer - PUSH edx // pass new_val - PUSH ecx // pass object - PUSH eax // pass field_idx - call PLT_SYMBOL(artSet8InstanceFromCode) // (field_idx, Object*, new_val, referrer, Thread*, SP) - addl LITERAL(32), %esp // pop arguments - CFI_ADJUST_CFA_OFFSET(-32) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - RETURN_IF_EAX_ZERO // return or deliver exception -END_FUNCTION art_quick_set8_instance +ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION +ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION +ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION +ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION +ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION +ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION +ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION -DEFINE_FUNCTION art_quick_set16_instance - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %ebx // remember SP - subl LITERAL(8), %esp // alignment padding - CFI_ADJUST_CFA_OFFSET(8) - PUSH ebx // pass SP - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() - CFI_ADJUST_CFA_OFFSET(4) - mov 32(%ebx), %ebx // get referrer - PUSH ebx // pass referrer - PUSH edx // pass new_val - PUSH ecx // pass object - PUSH eax // pass field_idx - call PLT_SYMBOL(artSet16InstanceFromCode) // (field_idx, Object*, new_val, referrer, Thread*, SP) - addl LITERAL(32), %esp // pop arguments - CFI_ADJUST_CFA_OFFSET(-32) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - RETURN_IF_EAX_ZERO // return or deliver exception -END_FUNCTION art_quick_set16_instance +TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION +TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION +TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION +TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION +TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION +TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION +TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION -DEFINE_FUNCTION art_quick_set32_instance - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %ebx // remember SP - subl LITERAL(8), %esp // alignment padding - CFI_ADJUST_CFA_OFFSET(8) - PUSH ebx // pass SP - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() - CFI_ADJUST_CFA_OFFSET(4) - mov 32(%ebx), %ebx // get referrer - PUSH ebx // pass referrer - PUSH edx // pass new_val - PUSH ecx // pass object - PUSH eax // pass field_idx - call SYMBOL(artSet32InstanceFromCode) // (field_idx, Object*, new_val, referrer, Thread*, SP) - addl LITERAL(32), %esp // pop arguments - CFI_ADJUST_CFA_OFFSET(-32) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - RETURN_IF_EAX_ZERO // return or deliver exception -END_FUNCTION art_quick_set32_instance +TWO_ARG_REF_DOWNCALL art_quick_set8_static, artSet8StaticFromCode, RETURN_IF_EAX_ZERO +TWO_ARG_REF_DOWNCALL art_quick_set16_static, artSet16StaticFromCode, RETURN_IF_EAX_ZERO +TWO_ARG_REF_DOWNCALL art_quick_set32_static, artSet32StaticFromCode, RETURN_IF_EAX_ZERO +TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCode, RETURN_IF_EAX_ZERO +THREE_ARG_REF_DOWNCALL art_quick_set8_instance, artSet8InstanceFromCode, RETURN_IF_EAX_ZERO +THREE_ARG_REF_DOWNCALL art_quick_set16_instance, artSet16InstanceFromCode, RETURN_IF_EAX_ZERO +THREE_ARG_REF_DOWNCALL art_quick_set32_instance, artSet32InstanceFromCode, RETURN_IF_EAX_ZERO +THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCode, RETURN_IF_EAX_ZERO + +// Call artSet64InstanceFromCode with 4 word size arguments and the referrer. DEFINE_FUNCTION art_quick_set64_instance - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC + // Outgoing argument set up + mov FRAME_SIZE_REFS_ONLY_CALLEE_SAVE(%esp), %ebx // get referrer subl LITERAL(8), %esp // alignment padding CFI_ADJUST_CFA_OFFSET(8) - PUSH esp // pass SP-8 - addl LITERAL(8), (%esp) // fix SP on stack by adding 8 pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) + pushl (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE+12)(%esp) // pass referrer + CFI_ADJUST_CFA_OFFSET(4) PUSH ebx // pass high half of new_val PUSH edx // pass low half of new_val PUSH ecx // pass object PUSH eax // pass field_idx - call SYMBOL(artSet64InstanceFromCode) // (field_idx, Object*, new_val, Thread*, SP) + call SYMBOL(artSet64InstanceFromCode) // (field_idx, Object*, new_val, referrer, Thread*) addl LITERAL(32), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-32) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address RETURN_IF_EAX_ZERO // return or deliver exception END_FUNCTION art_quick_set64_instance -DEFINE_FUNCTION art_quick_set_obj_instance - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %ebx // remember SP - subl LITERAL(8), %esp // alignment padding - CFI_ADJUST_CFA_OFFSET(8) - PUSH ebx // pass SP - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() - CFI_ADJUST_CFA_OFFSET(4) - mov 32(%ebx), %ebx // get referrer - PUSH ebx // pass referrer - PUSH edx // pass new_val - PUSH ecx // pass object - PUSH eax // pass field_idx - call SYMBOL(artSetObjInstanceFromCode) // (field_idx, Object*, new_val, referrer, Thread*, SP) - addl LITERAL(32), %esp // pop arguments - CFI_ADJUST_CFA_OFFSET(-32) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - RETURN_IF_EAX_ZERO // return or deliver exception -END_FUNCTION art_quick_set_obj_instance - -TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION -TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION -TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION -TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION -TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION -TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION - -DEFINE_FUNCTION art_quick_get64_instance - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %ebx // remember SP - mov 32(%esp), %edx // get referrer - subl LITERAL(12), %esp // alignment padding - CFI_ADJUST_CFA_OFFSET(12) - PUSH ebx // pass SP - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() - CFI_ADJUST_CFA_OFFSET(4) - PUSH edx // pass referrer - PUSH ecx // pass object - PUSH eax // pass field_idx - call SYMBOL(artGet64InstanceFromCode) // (field_idx, Object*, referrer, Thread*, SP) - addl LITERAL(32), %esp // pop arguments - CFI_ADJUST_CFA_OFFSET(-32) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception -END_FUNCTION art_quick_get64_instance - -DEFINE_FUNCTION art_quick_set8_static - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %ebx // remember SP - mov 32(%esp), %edx // get referrer - subl LITERAL(12), %esp // alignment padding - CFI_ADJUST_CFA_OFFSET(12) - PUSH ebx // pass SP - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() - CFI_ADJUST_CFA_OFFSET(4) - PUSH edx // pass referrer - PUSH ecx // pass new_val - PUSH eax // pass field_idx - call SYMBOL(artSet8StaticFromCode) // (field_idx, new_val, referrer, Thread*, SP) - addl LITERAL(32), %esp // pop arguments - CFI_ADJUST_CFA_OFFSET(-32) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - RETURN_IF_EAX_ZERO // return or deliver exception -END_FUNCTION art_quick_set8_static - -DEFINE_FUNCTION art_quick_set16_static - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %ebx // remember SP - mov 32(%esp), %edx // get referrer - subl LITERAL(12), %esp // alignment padding - CFI_ADJUST_CFA_OFFSET(12) - PUSH ebx // pass SP - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() - CFI_ADJUST_CFA_OFFSET(4) - PUSH edx // pass referrer - PUSH ecx // pass new_val - PUSH eax // pass field_idx - call SYMBOL(artSet16StaticFromCode) // (field_idx, new_val, referrer, Thread*, SP) - addl LITERAL(32), %esp // pop arguments - CFI_ADJUST_CFA_OFFSET(-32) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - RETURN_IF_EAX_ZERO // return or deliver exception -END_FUNCTION art_quick_set16_static - -DEFINE_FUNCTION art_quick_set32_static - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %ebx // remember SP - mov 32(%esp), %edx // get referrer +// Call artSet64StaticFromCode with 3 word size arguments plus with the referrer in the 2nd position +// so that new_val is aligned on even registers were we passing arguments in registers. +DEFINE_FUNCTION art_quick_set64_static + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC + mov FRAME_SIZE_REFS_ONLY_CALLEE_SAVE(%esp), %ebx // get referrer subl LITERAL(12), %esp // alignment padding CFI_ADJUST_CFA_OFFSET(12) - PUSH ebx // pass SP pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) - PUSH edx // pass referrer - PUSH ecx // pass new_val - PUSH eax // pass field_idx - call SYMBOL(artSet32StaticFromCode) // (field_idx, new_val, referrer, Thread*, SP) - addl LITERAL(32), %esp // pop arguments - CFI_ADJUST_CFA_OFFSET(-32) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - RETURN_IF_EAX_ZERO // return or deliver exception -END_FUNCTION art_quick_set32_static - -DEFINE_FUNCTION art_quick_set64_static - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %ebx // remember SP - subl LITERAL(8), %esp // alignment padding - CFI_ADJUST_CFA_OFFSET(8) - PUSH ebx // pass SP - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() - CFI_ADJUST_CFA_OFFSET(4) - mov 32(%ebx), %ebx // get referrer PUSH edx // pass high half of new_val PUSH ecx // pass low half of new_val PUSH ebx // pass referrer PUSH eax // pass field_idx - call SYMBOL(artSet64StaticFromCode) // (field_idx, referrer, new_val, Thread*, SP) + call SYMBOL(artSet64StaticFromCode) // (field_idx, referrer, new_val, Thread*) addl LITERAL(32), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-32) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address RETURN_IF_EAX_ZERO // return or deliver exception END_FUNCTION art_quick_set64_static -DEFINE_FUNCTION art_quick_set_obj_static - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC - mov %esp, %ebx // remember SP - mov 32(%esp), %edx // get referrer - subl LITERAL(12), %esp // alignment padding - CFI_ADJUST_CFA_OFFSET(12) - PUSH ebx // pass SP - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() - CFI_ADJUST_CFA_OFFSET(4) - PUSH edx // pass referrer - PUSH ecx // pass new_val - PUSH eax // pass field_idx - call SYMBOL(artSetObjStaticFromCode) // (field_idx, new_val, referrer, Thread*, SP) - addl LITERAL(32), %esp // pop arguments - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - RETURN_IF_EAX_ZERO // return or deliver exception -END_FUNCTION art_quick_set_obj_static - -ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION -ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION -ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION -ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION -ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION -ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION -ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION - DEFINE_FUNCTION art_quick_proxy_invoke_handler - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME // save frame and Method* + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_EAX PUSH esp // pass SP pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) @@ -1107,15 +980,15 @@ END_FUNCTION art_quick_proxy_invoke_handler DEFINE_FUNCTION art_quick_imt_conflict_trampoline PUSH ecx movl 8(%esp), %eax // load caller Method* - movl METHOD_DEX_CACHE_METHODS_OFFSET(%eax), %eax // load dex_cache_resolved_methods + movl MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET(%eax), %eax // load dex_cache_resolved_methods movd %xmm0, %ecx // get target method index stored in xmm0 - movl OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4), %eax // load the target method + movl MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4), %eax // load the target method POP ecx jmp SYMBOL(art_quick_invoke_interface_trampoline) END_FUNCTION art_quick_imt_conflict_trampoline DEFINE_FUNCTION art_quick_resolution_trampoline - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME ebx, ebx movl %esp, %edi PUSH EDI // pass SP. do not just PUSH ESP; that messes up unwinding pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() @@ -1136,14 +1009,12 @@ DEFINE_FUNCTION art_quick_resolution_trampoline xchgl 0(%esp),%edi // restore EDI and place code pointer as only value on stack ret // tail call into method 1: - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME DELIVER_PENDING_EXCEPTION END_FUNCTION art_quick_resolution_trampoline -DEFINE_FUNCTION_NO_HIDE art_quick_generic_jni_trampoline - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME - // This also stores the native ArtMethod reference at the bottom of the stack. - +DEFINE_FUNCTION art_quick_generic_jni_trampoline + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_EAX movl %esp, %ebp // save SP at callee-save frame CFI_DEF_CFA_REGISTER(ebp) subl LITERAL(5120), %esp @@ -1151,7 +1022,6 @@ DEFINE_FUNCTION_NO_HIDE art_quick_generic_jni_trampoline // (Thread*, SP) // (esp) 4(esp) <= C calling convention // fs:... ebp <= where they are - // Also: PLT, so need GOT in ebx. subl LITERAL(8), %esp // Padding for 16B alignment. pushl %ebp // Pass SP (to ArtMethod). @@ -1216,12 +1086,12 @@ DEFINE_FUNCTION_NO_HIDE art_quick_generic_jni_trampoline movl %ebp, %esp CFI_DEF_CFA_REGISTER(esp) .Lexception_in_native: - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME DELIVER_PENDING_EXCEPTION END_FUNCTION art_quick_generic_jni_trampoline -DEFINE_FUNCTION_NO_HIDE art_quick_to_interpreter_bridge - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME // save frame +DEFINE_FUNCTION art_quick_to_interpreter_bridge + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME ebx, ebx // save frame mov %esp, %edx // remember SP PUSH eax // alignment padding PUSH edx // pass SP @@ -1234,7 +1104,7 @@ DEFINE_FUNCTION_NO_HIDE art_quick_to_interpreter_bridge punpckldq %xmm1, %xmm0 addl LITERAL(16), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-16) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception END_FUNCTION art_quick_to_interpreter_bridge @@ -1242,26 +1112,23 @@ END_FUNCTION art_quick_to_interpreter_bridge * Routine that intercepts method calls and returns. */ DEFINE_FUNCTION art_quick_instrumentation_entry - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME - movl %esp, %edx // Save SP. + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME ebx, edx PUSH eax // Save eax which will be clobbered by the callee-save method. - subl LITERAL(8), %esp // Align stack. - CFI_ADJUST_CFA_OFFSET(8) + subl LITERAL(12), %esp // Align stack. + CFI_ADJUST_CFA_OFFSET(12) pushl 40(%esp) // Pass LR. CFI_ADJUST_CFA_OFFSET(4) - PUSH edx // Pass SP. pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current(). CFI_ADJUST_CFA_OFFSET(4) PUSH ecx // Pass receiver. PUSH eax // Pass Method*. - call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, SP, LR) - SETUP_GOT_NOSAVE + call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, LR) addl LITERAL(28), %esp // Pop arguments upto saved Method*. movl 28(%esp), %edi // Restore edi. movl %eax, 28(%esp) // Place code* over edi, just under return pc. movl SYMBOL(art_quick_instrumentation_exit)@GOT(%ebx), %ebx + // Place instrumentation exit as return pc. ebx holds the GOT computed on entry. movl %ebx, 32(%esp) - // Place instrumentation exit as return pc. movl (%esp), %eax // Restore eax. movl 8(%esp), %ecx // Restore ecx. movl 12(%esp), %edx // Restore edx. @@ -1274,7 +1141,7 @@ END_FUNCTION art_quick_instrumentation_entry DEFINE_FUNCTION art_quick_instrumentation_exit pushl LITERAL(0) // Push a fake return PC as there will be none on the stack. - SETUP_REF_ONLY_CALLEE_SAVE_FRAME + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx mov %esp, %ecx // Remember SP subl LITERAL(8), %esp // Save float return value. CFI_ADJUST_CFA_OFFSET(8) @@ -1300,7 +1167,7 @@ DEFINE_FUNCTION art_quick_instrumentation_exit movq (%esp), %xmm0 // Restore fpr return value. addl LITERAL(8), %esp CFI_ADJUST_CFA_OFFSET(-8) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME addl LITERAL(4), %esp // Remove fake return pc. jmp *%ecx // Return. END_FUNCTION art_quick_instrumentation_exit @@ -1311,14 +1178,12 @@ END_FUNCTION art_quick_instrumentation_exit */ DEFINE_FUNCTION art_quick_deoptimize pushl %ebx // Fake that we were called. - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME - mov %esp, %ecx // Remember SP. - subl LITERAL(8), %esp // Align stack. - CFI_ADJUST_CFA_OFFSET(8) - PUSH ecx // Pass SP. + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME ebx, ebx + subl LITERAL(12), %esp // Align stack. + CFI_ADJUST_CFA_OFFSET(12) pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current(). CFI_ADJUST_CFA_OFFSET(4) - call SYMBOL(artDeoptimize) // artDeoptimize(Thread*, SP) + call SYMBOL(artDeoptimize) // artDeoptimize(Thread*) int3 // Unreachable. END_FUNCTION art_quick_deoptimize @@ -1332,15 +1197,15 @@ END_FUNCTION art_quick_deoptimize DEFINE_FUNCTION art_quick_string_compareto PUSH esi // push callee save reg PUSH edi // push callee save reg - mov STRING_COUNT_OFFSET(%eax), %edx - mov STRING_COUNT_OFFSET(%ecx), %ebx - mov STRING_VALUE_OFFSET(%eax), %esi - mov STRING_VALUE_OFFSET(%ecx), %edi - mov STRING_OFFSET_OFFSET(%eax), %eax - mov STRING_OFFSET_OFFSET(%ecx), %ecx + mov MIRROR_STRING_COUNT_OFFSET(%eax), %edx + mov MIRROR_STRING_COUNT_OFFSET(%ecx), %ebx + mov MIRROR_STRING_VALUE_OFFSET(%eax), %esi + mov MIRROR_STRING_VALUE_OFFSET(%ecx), %edi + mov MIRROR_STRING_OFFSET_OFFSET(%eax), %eax + mov MIRROR_STRING_OFFSET_OFFSET(%ecx), %ecx /* Build pointers to the start of string data */ - lea STRING_DATA_OFFSET(%esi, %eax, 2), %esi - lea STRING_DATA_OFFSET(%edi, %ecx, 2), %edi + lea MIRROR_CHAR_ARRAY_DATA_OFFSET(%esi, %eax, 2), %esi + lea MIRROR_CHAR_ARRAY_DATA_OFFSET(%edi, %ecx, 2), %edi /* Calculate min length and count diff */ mov %edx, %ecx mov %edx, %eax @@ -1375,7 +1240,7 @@ END_FUNCTION art_quick_string_compareto // eax: address of jmp_buf in TLS DEFINE_FUNCTION art_nested_signal_return - SETUP_GOT_NOSAVE // sets %ebx for call into PLT + SETUP_GOT_NOSAVE ebx // sets %ebx for call into PLT movl LITERAL(1), %ecx pushl %ecx // second arg to longjmp (1) pushl %eax // first arg to longjmp (jmp_buf) diff --git a/runtime/arch/x86_64/asm_support_x86_64.S b/runtime/arch/x86_64/asm_support_x86_64.S index 4ae61a2a65..5964314914 100644 --- a/runtime/arch/x86_64/asm_support_x86_64.S +++ b/runtime/arch/x86_64/asm_support_x86_64.S @@ -132,16 +132,6 @@ VAR(c_name, 0): CFI_DEF_CFA(rsp, 8) END_MACRO -MACRO1(DEFINE_FUNCTION_NO_HIDE, c_name) - FUNCTION_TYPE(\c_name, 0) - .globl VAR(c_name, 0) - ALIGN_FUNCTION_ENTRY -VAR(c_name, 0): - CFI_STARTPROC - // Ensure we get a sane starting CFA. - CFI_DEF_CFA(rsp, 8) -END_MACRO - MACRO1(END_FUNCTION, c_name) CFI_ENDPROC SIZE(\c_name, 0) @@ -172,18 +162,6 @@ VAR(name, 0): SIZE(\name, 0) END_MACRO -MACRO1(UNIMPLEMENTED_NO_HIDE,name) - FUNCTION_TYPE(\name, 0) - .globl VAR(name, 0) - ALIGN_FUNCTION_ENTRY -VAR(name, 0): - CFI_STARTPROC - int3 - int3 - CFI_ENDPROC - SIZE(\name, 0) -END_MACRO - MACRO0(UNREACHABLE) int3 END_MACRO diff --git a/runtime/arch/x86_64/asm_support_x86_64.h b/runtime/arch/x86_64/asm_support_x86_64.h index 40958dcdf1..eddd17206c 100644 --- a/runtime/arch/x86_64/asm_support_x86_64.h +++ b/runtime/arch/x86_64/asm_support_x86_64.h @@ -19,30 +19,8 @@ #include "asm_support.h" -// Note: these callee save methods loads require read barriers. -// Offset of field Runtime::callee_save_methods_[kSaveAll] -#define RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET 0 -// Offset of field Runtime::callee_save_methods_[kRefsOnly] -#define RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET 8 -// Offset of field Runtime::callee_save_methods_[kRefsAndArgs] -#define RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET 16 - -// Offset of field Thread::self_ verified in InitCpu -#define THREAD_SELF_OFFSET 192 -// Offset of field Thread::card_table_ verified in InitCpu -#define THREAD_CARD_TABLE_OFFSET 120 -// Offset of field Thread::exception_ verified in InitCpu -#define THREAD_EXCEPTION_OFFSET 128 -// Offset of field Thread::thin_lock_thread_id_ verified in InitCpu -#define THREAD_ID_OFFSET 12 - #define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 64 + 4*8 #define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 64 + 4*8 #define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 176 + 4*8 -// Expected size of a heap reference -#define HEAP_REFERENCE_SIZE 4 -// Expected size of a stack reference -#define STACK_REFERENCE_SIZE 4 - #endif // ART_RUNTIME_ARCH_X86_64_ASM_SUPPORT_X86_64_H_ diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc index c9028e1355..be7359403a 100644 --- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc +++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc @@ -19,19 +19,12 @@ #include "entrypoints/portable/portable_entrypoints.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "entrypoints/entrypoint_utils.h" #include "entrypoints/math_entrypoints.h" +#include "entrypoints/runtime_asm_entrypoints.h" +#include "interpreter/interpreter.h" namespace art { -// Interpreter entrypoints. -extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result); -extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result); - // Portable entrypoints. extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*); extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*); diff --git a/runtime/arch/x86_64/portable_entrypoints_x86_64.S b/runtime/arch/x86_64/portable_entrypoints_x86_64.S index 7b84d178db..3a54005aee 100644 --- a/runtime/arch/x86_64/portable_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/portable_entrypoints_x86_64.S @@ -25,4 +25,6 @@ UNIMPLEMENTED art_portable_proxy_invoke_handler UNIMPLEMENTED art_portable_resolution_trampoline -UNIMPLEMENTED_NO_HIDE art_portable_to_interpreter_bridge +UNIMPLEMENTED art_portable_to_interpreter_bridge + +UNIMPLEMENTED art_portable_imt_conflict_trampoline diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index ca9c0bf0fa..bed7238b09 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -57,25 +57,25 @@ MACRO0(SETUP_SAVE_ALL_CALLEE_SAVE_FRAME) PUSH r12 // Callee save. PUSH rbp // Callee save. PUSH rbx // Callee save. - // Create space for FPR args, plus padding for alignment - subq LITERAL(4 * 8), %rsp - CFI_ADJUST_CFA_OFFSET(4 * 8) + // Create space for FPR args, plus space for StackReference<ArtMethod>. + subq MACRO_LITERAL(4 * 8 + 8), %rsp + CFI_ADJUST_CFA_OFFSET(4 * 8 + 8) // Save FPRs. - movq %xmm12, 0(%rsp) - movq %xmm13, 8(%rsp) - movq %xmm14, 16(%rsp) - movq %xmm15, 24(%rsp) - subq MACRO_LITERAL(8), %rsp // Space for Method* (also aligns the frame). - CFI_ADJUST_CFA_OFFSET(8) + movq %xmm12, 8(%rsp) + movq %xmm13, 16(%rsp) + movq %xmm14, 24(%rsp) + movq %xmm15, 32(%rsp) // R10 := ArtMethod* for save all callee save frame method. THIS_LOAD_REQUIRES_READ_BARRIER movq RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10 // Store ArtMethod* to bottom of stack. movq %r10, 0(%rsp) + // Store rsp as the top quick frame. + movq %rsp, %gs:THREAD_TOP_QUICK_FRAME_OFFSET // Ugly compile-time check, but we only have the preprocessor. // Last +8: implicit return address pushed on stack when caller made call. -#if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 6*8 + 4*8 + 8 + 8) +#if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 6 * 8 + 4 * 8 + 8 + 8) #error "SAVE_ALL_CALLEE_SAVE_FRAME(X86_64) size not as expected." #endif #endif // __APPLE__ @@ -85,7 +85,7 @@ END_MACRO * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kRefsOnly) */ -MACRO0(SETUP_REF_ONLY_CALLEE_SAVE_FRAME) +MACRO0(SETUP_REFS_ONLY_CALLEE_SAVE_FRAME) #if defined(__APPLE__) int3 int3 @@ -100,9 +100,9 @@ MACRO0(SETUP_REF_ONLY_CALLEE_SAVE_FRAME) PUSH r12 // Callee save. PUSH rbp // Callee save. PUSH rbx // Callee save. - // Create space for FPR args, plus padding for alignment - subq LITERAL(8 + 4*8), %rsp - CFI_ADJUST_CFA_OFFSET(8 + 4*8) + // Create space for FPR args, plus space for StackReference<ArtMethod>. + subq LITERAL(8 + 4 * 8), %rsp + CFI_ADJUST_CFA_OFFSET(8 + 4 * 8) // Save FPRs. movq %xmm12, 8(%rsp) movq %xmm13, 16(%rsp) @@ -113,16 +113,18 @@ MACRO0(SETUP_REF_ONLY_CALLEE_SAVE_FRAME) movq RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10 // Store ArtMethod* to bottom of stack. movq %r10, 0(%rsp) + // Store rsp as the stop quick frame. + movq %rsp, %gs:THREAD_TOP_QUICK_FRAME_OFFSET // Ugly compile-time check, but we only have the preprocessor. // Last +8: implicit return address pushed on stack when caller made call. -#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 6*8 + 4*8 + 8 + 8) +#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 6 * 8 + 4 * 8 + 8 + 8) #error "REFS_ONLY_CALLEE_SAVE_FRAME(X86_64) size not as expected." #endif #endif // __APPLE__ END_MACRO -MACRO0(RESTORE_REF_ONLY_CALLEE_SAVE_FRAME) +MACRO0(RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME) movq 8(%rsp), %xmm12 movq 16(%rsp), %xmm13 movq 24(%rsp), %xmm14 @@ -142,7 +144,7 @@ END_MACRO * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kRefsAndArgs) */ -MACRO0(SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME) +MACRO0(SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME) #if defined(__APPLE__) int3 int3 @@ -162,12 +164,13 @@ MACRO0(SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME) PUSH rbx // Callee save. PUSH rdx // Quick arg 2. PUSH rcx // Quick arg 3. - // Create space for FPR args and create 2 slots, 1 of padding and 1 for the ArtMethod*. + // Create space for FPR args and create 2 slots, 1 of padding and 1 for the + // StackReference<ArtMethod>. subq MACRO_LITERAL(80 + 4 * 8), %rsp CFI_ADJUST_CFA_OFFSET(80 + 4 * 8) // R10 := ArtMethod* for ref and args callee save frame method. THIS_LOAD_REQUIRES_READ_BARRIER - movq RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10 + movq RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10 // Save FPRs. movq %xmm0, 16(%rsp) movq %xmm1, 24(%rsp) @@ -183,16 +186,54 @@ MACRO0(SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME) movq %xmm15, 104(%rsp) // Store ArtMethod* to bottom of stack. movq %r10, 0(%rsp) + // Store rsp as the top quick frame. + movq %rsp, %gs:THREAD_TOP_QUICK_FRAME_OFFSET // Ugly compile-time check, but we only have the preprocessor. // Last +8: implicit return address pushed on stack when caller made call. -#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 11*8 + 4*8 + 80 + 8) +#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 11 * 8 + 4 * 8 + 80 + 8) #error "REFS_AND_ARGS_CALLEE_SAVE_FRAME(X86_64) size not as expected." #endif #endif // __APPLE__ END_MACRO -MACRO0(RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME) +MACRO0(SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_RDI) + // Save callee and GPR args, mixed together to agree with core spills bitmap. + PUSH r15 // Callee save. + PUSH r14 // Callee save. + PUSH r13 // Callee save. + PUSH r12 // Callee save. + PUSH r9 // Quick arg 5. + PUSH r8 // Quick arg 4. + PUSH rsi // Quick arg 1. + PUSH rbp // Callee save. + PUSH rbx // Callee save. + PUSH rdx // Quick arg 2. + PUSH rcx // Quick arg 3. + // Create space for FPR args and create 2 slots, 1 of padding and 1 for the + // StackReference<ArtMethod>. + subq LITERAL(80 + 4 * 8), %rsp + CFI_ADJUST_CFA_OFFSET(80 + 4 * 8) + // Save FPRs. + movq %xmm0, 16(%rsp) + movq %xmm1, 24(%rsp) + movq %xmm2, 32(%rsp) + movq %xmm3, 40(%rsp) + movq %xmm4, 48(%rsp) + movq %xmm5, 56(%rsp) + movq %xmm6, 64(%rsp) + movq %xmm7, 72(%rsp) + movq %xmm12, 80(%rsp) + movq %xmm13, 88(%rsp) + movq %xmm14, 96(%rsp) + movq %xmm15, 104(%rsp) + // Store ArtMethod to bottom of stack. + movq %rdi, 0(%rsp) + // Store rsp as the stop quick frame. + movq %rsp, %gs:THREAD_TOP_QUICK_FRAME_OFFSET +END_MACRO + +MACRO0(RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME) // Restore FPRs. movq 16(%rsp), %xmm0 movq 24(%rsp), %xmm1 @@ -229,10 +270,9 @@ END_MACRO */ MACRO0(DELIVER_PENDING_EXCEPTION) SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save callee saves for throw - // (Thread*, SP) setup + // (Thread*) setup movq %gs:THREAD_SELF_OFFSET, %rdi - movq %rsp, %rsi - call SYMBOL(artDeliverPendingExceptionFromCode) // artDeliverPendingExceptionFromCode(Thread*, SP) + call SYMBOL(artDeliverPendingExceptionFromCode) // artDeliverPendingExceptionFromCode(Thread*) UNREACHABLE END_MACRO @@ -240,9 +280,8 @@ MACRO2(NO_ARG_RUNTIME_EXCEPTION, c_name, cxx_name) DEFINE_FUNCTION VAR(c_name, 0) SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context // Outgoing argument set up - movq %rsp, %rsi // pass SP movq %gs:THREAD_SELF_OFFSET, %rdi // pass Thread::Current() - call VAR(cxx_name, 1) // cxx_name(Thread*, SP) + call VAR(cxx_name, 1) // cxx_name(Thread*) UNREACHABLE END_FUNCTION VAR(c_name, 0) END_MACRO @@ -251,9 +290,8 @@ MACRO2(ONE_ARG_RUNTIME_EXCEPTION, c_name, cxx_name) DEFINE_FUNCTION VAR(c_name, 0) SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context // Outgoing argument set up - movq %rsp, %rdx // pass SP movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current() - call VAR(cxx_name, 1) // cxx_name(arg1, Thread*, SP) + call VAR(cxx_name, 1) // cxx_name(arg1, Thread*) UNREACHABLE END_FUNCTION VAR(c_name, 0) END_MACRO @@ -262,9 +300,8 @@ MACRO2(TWO_ARG_RUNTIME_EXCEPTION, c_name, cxx_name) DEFINE_FUNCTION VAR(c_name, 0) SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context // Outgoing argument set up - movq %rsp, %rcx // pass SP movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current() - call VAR(cxx_name, 1) // cxx_name(Thread*, SP) + call VAR(cxx_name, 1) // cxx_name(Thread*) UNREACHABLE END_FUNCTION VAR(c_name, 0) END_MACRO @@ -321,7 +358,7 @@ TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromC */ MACRO2(INVOKE_TRAMPOLINE, c_name, cxx_name) DEFINE_FUNCTION VAR(c_name, 0) - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME // save callee saves in case allocation triggers GC + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME // save callee saves in case allocation triggers GC // Helper signature is always // (method_idx, *this_object, *caller_method, *self, sp) @@ -333,7 +370,7 @@ MACRO2(INVOKE_TRAMPOLINE, c_name, cxx_name) // save the code pointer movq %rax, %rdi movq %rdx, %rax - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME testq %rdi, %rdi jz 1f @@ -481,7 +518,7 @@ DEFINE_FUNCTION art_quick_invoke_stub LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished .Lgpr_setup_finished: - call *METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method. + call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method. movq %rbp, %rsp // Restore stack pointer. CFI_DEF_CFA_REGISTER(rsp) POP r9 // Pop r9 - shorty*. @@ -564,7 +601,7 @@ DEFINE_FUNCTION art_quick_invoke_static_stub LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished2 LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished2 .Lgpr_setup_finished2: - call *METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method. + call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method. movq %rbp, %rsp // Restore stack pointer. CFI_DEF_CFA_REGISTER(rsp) POP r9 // Pop r9 - shorty*. @@ -639,88 +676,81 @@ END_FUNCTION art_quick_do_long_jump MACRO3(NO_ARG_DOWNCALL, c_name, cxx_name, return_macro) DEFINE_FUNCTION VAR(c_name, 0) - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC // Outgoing argument set up - movq %rsp, %rsi // pass SP - movq %gs:THREAD_SELF_OFFSET, %rdi // pass Thread::Current() - call VAR(cxx_name, 1) // cxx_name(Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - CALL_MACRO(return_macro, 2) // return or deliver exception + movq %gs:THREAD_SELF_OFFSET, %rdi // pass Thread::Current() + call VAR(cxx_name, 1) // cxx_name(Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + CALL_MACRO(return_macro, 2) // return or deliver exception END_FUNCTION VAR(c_name, 0) END_MACRO MACRO3(ONE_ARG_DOWNCALL, c_name, cxx_name, return_macro) DEFINE_FUNCTION VAR(c_name, 0) - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC // Outgoing argument set up - movq %rsp, %rdx // pass SP - movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current() - call VAR(cxx_name, 1) // cxx_name(arg0, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - CALL_MACRO(return_macro, 2) // return or deliver exception + movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current() + call VAR(cxx_name, 1) // cxx_name(arg0, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + CALL_MACRO(return_macro, 2) // return or deliver exception END_FUNCTION VAR(c_name, 0) END_MACRO MACRO3(TWO_ARG_DOWNCALL, c_name, cxx_name, return_macro) DEFINE_FUNCTION VAR(c_name, 0) - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC // Outgoing argument set up - movq %rsp, %rcx // pass SP - movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current() - call VAR(cxx_name, 1) // cxx_name(arg0, arg1, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - CALL_MACRO(return_macro, 2) // return or deliver exception + movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current() + call VAR(cxx_name, 1) // cxx_name(arg0, arg1, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + CALL_MACRO(return_macro, 2) // return or deliver exception END_FUNCTION VAR(c_name, 0) END_MACRO MACRO3(THREE_ARG_DOWNCALL, c_name, cxx_name, return_macro) DEFINE_FUNCTION VAR(c_name, 0) - SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC // Outgoing argument set up - movq %rsp, %r8 // pass SP - movq %gs:THREAD_SELF_OFFSET, %rcx // pass Thread::Current() - call VAR(cxx_name, 1) // cxx_name(arg0, arg1, arg2, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - CALL_MACRO(return_macro, 2) // return or deliver exception + movq %gs:THREAD_SELF_OFFSET, %rcx // pass Thread::Current() + call VAR(cxx_name, 1) // cxx_name(arg0, arg1, arg2, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + CALL_MACRO(return_macro, 2) // return or deliver exception END_FUNCTION VAR(c_name, 0) END_MACRO MACRO3(ONE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) DEFINE_FUNCTION VAR(c_name, 0) - movl 8(%rsp), %esi // pass referrer - SETUP_REF_ONLY_CALLEE_SAVE_FRAME - // arg0 is in rdi - movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current() - movq %rsp, %rcx // pass SP - call VAR(cxx_name, 1) // cxx_name(arg0, referrer, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + movl 8(%rsp), %esi // pass referrer + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME + // arg0 is in rdi + movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current() + call VAR(cxx_name, 1) // cxx_name(arg0, referrer, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address CALL_MACRO(return_macro, 2) END_FUNCTION VAR(c_name, 0) END_MACRO MACRO3(TWO_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) DEFINE_FUNCTION VAR(c_name, 0) - movl 8(%rsp), %edx // pass referrer - SETUP_REF_ONLY_CALLEE_SAVE_FRAME - // arg0 and arg1 are in rdi/rsi - movq %gs:THREAD_SELF_OFFSET, %rcx // pass Thread::Current() - movq %rsp, %r8 // pass SP - call VAR(cxx_name, 1) // (arg0, arg1, referrer, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + movl 8(%rsp), %edx // pass referrer + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME + // arg0 and arg1 are in rdi/rsi + movq %gs:THREAD_SELF_OFFSET, %rcx // pass Thread::Current() + call VAR(cxx_name, 1) // (arg0, arg1, referrer, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address CALL_MACRO(return_macro, 2) END_FUNCTION VAR(c_name, 0) END_MACRO MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) DEFINE_FUNCTION VAR(c_name, 0) - movl 8(%rsp), %ecx // pass referrer - SETUP_REF_ONLY_CALLEE_SAVE_FRAME - // arg0, arg1, and arg2 are in rdi/rsi/rdx + movl 8(%rsp), %ecx // pass referrer + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME + // arg0, arg1, and arg2 are in rdi/rsi/rdx movq %gs:THREAD_SELF_OFFSET, %r8 // pass Thread::Current() - movq %rsp, %r9 // pass SP - call VAR(cxx_name, 1) // cxx_name(arg0, arg1, arg2, referrer, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - CALL_MACRO(return_macro, 2) // return or deliver exception + call VAR(cxx_name, 1) // cxx_name(arg0, arg1, arg2, referrer, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + CALL_MACRO(return_macro, 2) // return or deliver exception END_FUNCTION VAR(c_name, 0) END_MACRO @@ -864,7 +894,7 @@ DEFINE_FUNCTION art_quick_lock_object testl %edi, %edi // Null check object/rdi. jz .Lslow_lock .Lretry_lock: - movl LOCK_WORD_OFFSET(%edi), %ecx // ecx := lock word. + movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi), %ecx // ecx := lock word. test LITERAL(0xC0000000), %ecx // Test the 2 high bits. jne .Lslow_lock // Slow path if either of the two high bits are set. movl %gs:THREAD_ID_OFFSET, %edx // edx := thread id @@ -872,7 +902,7 @@ DEFINE_FUNCTION art_quick_lock_object jnz .Lalready_thin // Lock word contains a thin lock. // unlocked case - %edx holds thread id with count of 0 xor %eax, %eax // eax == 0 for comparison with lock word in cmpxchg - lock cmpxchg %edx, LOCK_WORD_OFFSET(%edi) + lock cmpxchg %edx, MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi) jnz .Lretry_lock // cmpxchg failed retry ret .Lalready_thin: @@ -881,21 +911,21 @@ DEFINE_FUNCTION art_quick_lock_object addl LITERAL(65536), %ecx // increment recursion count test LITERAL(0xC0000000), %ecx // overflowed if either of top two bits are set jne .Lslow_lock // count overflowed so go slow - movl %ecx, LOCK_WORD_OFFSET(%edi) // update lockword, cmpxchg not necessary as we hold lock + // update lockword, cmpxchg not necessary as we hold lock + movl %ecx, MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi) ret .Lslow_lock: - SETUP_REF_ONLY_CALLEE_SAVE_FRAME + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current() - movq %rsp, %rdx // pass SP - call SYMBOL(artLockObjectFromCode) // artLockObjectFromCode(object, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + call SYMBOL(artLockObjectFromCode) // artLockObjectFromCode(object, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address RETURN_IF_EAX_ZERO END_FUNCTION art_quick_lock_object DEFINE_FUNCTION art_quick_unlock_object testl %edi, %edi // null check object/edi jz .Lslow_unlock - movl LOCK_WORD_OFFSET(%edi), %ecx // ecx := lock word + movl MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi), %ecx // ecx := lock word movl %gs:THREAD_ID_OFFSET, %edx // edx := thread id test LITERAL(0xC0000000), %ecx jnz .Lslow_unlock // lock word contains a monitor @@ -903,18 +933,17 @@ DEFINE_FUNCTION art_quick_unlock_object jne .Lslow_unlock cmpl LITERAL(65536), %ecx jae .Lrecursive_thin_unlock - movl LITERAL(0), LOCK_WORD_OFFSET(%edi) + movl LITERAL(0), MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi) ret .Lrecursive_thin_unlock: subl LITERAL(65536), %ecx - mov %ecx, LOCK_WORD_OFFSET(%edi) + mov %ecx, MIRROR_OBJECT_LOCK_WORD_OFFSET(%edi) ret .Lslow_unlock: - SETUP_REF_ONLY_CALLEE_SAVE_FRAME + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current() - movq %rsp, %rdx // pass SP - call SYMBOL(artUnlockObjectFromCode) // artUnlockObjectFromCode(object, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + call SYMBOL(artUnlockObjectFromCode) // artUnlockObjectFromCode(object, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address RETURN_IF_EAX_ZERO END_FUNCTION art_quick_unlock_object @@ -935,9 +964,8 @@ DEFINE_FUNCTION art_quick_check_cast POP rsi // Pop arguments POP rdi SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context - mov %rsp, %rcx // pass SP mov %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current() - call SYMBOL(artThrowClassCastException) // (Class* a, Class* b, Thread*, SP) + call SYMBOL(artThrowClassCastException) // (Class* a, Class* b, Thread*) int3 // unreached END_FUNCTION art_quick_check_cast @@ -969,8 +997,8 @@ DEFINE_FUNCTION art_quick_aput_obj_with_bound_check int3 int3 #else - movl ARRAY_LENGTH_OFFSET(%edi), %ecx -// movl ARRAY_LENGTH_OFFSET(%rdi), %ecx // This zero-extends, so value(%rcx)=value(%ecx) + movl MIRROR_ARRAY_LENGTH_OFFSET(%edi), %ecx +// movl MIRROR_ARRAY_LENGTH_OFFSET(%rdi), %ecx // This zero-extends, so value(%rcx)=value(%ecx) cmpl %ecx, %esi jb art_quick_aput_obj mov %esi, %edi @@ -986,24 +1014,24 @@ DEFINE_FUNCTION art_quick_aput_obj testl %edx, %edx // store of null // test %rdx, %rdx jz .Ldo_aput_null - movl CLASS_OFFSET(%edi), %ecx -// movq CLASS_OFFSET(%rdi), %rcx - movl CLASS_COMPONENT_TYPE_OFFSET(%ecx), %ecx -// movq CLASS_COMPONENT_TYPE_OFFSET(%rcx), %rcx - cmpl CLASS_OFFSET(%edx), %ecx // value's type == array's component type - trivial assignability -// cmpq CLASS_OFFSET(%rdx), %rcx + movl MIRROR_OBJECT_CLASS_OFFSET(%edi), %ecx +// movq MIRROR_OBJECT_CLASS_OFFSET(%rdi), %rcx + movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%ecx), %ecx +// movq MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%rcx), %rcx + cmpl MIRROR_OBJECT_CLASS_OFFSET(%edx), %ecx // value's type == array's component type - trivial assignability +// cmpq MIRROR_CLASS_OFFSET(%rdx), %rcx jne .Lcheck_assignability .Ldo_aput: - movl %edx, OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4) -// movq %rdx, OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4) + movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4) +// movq %rdx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4) movq %gs:THREAD_CARD_TABLE_OFFSET, %rdx shrl LITERAL(7), %edi // shrl LITERAL(7), %rdi movb %dl, (%rdx, %rdi) // Note: this assumes that top 32b of %rdi are zero ret .Ldo_aput_null: - movl %edx, OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4) -// movq %rdx, OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4) + movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4) +// movq %rdx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4) ret .Lcheck_assignability: // Save arguments. @@ -1015,7 +1043,7 @@ DEFINE_FUNCTION art_quick_aput_obj SETUP_FP_CALLEE_SAVE_FRAME // "Uncompress" = do nothing, as already zero-extended on load. - movl CLASS_OFFSET(%edx), %esi // Pass arg2 = value's class. + movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %esi // Pass arg2 = value's class. movq %rcx, %rdi // Pass arg1 = array's component type. call SYMBOL(artIsAssignableFromCode) // (Class* a, Class* b) @@ -1032,8 +1060,8 @@ DEFINE_FUNCTION art_quick_aput_obj POP rsi POP rdi - movl %edx, OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4) -// movq %rdx, OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4) + movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4) +// movq %rdx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4) movq %gs:THREAD_CARD_TABLE_OFFSET, %rdx shrl LITERAL(7), %edi // shrl LITERAL(7), %rdi @@ -1052,12 +1080,10 @@ DEFINE_FUNCTION art_quick_aput_obj SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // Save all registers as basis for long jump context. // Outgoing argument set up. - movq %rsp, %rcx // Pass arg 4 = SP. movq %rdx, %rsi // Pass arg 2 = value. - movq %gs:THREAD_SELF_OFFSET, %rdx // Pass arg 3 = Thread::Current(). + movq %gs:THREAD_SELF_OFFSET, %rdx // Pass arg 3 = Thread::Current(). // Pass arg 1 = array. - - call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*, SP) + call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*) int3 // unreached END_FUNCTION art_quick_aput_obj @@ -1079,7 +1105,7 @@ UNIMPLEMENTED art_quick_lushr THREE_ARG_REF_DOWNCALL art_quick_set8_instance, artSet8InstanceFromCode, RETURN_IF_EAX_ZERO THREE_ARG_REF_DOWNCALL art_quick_set16_instance, artSet16InstanceFromCode, RETURN_IF_EAX_ZERO THREE_ARG_REF_DOWNCALL art_quick_set32_instance, artSet32InstanceFromCode, RETURN_IF_EAX_ZERO -THREE_ARG_DOWNCALL art_quick_set64_instance, artSet64InstanceFromCode, RETURN_IF_EAX_ZERO +THREE_ARG_REF_DOWNCALL art_quick_set64_instance, artSet64InstanceFromCode, RETURN_IF_EAX_ZERO THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCode, RETURN_IF_EAX_ZERO TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION @@ -1105,55 +1131,25 @@ ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCode, RETURN_O // This is singled out as the argument order is different. DEFINE_FUNCTION art_quick_set64_static - movq %rsi, %rdx // pass new_val - movl 8(%rsp), %esi // pass referrer - SETUP_REF_ONLY_CALLEE_SAVE_FRAME - // field_idx is in rdi - movq %gs:THREAD_SELF_OFFSET, %rcx // pass Thread::Current() - movq %rsp, %r8 // pass SP - call SYMBOL(artSet64StaticFromCode) // (field_idx, referrer, new_val, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address - RETURN_IF_EAX_ZERO // return or deliver exception + movq %rsi, %rdx // pass new_val + movl 8(%rsp), %esi // pass referrer + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME + // field_idx is in rdi + movq %gs:THREAD_SELF_OFFSET, %rcx // pass Thread::Current() + call SYMBOL(artSet64StaticFromCode) // (field_idx, referrer, new_val, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + RETURN_IF_EAX_ZERO // return or deliver exception END_FUNCTION art_quick_set64_static DEFINE_FUNCTION art_quick_proxy_invoke_handler - // Save callee and GPR args, mixed together to agree with core spills bitmap of ref. and args - // callee save frame. - PUSH r15 // Callee save. - PUSH r14 // Callee save. - PUSH r13 // Callee save. - PUSH r12 // Callee save. - PUSH r9 // Quick arg 5. - PUSH r8 // Quick arg 4. - PUSH rsi // Quick arg 1. - PUSH rbp // Callee save. - PUSH rbx // Callee save. - PUSH rdx // Quick arg 2. - PUSH rcx // Quick arg 3. - // Create space for FPR args and create 2 slots, 1 of padding and 1 for the ArtMethod*. - subq LITERAL(80 + 4*8), %rsp - CFI_ADJUST_CFA_OFFSET(80 + 4*8) - // Save FPRs. - movq %xmm0, 16(%rsp) - movq %xmm1, 24(%rsp) - movq %xmm2, 32(%rsp) - movq %xmm3, 40(%rsp) - movq %xmm4, 48(%rsp) - movq %xmm5, 56(%rsp) - movq %xmm6, 64(%rsp) - movq %xmm7, 72(%rsp) - movq %xmm12, 80(%rsp) - movq %xmm13, 88(%rsp) - movq %xmm14, 96(%rsp) - movq %xmm15, 104(%rsp) - // Store proxy method to bottom of stack. - movq %rdi, 0(%rsp) - movq %gs:THREAD_SELF_OFFSET, %rdx // Pass Thread::Current(). - movq %rsp, %rcx // Pass SP. + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_RDI + + movq %gs:THREAD_SELF_OFFSET, %rdx // Pass Thread::Current(). + movq %rsp, %rcx // Pass SP. call SYMBOL(artQuickProxyInvokeHandler) // (proxy method, receiver, Thread*, SP) - movq %rax, %xmm0 // Copy return value in case of float returns. - addq LITERAL(168 + 4*8), %rsp // Pop arguments. + movq %rax, %xmm0 // Copy return value in case of float returns. + addq LITERAL(168 + 4*8), %rsp // Pop arguments. CFI_ADJUST_CFA_OFFSET(-168 - 4*8) RETURN_OR_DELIVER_PENDING_EXCEPTION END_FUNCTION art_quick_proxy_invoke_handler @@ -1168,20 +1164,20 @@ DEFINE_FUNCTION art_quick_imt_conflict_trampoline int3 #else movl 8(%rsp), %edi // load caller Method* - movl METHOD_DEX_CACHE_METHODS_OFFSET(%rdi), %edi // load dex_cache_resolved_methods - movl OBJECT_ARRAY_DATA_OFFSET(%rdi, %rax, 4), %edi // load the target method + movl MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET(%rdi), %edi // load dex_cache_resolved_methods + movl MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rax, 4), %edi // load the target method jmp art_quick_invoke_interface_trampoline #endif // __APPLE__ END_FUNCTION art_quick_imt_conflict_trampoline DEFINE_FUNCTION art_quick_resolution_trampoline - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME movq %gs:THREAD_SELF_OFFSET, %rdx movq %rsp, %rcx call SYMBOL(artQuickResolutionTrampoline) // (called, receiver, Thread*, SP) movq %rax, %r10 // Remember returned code pointer in R10. movq (%rsp), %rdi // Load called method into RDI. - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME testq %r10, %r10 // If code pointer is NULL goto deliver pending exception. jz 1f jmp *%r10 // Tail call into method. @@ -1266,38 +1262,12 @@ END_FUNCTION art_quick_resolution_trampoline /* * Called to do a generic JNI down-call */ -DEFINE_FUNCTION_NO_HIDE art_quick_generic_jni_trampoline - // Save callee and GPR args, mixed together to agree with core spills bitmap. - PUSH r15 // Callee save. - PUSH r14 // Callee save. - PUSH r13 // Callee save. - PUSH r12 // Callee save. - PUSH r9 // Quick arg 5. - PUSH r8 // Quick arg 4. - PUSH rsi // Quick arg 1. - PUSH rbp // Callee save. - PUSH rbx // Callee save. - PUSH rdx // Quick arg 2. - PUSH rcx // Quick arg 3. - // Create space for FPR args and create 2 slots, 1 of padding and 1 for the ArtMethod*. - subq LITERAL(80 + 4*8), %rsp - CFI_ADJUST_CFA_OFFSET(80 + 4*8) - // Save FPRs. - movq %xmm0, 16(%rsp) - movq %xmm1, 24(%rsp) - movq %xmm2, 32(%rsp) - movq %xmm3, 40(%rsp) - movq %xmm4, 48(%rsp) - movq %xmm5, 56(%rsp) - movq %xmm6, 64(%rsp) - movq %xmm7, 72(%rsp) - movq %xmm12, 80(%rsp) - movq %xmm13, 88(%rsp) - movq %xmm14, 96(%rsp) - movq %xmm15, 104(%rsp) - movq %rdi, 0(%rsp) // Store native ArtMethod* to bottom of stack. +DEFINE_FUNCTION art_quick_generic_jni_trampoline + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_RDI + movq %rsp, %rbp // save SP at (old) callee-save frame CFI_DEF_CFA_REGISTER(rbp) + // // reserve a lot of space // @@ -1453,12 +1423,12 @@ END_FUNCTION art_quick_generic_jni_trampoline * RDI = method being called / to bridge to. * RSI, RDX, RCX, R8, R9 are arguments to that method. */ -DEFINE_FUNCTION_NO_HIDE art_quick_to_interpreter_bridge - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME // Set up frame and save arguments. +DEFINE_FUNCTION art_quick_to_interpreter_bridge + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME // Set up frame and save arguments. movq %gs:THREAD_SELF_OFFSET, %rsi // RSI := Thread::Current() movq %rsp, %rdx // RDX := sp call SYMBOL(artQuickToInterpreterBridge) // (method, Thread*, SP) - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME // TODO: no need to restore arguments in this case. + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME // TODO: no need to restore arguments in this case. movq %rax, %xmm0 // Place return value also into floating point return value. RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception END_FUNCTION art_quick_to_interpreter_bridge @@ -1471,15 +1441,14 @@ DEFINE_FUNCTION art_quick_instrumentation_entry int3 int3 #else - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME movq %rdi, %r12 // Preserve method pointer in a callee-save. movq %gs:THREAD_SELF_OFFSET, %rdx // Pass thread. - movq %rsp, %rcx // Pass SP. movq FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE-8(%rsp), %r8 // Pass return PC. - call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, SP, LR) + call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, LR) // %rax = result of call. movq %r12, %rdi // Reload method pointer. @@ -1487,7 +1456,7 @@ DEFINE_FUNCTION art_quick_instrumentation_entry leaq art_quick_instrumentation_exit(%rip), %r12 // Set up return through instrumentation movq %r12, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE-8(%rsp) // exit. - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME jmp *%rax // Tail call to intended method. #endif // __APPLE__ @@ -1496,7 +1465,7 @@ END_FUNCTION art_quick_instrumentation_entry DEFINE_FUNCTION art_quick_instrumentation_exit pushq LITERAL(0) // Push a fake return PC as there will be none on the stack. - SETUP_REF_ONLY_CALLEE_SAVE_FRAME + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // We need to save rax and xmm0. We could use a callee-save from SETUP_REF_ONLY, but then // we would need to fully restore it. As there are a good number of callee-save registers, it @@ -1536,9 +1505,8 @@ DEFINE_FUNCTION art_quick_deoptimize pushq %rsi // Fake that we were called. Use hidden arg. SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // Stack should be aligned now. - movq %rsp, %rsi // Pass SP. movq %gs:THREAD_SELF_OFFSET, %rdi // Pass Thread. - call SYMBOL(artDeoptimize) // artDeoptimize(Thread*, SP) + call SYMBOL(artDeoptimize) // artDeoptimize(Thread*) int3 // Unreachable. END_FUNCTION art_quick_deoptimize @@ -1551,15 +1519,15 @@ END_FUNCTION art_quick_deoptimize * rsi: comp string object (known non-null) */ DEFINE_FUNCTION art_quick_string_compareto - movl STRING_COUNT_OFFSET(%edi), %r8d - movl STRING_COUNT_OFFSET(%esi), %r9d - movl STRING_VALUE_OFFSET(%edi), %r10d - movl STRING_VALUE_OFFSET(%esi), %r11d - movl STRING_OFFSET_OFFSET(%edi), %eax - movl STRING_OFFSET_OFFSET(%esi), %ecx + movl MIRROR_STRING_COUNT_OFFSET(%edi), %r8d + movl MIRROR_STRING_COUNT_OFFSET(%esi), %r9d + movl MIRROR_STRING_VALUE_OFFSET(%edi), %r10d + movl MIRROR_STRING_VALUE_OFFSET(%esi), %r11d + movl MIRROR_STRING_OFFSET_OFFSET(%edi), %eax + movl MIRROR_STRING_OFFSET_OFFSET(%esi), %ecx /* Build pointers to the start of string data */ - leal STRING_DATA_OFFSET(%r10d, %eax, 2), %esi - leal STRING_DATA_OFFSET(%r11d, %ecx, 2), %edi + leal MIRROR_CHAR_ARRAY_DATA_OFFSET(%r10d, %eax, 2), %esi + leal MIRROR_CHAR_ARRAY_DATA_OFFSET(%r11d, %ecx, 2), %edi /* Calculate min length and count diff */ movl %r8d, %ecx movl %r8d, %eax @@ -1605,5 +1573,3 @@ DEFINE_FUNCTION art_nested_signal_return call PLT_SYMBOL(longjmp) int3 // won't get here END_FUNCTION art_nested_signal_return - - diff --git a/runtime/arch/x86_64/thread_x86_64.cc b/runtime/arch/x86_64/thread_x86_64.cc index 6dff2b4a55..553b6569c8 100644 --- a/runtime/arch/x86_64/thread_x86_64.cc +++ b/runtime/arch/x86_64/thread_x86_64.cc @@ -49,29 +49,16 @@ void Thread::InitCpu() { // Sanity check that reads from %gs point to this Thread*. Thread* self_check; - CHECK_EQ(THREAD_SELF_OFFSET, SelfOffset<8>().Int32Value()); __asm__ __volatile__("movq %%gs:(%1), %0" : "=r"(self_check) // output : "r"(THREAD_SELF_OFFSET) // input :); // clobber CHECK_EQ(self_check, this); - - // Sanity check other offsets. - CHECK_EQ(static_cast<size_t>(RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET), - Runtime::GetCalleeSaveMethodOffset(Runtime::kSaveAll)); - CHECK_EQ(static_cast<size_t>(RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET), - Runtime::GetCalleeSaveMethodOffset(Runtime::kRefsOnly)); - CHECK_EQ(static_cast<size_t>(RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET), - Runtime::GetCalleeSaveMethodOffset(Runtime::kRefsAndArgs)); - CHECK_EQ(THREAD_EXCEPTION_OFFSET, ExceptionOffset<8>().Int32Value()); - CHECK_EQ(THREAD_CARD_TABLE_OFFSET, CardTableOffset<8>().Int32Value()); - CHECK_EQ(THREAD_ID_OFFSET, ThinLockIdOffset<8>().Int32Value()); } void Thread::CleanupCpu() { // Sanity check that reads from %gs point to this Thread*. Thread* self_check; - CHECK_EQ(THREAD_SELF_OFFSET, SelfOffset<8>().Int32Value()); __asm__ __volatile__("movq %%gs:(%1), %0" : "=r"(self_check) // output : "r"(THREAD_SELF_OFFSET) // input diff --git a/runtime/asm_support.h b/runtime/asm_support.h index 62f359346e..26df045a25 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -17,56 +17,147 @@ #ifndef ART_RUNTIME_ASM_SUPPORT_H_ #define ART_RUNTIME_ASM_SUPPORT_H_ +#if defined(__cplusplus) +#include "mirror/art_method.h" +#include "mirror/class.h" +#include "mirror/string.h" +#include "runtime.h" +#include "thread.h" +#endif + #include "read_barrier_c.h" -// Value loaded into rSUSPEND for quick. When this value is counted down to zero we do a suspend -// check. -#define SUSPEND_CHECK_INTERVAL (1000) +#if defined(__arm__) || defined(__aarch64__) || defined(__mips__) +// In quick code for ARM, ARM64 and MIPS we make poor use of registers and perform frequent suspend +// checks in the event of loop back edges. The SUSPEND_CHECK_INTERVAL constant is loaded into a +// register at the point of an up-call or after handling a suspend check. It reduces the number of +// loads of the TLS suspend check value by the given amount (turning it into a decrement and compare +// of a register). This increases the time for a thread to respond to requests from GC and the +// debugger, damaging GC performance and creating other unwanted artifacts. For example, this count +// has the effect of making loops and Java code look cold in profilers, where the count is reset +// impacts where samples will occur. Reducing the count as much as possible improves profiler +// accuracy in tools like traceview. +// TODO: get a compiler that can do a proper job of loop optimization and remove this. +#define SUSPEND_CHECK_INTERVAL 1000 +#endif -// Offsets within java.lang.Object. -#define CLASS_OFFSET 0 -#define LOCK_WORD_OFFSET 4 +#if defined(__cplusplus) -#ifndef USE_BAKER_OR_BROOKS_READ_BARRIER +#ifndef ADD_TEST_EQ // Allow #include-r to replace with their own. +#define ADD_TEST_EQ(x, y) CHECK_EQ(x, y); +#endif -// Offsets within java.lang.Class. -#define CLASS_COMPONENT_TYPE_OFFSET 12 +static inline void CheckAsmSupportOffsetsAndSizes() { +#else +#define ADD_TEST_EQ(x, y) +#endif -// Array offsets. -#define ARRAY_LENGTH_OFFSET 8 -#define OBJECT_ARRAY_DATA_OFFSET 12 +// Size of references to the heap on the stack. +#define STACK_REFERENCE_SIZE 4 +ADD_TEST_EQ(static_cast<size_t>(STACK_REFERENCE_SIZE), sizeof(art::StackReference<art::mirror::Object>)) + +// Note: these callee save methods loads require read barriers. +// Offset of field Runtime::callee_save_methods_[kSaveAll] +#define RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET 0 +ADD_TEST_EQ(static_cast<size_t>(RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET), + art::Runtime::GetCalleeSaveMethodOffset(art::Runtime::kSaveAll)) + +// Offset of field Runtime::callee_save_methods_[kRefsOnly] +#define RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET __SIZEOF_POINTER__ +ADD_TEST_EQ(static_cast<size_t>(RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET), + art::Runtime::GetCalleeSaveMethodOffset(art::Runtime::kRefsOnly)) + +// Offset of field Runtime::callee_save_methods_[kRefsAndArgs] +#define RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET (2 * __SIZEOF_POINTER__) +ADD_TEST_EQ(static_cast<size_t>(RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET), + art::Runtime::GetCalleeSaveMethodOffset(art::Runtime::kRefsAndArgs)) + +// Offset of field Thread::tls32_.state_and_flags. +#define THREAD_FLAGS_OFFSET 0 +ADD_TEST_EQ(THREAD_FLAGS_OFFSET, + art::Thread::ThreadFlagsOffset<__SIZEOF_POINTER__>().Int32Value()) + +// Offset of field Thread::tls32_.thin_lock_thread_id. +#define THREAD_ID_OFFSET 12 +ADD_TEST_EQ(THREAD_ID_OFFSET, + art::Thread::ThinLockIdOffset<__SIZEOF_POINTER__>().Int32Value()) + +// Offset of field Thread::tlsPtr_.card_table. +#define THREAD_CARD_TABLE_OFFSET 120 +ADD_TEST_EQ(THREAD_CARD_TABLE_OFFSET, + art::Thread::CardTableOffset<__SIZEOF_POINTER__>().Int32Value()) + +// Offset of field Thread::tlsPtr_.exception. +#define THREAD_EXCEPTION_OFFSET (THREAD_CARD_TABLE_OFFSET + __SIZEOF_POINTER__) +ADD_TEST_EQ(THREAD_EXCEPTION_OFFSET, + art::Thread::ExceptionOffset<__SIZEOF_POINTER__>().Int32Value()) + +// Offset of field Thread::tlsPtr_.managed_stack.top_quick_frame_. +#define THREAD_TOP_QUICK_FRAME_OFFSET (THREAD_CARD_TABLE_OFFSET + (3 * __SIZEOF_POINTER__)) +ADD_TEST_EQ(THREAD_TOP_QUICK_FRAME_OFFSET, + art::Thread::TopOfManagedStackOffset<__SIZEOF_POINTER__>().Int32Value()) + +// Offset of field Thread::tlsPtr_.managed_stack.top_quick_frame_. +#define THREAD_SELF_OFFSET (THREAD_CARD_TABLE_OFFSET + (8 * __SIZEOF_POINTER__)) +ADD_TEST_EQ(THREAD_SELF_OFFSET, + art::Thread::SelfOffset<__SIZEOF_POINTER__>().Int32Value()) -// Offsets within java.lang.String. -#define STRING_VALUE_OFFSET 8 -#define STRING_COUNT_OFFSET 12 -#define STRING_OFFSET_OFFSET 20 -#define STRING_DATA_OFFSET 12 - -// Offsets within java.lang.Method. -#define METHOD_DEX_CACHE_METHODS_OFFSET 12 -#define METHOD_PORTABLE_CODE_OFFSET 40 -#define METHOD_QUICK_CODE_OFFSET 48 +// Offsets within java.lang.Object. +#define MIRROR_OBJECT_CLASS_OFFSET 0 +ADD_TEST_EQ(MIRROR_OBJECT_CLASS_OFFSET, art::mirror::Object::ClassOffset().Int32Value()) +#define MIRROR_OBJECT_LOCK_WORD_OFFSET 4 +ADD_TEST_EQ(MIRROR_OBJECT_LOCK_WORD_OFFSET, art::mirror::Object::MonitorOffset().Int32Value()) +#if defined(USE_BAKER_OR_BROOKS_READ_BARRIER) +#define MIRROR_OBJECT_HEADER_SIZE 16 #else +#define MIRROR_OBJECT_HEADER_SIZE 8 +#endif +ADD_TEST_EQ(size_t(MIRROR_OBJECT_HEADER_SIZE), sizeof(art::mirror::Object)) // Offsets within java.lang.Class. -#define CLASS_COMPONENT_TYPE_OFFSET 20 +#define MIRROR_CLASS_COMPONENT_TYPE_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE) +ADD_TEST_EQ(MIRROR_CLASS_COMPONENT_TYPE_OFFSET, + art::mirror::Class::ComponentTypeOffset().Int32Value()) // Array offsets. -#define ARRAY_LENGTH_OFFSET 16 -#define OBJECT_ARRAY_DATA_OFFSET 20 +#define MIRROR_ARRAY_LENGTH_OFFSET MIRROR_OBJECT_HEADER_SIZE +ADD_TEST_EQ(MIRROR_ARRAY_LENGTH_OFFSET, art::mirror::Array::LengthOffset().Int32Value()) + +#define MIRROR_CHAR_ARRAY_DATA_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE) +ADD_TEST_EQ(MIRROR_CHAR_ARRAY_DATA_OFFSET, + art::mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value()) + +#define MIRROR_OBJECT_ARRAY_DATA_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE) +ADD_TEST_EQ(MIRROR_OBJECT_ARRAY_DATA_OFFSET, + art::mirror::Array::DataOffset( + sizeof(art::mirror::HeapReference<art::mirror::Object>)).Int32Value()) // Offsets within java.lang.String. -#define STRING_VALUE_OFFSET 16 -#define STRING_COUNT_OFFSET 20 -#define STRING_OFFSET_OFFSET 28 -#define STRING_DATA_OFFSET 20 +#define MIRROR_STRING_VALUE_OFFSET MIRROR_OBJECT_HEADER_SIZE +ADD_TEST_EQ(MIRROR_STRING_VALUE_OFFSET, art::mirror::String::ValueOffset().Int32Value()) + +#define MIRROR_STRING_COUNT_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE) +ADD_TEST_EQ(MIRROR_STRING_COUNT_OFFSET, art::mirror::String::CountOffset().Int32Value()) + +#define MIRROR_STRING_OFFSET_OFFSET (12 + MIRROR_OBJECT_HEADER_SIZE) +ADD_TEST_EQ(MIRROR_STRING_OFFSET_OFFSET, art::mirror::String::OffsetOffset().Int32Value()) + +// Offsets within java.lang.reflect.ArtMethod. +#define MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE) +ADD_TEST_EQ(MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET, + art::mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()) + +#define MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET (32 + MIRROR_OBJECT_HEADER_SIZE) +ADD_TEST_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET, + art::mirror::ArtMethod::EntryPointFromPortableCompiledCodeOffset().Int32Value()) -// Offsets within java.lang.Method. -#define METHOD_DEX_CACHE_METHODS_OFFSET 20 -#define METHOD_PORTABLE_CODE_OFFSET 48 -#define METHOD_QUICK_CODE_OFFSET 56 +#define MIRROR_ART_METHOD_QUICK_CODE_OFFSET (40 + MIRROR_OBJECT_HEADER_SIZE) +ADD_TEST_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET, + art::mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()) +#if defined(__cplusplus) +} // End of CheckAsmSupportOffsets. #endif #endif // ART_RUNTIME_ASM_SUPPORT_H_ diff --git a/runtime/base/allocator.cc b/runtime/base/allocator.cc index f67616ef0d..994e2357af 100644 --- a/runtime/base/allocator.cc +++ b/runtime/base/allocator.cc @@ -74,6 +74,7 @@ Allocator* Allocator::GetNoopAllocator() { namespace TrackedAllocators { +// These globals are safe since they don't have any non-trivial destructors. Atomic<size_t> g_bytes_used[kAllocatorTagCount]; volatile size_t g_max_bytes_used[kAllocatorTagCount]; Atomic<uint64_t> g_total_bytes_used[kAllocatorTagCount]; diff --git a/runtime/base/bit_vector.cc b/runtime/base/bit_vector.cc index 3d2f0deac5..5b8b6e2cde 100644 --- a/runtime/base/bit_vector.cc +++ b/runtime/base/bit_vector.cc @@ -16,6 +16,8 @@ #include "bit_vector.h" +#include <sstream> + #include "allocator.h" #include "bit_vector-inl.h" @@ -145,10 +147,7 @@ bool BitVector::UnionIfNotIn(const BitVector* union_with, const BitVector* not_i // Is the storage size smaller than src's? if (storage_size_ < union_with_size) { - changed = true; - - // Set it to reallocate. - SetBit(highest_bit); + EnsureSize(highest_bit); // Paranoid: storage size should be big enough to hold this bit now. DCHECK_LT(static_cast<uint32_t> (highest_bit), storage_size_ * kWordBits); diff --git a/runtime/base/bit_vector_test.cc b/runtime/base/bit_vector_test.cc index df5d79d893..31fd0e7f3e 100644 --- a/runtime/base/bit_vector_test.cc +++ b/runtime/base/bit_vector_test.cc @@ -141,4 +141,30 @@ TEST(BitVector, SetInitialBits) { EXPECT_EQ(64u, bv.NumSetBits()); } +TEST(BitVector, UnionIfNotIn) { + { + BitVector first(2, true, Allocator::GetMallocAllocator()); + BitVector second(5, true, Allocator::GetMallocAllocator()); + BitVector third(5, true, Allocator::GetMallocAllocator()); + + second.SetBit(64); + third.SetBit(64); + bool changed = first.UnionIfNotIn(&second, &third); + EXPECT_EQ(0u, first.NumSetBits()); + EXPECT_FALSE(changed); + } + + { + BitVector first(2, true, Allocator::GetMallocAllocator()); + BitVector second(5, true, Allocator::GetMallocAllocator()); + BitVector third(5, true, Allocator::GetMallocAllocator()); + + second.SetBit(64); + bool changed = first.UnionIfNotIn(&second, &third); + EXPECT_EQ(1u, first.NumSetBits()); + EXPECT_TRUE(changed); + EXPECT_TRUE(first.IsBitSet(64)); + } +} + } // namespace art diff --git a/runtime/base/casts.h b/runtime/base/casts.h index be94c2eb78..138c2fda80 100644 --- a/runtime/base/casts.h +++ b/runtime/base/casts.h @@ -19,6 +19,8 @@ #include <assert.h> #include <string.h> +#include <type_traits> + #include "base/macros.h" namespace art { @@ -65,16 +67,9 @@ inline To implicit_cast(From const &f) { template<typename To, typename From> // use like this: down_cast<T*>(foo); inline To down_cast(From* f) { // so we only accept pointers - // Ensures that To is a sub-type of From *. This test is here only - // for compile-time type checking, and has no overhead in an - // optimized build at run-time, as it will be optimized away - // completely. - if (false) { - implicit_cast<From*, To>(0); - } + static_assert(std::is_base_of<From, typename std::remove_pointer<To>::type>::value, + "down_cast unsafe as To is not a subtype of From"); - // - // assert(f == NULL || dynamic_cast<To>(f) != NULL); // RTTI: debug mode only! return static_cast<To>(f); } diff --git a/runtime/base/dumpable.h b/runtime/base/dumpable.h new file mode 100644 index 0000000000..3c316cc015 --- /dev/null +++ b/runtime/base/dumpable.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_BASE_DUMPABLE_H_ +#define ART_RUNTIME_BASE_DUMPABLE_H_ + +#include "base/macros.h" + +namespace art { + +// A convenience to allow any class with a "Dump(std::ostream& os)" member function +// but without an operator<< to be used as if it had an operator<<. Use like this: +// +// os << Dumpable<MyType>(my_type_instance); +// +template<typename T> +class Dumpable FINAL { + public: + explicit Dumpable(const T& value) : value_(value) { + } + + void Dump(std::ostream& os) const { + value_.Dump(os); + } + + private: + const T& value_; + + DISALLOW_COPY_AND_ASSIGN(Dumpable); +}; + +template<typename T> +std::ostream& operator<<(std::ostream& os, const Dumpable<T>& rhs) { + rhs.Dump(os); + return os; +} + +} // namespace art + +#endif // ART_RUNTIME_BASE_DUMPABLE_H_ diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc index b2ad1d06d6..46c3538ca3 100644 --- a/runtime/base/logging.cc +++ b/runtime/base/logging.cc @@ -16,17 +16,25 @@ #include "logging.h" +#include <sstream> + #include "base/mutex.h" #include "runtime.h" #include "thread-inl.h" #include "utils.h" +// Headers for LogMessage::LogLine. +#ifdef HAVE_ANDROID_OS +#include "cutils/log.h" +#else +#include <sys/types.h> +#include <unistd.h> +#endif + namespace art { LogVerbosity gLogVerbosity; -std::vector<std::string> gVerboseMethods; - unsigned int gAborting = 0; static LogSeverity gMinimumLogSeverity = INFO; @@ -47,14 +55,6 @@ const char* ProgramInvocationShortName() { : "art"; } -// Configure logging based on ANDROID_LOG_TAGS environment variable. -// We need to parse a string that looks like -// -// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i -// -// The tag (or '*' for the global level) comes first, followed by a colon -// and a letter indicating the minimum priority level we're expected to log. -// This can be used to reveal or conceal logs with specific tags. void InitLogging(char* argv[]) { if (gCmdLine.get() != nullptr) { return; @@ -65,27 +65,27 @@ void InitLogging(char* argv[]) { // Stash the command line for later use. We can use /proc/self/cmdline on Linux to recover this, // but we don't have that luxury on the Mac, and there are a couple of argv[0] variants that are // commonly used. - if (argv != NULL) { + if (argv != nullptr) { gCmdLine.reset(new std::string(argv[0])); - for (size_t i = 1; argv[i] != NULL; ++i) { + for (size_t i = 1; argv[i] != nullptr; ++i) { gCmdLine->append(" "); gCmdLine->append(argv[i]); } gProgramInvocationName.reset(new std::string(argv[0])); const char* last_slash = strrchr(argv[0], '/'); - gProgramInvocationShortName.reset(new std::string((last_slash != NULL) ? last_slash + 1 + gProgramInvocationShortName.reset(new std::string((last_slash != nullptr) ? last_slash + 1 : argv[0])); } else { - // TODO: fall back to /proc/self/cmdline when argv is NULL on Linux + // TODO: fall back to /proc/self/cmdline when argv is NULL on Linux. gCmdLine.reset(new std::string("<unset>")); } const char* tags = getenv("ANDROID_LOG_TAGS"); - if (tags == NULL) { + if (tags == nullptr) { return; } std::vector<std::string> specs; - Split(tags, ' ', specs); + Split(tags, ' ', &specs); for (size_t i = 0; i < specs.size(); ++i) { // "tag-pattern:[vdiwefs]" std::string spec(specs[i]); @@ -119,47 +119,121 @@ void InitLogging(char* argv[]) { } } -LogMessageData::LogMessageData(const char* file, int line, LogSeverity severity, int error) - : file(file), - line_number(line), - severity(severity), - error(error) { - const char* last_slash = strrchr(file, '/'); - file = (last_slash == NULL) ? file : last_slash + 1; -} +// This indirection greatly reduces the stack impact of having +// lots of checks/logging in a function. +class LogMessageData { + public: + LogMessageData(const char* file, unsigned int line, LogSeverity severity, int error) + : file_(file), + line_number_(line), + severity_(severity), + error_(error) { + const char* last_slash = strrchr(file, '/'); + file = (last_slash == nullptr) ? file : last_slash + 1; + } + + const char * GetFile() const { + return file_; + } + unsigned int GetLineNumber() const { + return line_number_; + } + + LogSeverity GetSeverity() const { + return severity_; + } + + int GetError() const { + return error_; + } + + std::ostream& GetBuffer() { + return buffer_; + } + + std::string ToString() const { + return buffer_.str(); + } + + private: + std::ostringstream buffer_; + const char* const file_; + const unsigned int line_number_; + const LogSeverity severity_; + const int error_; + + DISALLOW_COPY_AND_ASSIGN(LogMessageData); +}; + + +LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, int error) + : data_(new LogMessageData(file, line, severity, error)) { +} LogMessage::~LogMessage() { - if (data_->severity < gMinimumLogSeverity) { + if (data_->GetSeverity() < gMinimumLogSeverity) { return; // No need to format something we're not going to output. } // Finish constructing the message. - if (data_->error != -1) { - data_->buffer << ": " << strerror(data_->error); + if (data_->GetError() != -1) { + data_->GetBuffer() << ": " << strerror(data_->GetError()); } - std::string msg(data_->buffer.str()); + std::string msg(data_->ToString()); // Do the actual logging with the lock held. { MutexLock mu(Thread::Current(), *Locks::logging_lock_); if (msg.find('\n') == std::string::npos) { - LogLine(*data_, msg.c_str()); + LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), msg.c_str()); } else { msg += '\n'; size_t i = 0; while (i < msg.size()) { size_t nl = msg.find('\n', i); msg[nl] = '\0'; - LogLine(*data_, &msg[i]); + LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), &msg[i]); i = nl + 1; } } } // Abort if necessary. - if (data_->severity == FATAL) { + if (data_->GetSeverity() == FATAL) { Runtime::Abort(); } } +std::ostream& LogMessage::stream() { + return data_->GetBuffer(); +} + +#ifdef HAVE_ANDROID_OS +static const android_LogPriority kLogSeverityToAndroidLogPriority[] = { + ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN, + ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, ANDROID_LOG_FATAL +}; +COMPILE_ASSERT(arraysize(kLogSeverityToAndroidLogPriority) == INTERNAL_FATAL + 1, + mismatch_in_size_of_kLogSeverityToAndroidLogPriority_and_values_in_LogSeverity); +#endif + +void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity log_severity, + const char* message) { +#ifdef HAVE_ANDROID_OS + const char* tag = ProgramInvocationShortName(); + int priority = kLogSeverityToAndroidLogPriority[log_severity]; + if (priority == ANDROID_LOG_FATAL) { + LOG_PRI(priority, tag, "%s:%u] %s", file, line, message); + } else { + LOG_PRI(priority, tag, "%s", message); + } +#else + static const char* log_characters = "VDIWEFF"; + CHECK_EQ(strlen(log_characters), INTERNAL_FATAL + 1U); + char severity = log_characters[log_severity]; + fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", + ProgramInvocationShortName(), severity, getpid(), ::art::GetTid(), file, line, message); +#endif +} + } // namespace art diff --git a/runtime/base/logging.h b/runtime/base/logging.h index cf3e763f1e..baa83e35af 100644 --- a/runtime/base/logging.h +++ b/runtime/base/logging.h @@ -17,29 +17,113 @@ #ifndef ART_RUNTIME_BASE_LOGGING_H_ #define ART_RUNTIME_BASE_LOGGING_H_ -#include <cerrno> -#include <cstring> -#include <iostream> // NOLINT #include <memory> -#include <sstream> -#include <signal.h> -#include <vector> +#include <ostream> #include "base/macros.h" -#include "log_severity.h" +namespace art { + +enum LogSeverity { + VERBOSE, + DEBUG, + INFO, + WARNING, + ERROR, + FATAL, + INTERNAL_FATAL, // For Runtime::Abort. +}; + +// The members of this struct are the valid arguments to VLOG and VLOG_IS_ON in code, +// and the "-verbose:" command line argument. +struct LogVerbosity { + bool class_linker; // Enabled with "-verbose:class". + bool compiler; + bool gc; + bool heap; + bool jdwp; + bool jni; + bool monitor; + bool profiler; + bool signals; + bool startup; + bool third_party_jni; // Enabled with "-verbose:third-party-jni". + bool threads; + bool verifier; +}; + +// Global log verbosity setting, initialized by InitLogging. +extern LogVerbosity gLogVerbosity; + +// 0 if not abort, non-zero if an abort is in progress. Used on fatal exit to prevents recursive +// aborts. Global declaration allows us to disable some error checking to ensure fatal shutdown +// makes forward progress. +extern unsigned int gAborting; + +// Configure logging based on ANDROID_LOG_TAGS environment variable. +// We need to parse a string that looks like +// +// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i +// +// The tag (or '*' for the global level) comes first, followed by a colon +// and a letter indicating the minimum priority level we're expected to log. +// This can be used to reveal or conceal logs with specific tags. +extern void InitLogging(char* argv[]); + +// Returns the command line used to invoke the current tool or nullptr if InitLogging hasn't been +// performed. +extern const char* GetCmdLine(); + +// The command used to start the ART runtime, such as "/system/bin/dalvikvm". If InitLogging hasn't +// been performed then just returns "art" +extern const char* ProgramInvocationName(); + +// A short version of the command used to start the ART runtime, such as "dalvikvm". If InitLogging +// hasn't been performed then just returns "art" +extern const char* ProgramInvocationShortName(); + +// Logs a message to logcat on Android otherwise to stderr. If the severity is FATAL it also causes +// an abort. For example: LOG(FATAL) << "We didn't expect to reach here"; +#define LOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity, -1).stream() + +// A variant of LOG that also logs the current errno value. To be used when library calls fail. +#define PLOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity, errno).stream() + +// Marker that code is yet to be implemented. +#define UNIMPLEMENTED(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented " + +// Is verbose logging enabled for the given module? Where the module is defined in LogVerbosity. +#define VLOG_IS_ON(module) UNLIKELY(::art::gLogVerbosity.module) + +// Variant of LOG that logs when verbose logging is enabled for a module. For example, +// VLOG(jni) << "A JNI operation was performed"; +#define VLOG(module) \ + if (VLOG_IS_ON(module)) \ + ::art::LogMessage(__FILE__, __LINE__, INFO, -1).stream() + +// Return the stream associated with logging for the given module. +#define VLOG_STREAM(module) ::art::LogMessage(__FILE__, __LINE__, INFO, -1).stream() + +// Check whether condition x holds and LOG(FATAL) if not. The value of the expression x is only +// evaluated once. Extra logging can be appended using << after. For example, +// CHECK(false == true) results in a log message of "Check failed: false == true". #define CHECK(x) \ if (UNLIKELY(!(x))) \ - ::art::LogMessage(__FILE__, __LINE__, FATAL, -1).stream() \ + ::art::LogMessage(__FILE__, __LINE__, ::art::FATAL, -1).stream() \ << "Check failed: " #x << " " +// Helper for CHECK_xx(x,y) macros. #define CHECK_OP(LHS, RHS, OP) \ for (auto _values = ::art::MakeEagerEvaluator(LHS, RHS); \ UNLIKELY(!(_values.lhs OP _values.rhs)); /* empty */) \ - ::art::LogMessage(__FILE__, __LINE__, FATAL, -1).stream() \ + ::art::LogMessage(__FILE__, __LINE__, ::art::FATAL, -1).stream() \ << "Check failed: " << #LHS << " " << #OP << " " << #RHS \ << " (" #LHS "=" << _values.lhs << ", " #RHS "=" << _values.rhs << ") " + +// Check whether a condition holds between x and y, LOG(FATAL) if not. The value of the expressions +// x and y is evaluated once. Extra logging can be appended using << after. For example, +// CHECK_NE(0 == 1, false) results in "Check failed: false != false (0==1=false, false=false) ". #define CHECK_EQ(x, y) CHECK_OP(x, y, ==) #define CHECK_NE(x, y) CHECK_OP(x, y, !=) #define CHECK_LE(x, y) CHECK_OP(x, y, <=) @@ -47,22 +131,25 @@ #define CHECK_GE(x, y) CHECK_OP(x, y, >=) #define CHECK_GT(x, y) CHECK_OP(x, y, >) +// Helper for CHECK_STRxx(s1,s2) macros. #define CHECK_STROP(s1, s2, sense) \ if (UNLIKELY((strcmp(s1, s2) == 0) != sense)) \ - LOG(FATAL) << "Check failed: " \ - << "\"" << s1 << "\"" \ - << (sense ? " == " : " != ") \ - << "\"" << s2 << "\"" + LOG(::art::FATAL) << "Check failed: " \ + << "\"" << s1 << "\"" \ + << (sense ? " == " : " != ") \ + << "\"" << s2 << "\"" +// Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not. #define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true) #define CHECK_STRNE(s1, s2) CHECK_STROP(s1, s2, false) +// Perform the pthread function call(args), LOG(FATAL) on error. #define CHECK_PTHREAD_CALL(call, args, what) \ do { \ int rc = call args; \ if (rc != 0) { \ errno = rc; \ - PLOG(FATAL) << # call << " failed for " << what; \ + PLOG(::art::FATAL) << # call << " failed for " << what; \ } \ } while (false) @@ -74,81 +161,34 @@ // n / 2; // } #define CHECK_CONSTEXPR(x, out, dummy) \ - (UNLIKELY(!(x))) ? (LOG(FATAL) << "Check failed: " << #x out, dummy) : - -#ifndef NDEBUG - -#define DCHECK(x) CHECK(x) -#define DCHECK_EQ(x, y) CHECK_EQ(x, y) -#define DCHECK_NE(x, y) CHECK_NE(x, y) -#define DCHECK_LE(x, y) CHECK_LE(x, y) -#define DCHECK_LT(x, y) CHECK_LT(x, y) -#define DCHECK_GE(x, y) CHECK_GE(x, y) -#define DCHECK_GT(x, y) CHECK_GT(x, y) -#define DCHECK_STREQ(s1, s2) CHECK_STREQ(s1, s2) -#define DCHECK_STRNE(s1, s2) CHECK_STRNE(s1, s2) -#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy) - -#else // NDEBUG - -#define DCHECK(condition) \ - while (false) \ - CHECK(condition) - -#define DCHECK_EQ(val1, val2) \ - while (false) \ - CHECK_EQ(val1, val2) - -#define DCHECK_NE(val1, val2) \ - while (false) \ - CHECK_NE(val1, val2) - -#define DCHECK_LE(val1, val2) \ - while (false) \ - CHECK_LE(val1, val2) - -#define DCHECK_LT(val1, val2) \ - while (false) \ - CHECK_LT(val1, val2) - -#define DCHECK_GE(val1, val2) \ - while (false) \ - CHECK_GE(val1, val2) - -#define DCHECK_GT(val1, val2) \ - while (false) \ - CHECK_GT(val1, val2) - -#define DCHECK_STREQ(str1, str2) \ - while (false) \ - CHECK_STREQ(str1, str2) + (UNLIKELY(!(x))) ? (LOG(::art::FATAL) << "Check failed: " << #x out, dummy) : -#define DCHECK_STRNE(str1, str2) \ - while (false) \ - CHECK_STRNE(str1, str2) - -#define DCHECK_CONSTEXPR(x, out, dummy) \ - (false && (x)) ? (dummy) : +// DCHECKs are debug variants of CHECKs only enabled in debug builds. Generally CHECK should be +// used unless profiling identifies a CHECK as being in performance critical code. +#if defined(NDEBUG) +static constexpr bool kEnableDChecks = false; +#else +static constexpr bool kEnableDChecks = true; #endif -#define LOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity, -1).stream() -#define PLOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity, errno).stream() - -#define LG LOG(INFO) - -#define UNIMPLEMENTED(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented " - -#define VLOG_IS_ON(module) UNLIKELY(::art::gLogVerbosity.module) -#define VLOG(module) if (VLOG_IS_ON(module)) ::art::LogMessage(__FILE__, __LINE__, INFO, -1).stream() -#define VLOG_STREAM(module) ::art::LogMessage(__FILE__, __LINE__, INFO, -1).stream() - -// -// Implementation details beyond this point. -// - -namespace art { +#define DCHECK(x) if (::art::kEnableDChecks) CHECK(x) +#define DCHECK_EQ(x, y) if (::art::kEnableDChecks) CHECK_EQ(x, y) +#define DCHECK_NE(x, y) if (::art::kEnableDChecks) CHECK_NE(x, y) +#define DCHECK_LE(x, y) if (::art::kEnableDChecks) CHECK_LE(x, y) +#define DCHECK_LT(x, y) if (::art::kEnableDChecks) CHECK_LT(x, y) +#define DCHECK_GE(x, y) if (::art::kEnableDChecks) CHECK_GE(x, y) +#define DCHECK_GT(x, y) if (::art::kEnableDChecks) CHECK_GT(x, y) +#define DCHECK_STREQ(s1, s2) if (::art::kEnableDChecks) CHECK_STREQ(s1, s2) +#define DCHECK_STRNE(s1, s2) if (::art::kEnableDChecks) CHECK_STRNE(s1, s2) +#if defined(NDEBUG) +#define DCHECK_CONSTEXPR(x, out, dummy) +#else +#define DCHECK_CONSTEXPR(x, out, dummy) CHECK_CONSTEXPR(x, out, dummy) +#endif +// Temporary class created to evaluate the LHS and RHS, used with MakeEagerEvaluator to infer the +// types of LHS and RHS. template <typename LHS, typename RHS> struct EagerEvaluator { EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) { } @@ -156,10 +196,14 @@ struct EagerEvaluator { RHS rhs; }; -// We want char*s to be treated as pointers, not strings. If you want them treated like strings, -// you'd need to use CHECK_STREQ and CHECK_STRNE anyway to compare the characters rather than their -// addresses. We could express this more succinctly with std::remove_const, but this is quick and -// easy to understand, and works before we have C++0x. We rely on signed/unsigned warnings to +// Helper function for CHECK_xx. +template <typename LHS, typename RHS> +static inline EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) { + return EagerEvaluator<LHS, RHS>(lhs, rhs); +} + +// Explicitly instantiate EagerEvalue for pointers so that char*s aren't treated as strings. To +// compare strings use CHECK_STREQ and CHECK_STRNE. We rely on signed/unsigned warnings to // protect you against combinations not explicitly listed below. #define EAGER_PTR_EVALUATOR(T1, T2) \ template <> struct EagerEvaluator<T1, T2> { \ @@ -182,153 +226,30 @@ EAGER_PTR_EVALUATOR(const signed char*, signed char*); EAGER_PTR_EVALUATOR(signed char*, const signed char*); EAGER_PTR_EVALUATOR(signed char*, signed char*); -template <typename LHS, typename RHS> -EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) { - return EagerEvaluator<LHS, RHS>(lhs, rhs); -} - -// This indirection greatly reduces the stack impact of having -// lots of checks/logging in a function. -struct LogMessageData { - public: - LogMessageData(const char* file, int line, LogSeverity severity, int error); - std::ostringstream buffer; - const char* const file; - const int line_number; - const LogSeverity severity; - const int error; - - private: - DISALLOW_COPY_AND_ASSIGN(LogMessageData); -}; +// Data for the log message, not stored in LogMessage to avoid increasing the stack size. +class LogMessageData; +// A LogMessage is a temporarily scoped object used by LOG and the unlikely part of a CHECK. The +// destructor will abort if the severity is FATAL. class LogMessage { public: - LogMessage(const char* file, int line, LogSeverity severity, int error) - : data_(new LogMessageData(file, line, severity, error)) { - } + LogMessage(const char* file, unsigned int line, LogSeverity severity, int error); ~LogMessage(); // TODO: enable LOCKS_EXCLUDED(Locks::logging_lock_). - std::ostream& stream() { - return data_->buffer; - } + // Returns the stream associated with the message, the LogMessage performs output when it goes + // out of scope. + std::ostream& stream(); - private: - static void LogLine(const LogMessageData& data, const char*); + // The routine that performs the actual logging. + static void LogLine(const char* file, unsigned int line, LogSeverity severity, const char* msg); + private: const std::unique_ptr<LogMessageData> data_; - friend void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context); - friend class Mutex; DISALLOW_COPY_AND_ASSIGN(LogMessage); }; -// A convenience to allow any class with a "Dump(std::ostream& os)" member function -// but without an operator<< to be used as if it had an operator<<. Use like this: -// -// os << Dumpable<MyType>(my_type_instance); -// -template<typename T> -class Dumpable { - public: - explicit Dumpable(T& value) : value_(value) { - } - - void Dump(std::ostream& os) const { - value_.Dump(os); - } - - private: - T& value_; - - DISALLOW_COPY_AND_ASSIGN(Dumpable); -}; - -template<typename T> -std::ostream& operator<<(std::ostream& os, const Dumpable<T>& rhs) { - rhs.Dump(os); - return os; -} - -template<typename T> -class ConstDumpable { - public: - explicit ConstDumpable(const T& value) : value_(value) { - } - - void Dump(std::ostream& os) const { - value_.Dump(os); - } - - private: - const T& value_; - - DISALLOW_COPY_AND_ASSIGN(ConstDumpable); -}; - -template<typename T> -std::ostream& operator<<(std::ostream& os, const ConstDumpable<T>& rhs) { - rhs.Dump(os); - return os; -} - -// Helps you use operator<< in a const char*-like context such as our various 'F' methods with -// format strings. -template<typename T> -class ToStr { - public: - explicit ToStr(const T& value) { - std::ostringstream os; - os << value; - s_ = os.str(); - } - - const char* c_str() const { - return s_.c_str(); - } - - const std::string& str() const { - return s_; - } - - private: - std::string s_; - DISALLOW_COPY_AND_ASSIGN(ToStr); -}; - -// The members of this struct are the valid arguments to VLOG and VLOG_IS_ON in code, -// and the "-verbose:" command line argument. -struct LogVerbosity { - bool class_linker; // Enabled with "-verbose:class". - bool compiler; - bool gc; - bool heap; - bool jdwp; - bool jni; - bool monitor; - bool profiler; - bool signals; - bool startup; - bool third_party_jni; // Enabled with "-verbose:third-party-jni". - bool threads; - bool verifier; -}; - -extern LogVerbosity gLogVerbosity; - -extern std::vector<std::string> gVerboseMethods; - -// Used on fatal exit. Prevents recursive aborts. Allows us to disable -// some error checking to ensure fatal shutdown makes forward progress. -extern unsigned int gAborting; - -extern void InitLogging(char* argv[]); - -extern const char* GetCmdLine(); -extern const char* ProgramInvocationName(); -extern const char* ProgramInvocationShortName(); - } // namespace art #endif // ART_RUNTIME_BASE_LOGGING_H_ diff --git a/runtime/base/logging_android.cc b/runtime/base/logging_android.cc deleted file mode 100644 index 9b1ac58461..0000000000 --- a/runtime/base/logging_android.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "logging.h" - -#include <unistd.h> - -#include <iostream> - -#include "base/stringprintf.h" -#include "cutils/log.h" - -namespace art { - -static const int kLogSeverityToAndroidLogPriority[] = { - ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN, - ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, ANDROID_LOG_FATAL -}; - -void LogMessage::LogLine(const LogMessageData& data, const char* message) { - const char* tag = ProgramInvocationShortName(); - int priority = kLogSeverityToAndroidLogPriority[data.severity]; - if (priority == ANDROID_LOG_FATAL) { - LOG_PRI(priority, tag, "%s:%d] %s", data.file, data.line_number, message); - } else { - LOG_PRI(priority, tag, "%s", message); - } -} - -} // namespace art diff --git a/runtime/base/macros.h b/runtime/base/macros.h index bbe0f5a25a..febea61b8d 100644 --- a/runtime/base/macros.h +++ b/runtime/base/macros.h @@ -63,22 +63,33 @@ struct CompileAssert { #define COMPILE_ASSERT(expr, msg) \ typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] // NOLINT -// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. -// It goes in the private: declarations in a class. +// Declare a friend relationship in a class with a test. Used rather that FRIEND_TEST to avoid +// globally importing gtest/gtest.h into the main ART header files. +#define ART_FRIEND_TEST(test_set_name, individual_test)\ +friend class test_set_name##_##individual_test##_Test + +// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private: +// declarations in a class. #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete -// A macro to disallow all the implicit constructors, namely the -// default constructor, copy constructor and operator= functions. +// A macro to disallow all the implicit constructors, namely the default constructor, copy +// constructor and operator= functions. // -// This should be used in the private: declarations for a class -// that wants to prevent anyone from instantiating it. This is -// especially useful for classes containing only static methods. +// This should be used in the private: declarations for a class that wants to prevent anyone from +// instantiating it. This is especially useful for classes containing only static methods. #define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ - TypeName(); \ + TypeName() = delete; \ DISALLOW_COPY_AND_ASSIGN(TypeName) +// A macro to disallow new and delete operators for a class. It goes in the private: declarations. +#define DISALLOW_ALLOCATION() \ + public: \ + ALWAYS_INLINE void operator delete(void*, size_t) { UNREACHABLE(); } \ + private: \ + void* operator new(size_t) = delete + // The arraysize(arr) macro returns the # of elements in an array arr. // The expression is a compile-time constant, and therefore can be // used in defining new arrays, for example. If you use arraysize on @@ -177,7 +188,6 @@ char (&ArraySizeHelper(T (&array)[N]))[N]; #define PURE __attribute__ ((__pure__)) #define WARN_UNUSED __attribute__((warn_unused_result)) -#define NO_RETURN __attribute__((noreturn)) template<typename T> void UNUSED(const T&) {} #define UNREACHABLE __builtin_unreachable diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h index f70db35f1c..e066787bfd 100644 --- a/runtime/base/mutex-inl.h +++ b/runtime/base/mutex-inl.h @@ -21,11 +21,8 @@ #include "mutex.h" -#define ATRACE_TAG ATRACE_TAG_DALVIK - -#include "cutils/trace.h" - #include "base/stringprintf.h" +#include "base/value_object.h" #include "runtime.h" #include "thread.h" @@ -44,35 +41,6 @@ static inline int futex(volatile int *uaddr, int op, int val, const struct times } #endif // ART_USE_FUTEXES -class ScopedContentionRecorder { - public: - ScopedContentionRecorder(BaseMutex* mutex, uint64_t blocked_tid, uint64_t owner_tid) - : mutex_(kLogLockContentions ? mutex : NULL), - blocked_tid_(kLogLockContentions ? blocked_tid : 0), - owner_tid_(kLogLockContentions ? owner_tid : 0), - start_nano_time_(kLogLockContentions ? NanoTime() : 0) { - if (ATRACE_ENABLED()) { - std::string msg = StringPrintf("Lock contention on %s (owner tid: %" PRIu64 ")", - mutex->GetName(), owner_tid); - ATRACE_BEGIN(msg.c_str()); - } - } - - ~ScopedContentionRecorder() { - ATRACE_END(); - if (kLogLockContentions) { - uint64_t end_nano_time = NanoTime(); - mutex_->RecordContention(blocked_tid_, owner_tid_, end_nano_time - start_nano_time_); - } - } - - private: - BaseMutex* const mutex_; - const uint64_t blocked_tid_; - const uint64_t owner_tid_; - const uint64_t start_nano_time_; -}; - static inline uint64_t SafeGetTid(const Thread* self) { if (self != NULL) { return static_cast<uint64_t>(self->GetTid()); @@ -158,15 +126,7 @@ inline void ReaderWriterMutex::SharedLock(Thread* self) { // Add as an extra reader. done = state_.CompareExchangeWeakAcquire(cur_state, cur_state + 1); } else { - // Owner holds it exclusively, hang up. - ScopedContentionRecorder scr(this, GetExclusiveOwnerTid(), SafeGetTid(self)); - ++num_pending_readers_; - if (futex(state_.Address(), FUTEX_WAIT, cur_state, NULL, NULL, 0) != 0) { - if (errno != EAGAIN) { - PLOG(FATAL) << "futex wait failed for " << name_; - } - } - --num_pending_readers_; + HandleSharedLockContention(self, cur_state); } } while (!done); #else diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index cbcd408b6d..423ea77da9 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -19,8 +19,12 @@ #include <errno.h> #include <sys/time.h> +#define ATRACE_TAG ATRACE_TAG_DALVIK +#include "cutils/trace.h" + #include "atomic.h" #include "base/logging.h" +#include "base/value_object.h" #include "mutex-inl.h" #include "runtime.h" #include "scoped_thread_state_change.h" @@ -83,22 +87,59 @@ static bool ComputeRelativeTimeSpec(timespec* result_ts, const timespec& lhs, co } #endif -class ScopedAllMutexesLock { +class ScopedAllMutexesLock FINAL { public: explicit ScopedAllMutexesLock(const BaseMutex* mutex) : mutex_(mutex) { while (!gAllMutexData->all_mutexes_guard.CompareExchangeWeakAcquire(0, mutex)) { NanoSleep(100); } } + ~ScopedAllMutexesLock() { +#if !defined(__clang__) + // TODO: remove this workaround target GCC/libc++/bionic bug "invalid failure memory model". + while (!gAllMutexData->all_mutexes_guard.CompareExchangeWeakSequentiallyConsistent(mutex_, 0)) { +#else while (!gAllMutexData->all_mutexes_guard.CompareExchangeWeakRelease(mutex_, 0)) { +#endif NanoSleep(100); } } + private: const BaseMutex* const mutex_; }; +// Scoped class that generates events at the beginning and end of lock contention. +class ScopedContentionRecorder FINAL : public ValueObject { + public: + ScopedContentionRecorder(BaseMutex* mutex, uint64_t blocked_tid, uint64_t owner_tid) + : mutex_(kLogLockContentions ? mutex : NULL), + blocked_tid_(kLogLockContentions ? blocked_tid : 0), + owner_tid_(kLogLockContentions ? owner_tid : 0), + start_nano_time_(kLogLockContentions ? NanoTime() : 0) { + if (ATRACE_ENABLED()) { + std::string msg = StringPrintf("Lock contention on %s (owner tid: %" PRIu64 ")", + mutex->GetName(), owner_tid); + ATRACE_BEGIN(msg.c_str()); + } + } + + ~ScopedContentionRecorder() { + ATRACE_END(); + if (kLogLockContentions) { + uint64_t end_nano_time = NanoTime(); + mutex_->RecordContention(blocked_tid_, owner_tid_, end_nano_time - start_nano_time_); + } + } + + private: + BaseMutex* const mutex_; + const uint64_t blocked_tid_; + const uint64_t owner_tid_; + const uint64_t start_nano_time_; +}; + BaseMutex::BaseMutex(const char* name, LockLevel level) : level_(level), name_(name) { if (kLogLockContentions) { ScopedAllMutexesLock mu(this); @@ -421,9 +462,9 @@ void Mutex::ExclusiveUnlock(Thread* self) { if (this != Locks::logging_lock_) { LOG(FATAL) << "Unexpected state_ in unlock " << cur_state << " for " << name_; } else { - LogMessageData data(__FILE__, __LINE__, INTERNAL_FATAL, -1); - LogMessage::LogLine(data, StringPrintf("Unexpected state_ %d in unlock for %s", - cur_state, name_).c_str()); + LogMessage::LogLine(__FILE__, __LINE__, INTERNAL_FATAL, + StringPrintf("Unexpected state_ %d in unlock for %s", + cur_state, name_).c_str()); _exit(1); } } @@ -605,6 +646,20 @@ bool ReaderWriterMutex::ExclusiveLockWithTimeout(Thread* self, int64_t ms, int32 } #endif +#if ART_USE_FUTEXES +void ReaderWriterMutex::HandleSharedLockContention(Thread* self, int32_t cur_state) { + // Owner holds it exclusively, hang up. + ScopedContentionRecorder scr(this, GetExclusiveOwnerTid(), SafeGetTid(self)); + ++num_pending_readers_; + if (futex(state_.Address(), FUTEX_WAIT, cur_state, NULL, NULL, 0) != 0) { + if (errno != EAGAIN) { + PLOG(FATAL) << "futex wait failed for " << name_; + } + } + --num_pending_readers_; +} +#endif + bool ReaderWriterMutex::SharedTryLock(Thread* self) { DCHECK(self == NULL || self == Thread::Current()); #if ART_USE_FUTEXES @@ -673,7 +728,7 @@ ConditionVariable::ConditionVariable(const char* name, Mutex& guard) CHECK_MUTEX_CALL(pthread_condattr_init, (&cond_attrs)); #if !defined(__APPLE__) // Apple doesn't have CLOCK_MONOTONIC or pthread_condattr_setclock. - CHECK_MUTEX_CALL(pthread_condattr_setclock(&cond_attrs, CLOCK_MONOTONIC)); + CHECK_MUTEX_CALL(pthread_condattr_setclock, (&cond_attrs, CLOCK_MONOTONIC)); #endif CHECK_MUTEX_CALL(pthread_cond_init, (&cond_, &cond_attrs)); #endif diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 516fa07b66..628231a273 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -361,6 +361,9 @@ class LOCKABLE ReaderWriterMutex : public BaseMutex { private: #if ART_USE_FUTEXES + // Out-of-inline path for handling contention for a SharedLock. + void HandleSharedLockContention(Thread* self, int32_t cur_state); + // -1 implies held exclusive, +ve shared held by state_ many owners. AtomicInteger state_; // Exclusive owner. Modification guarded by this mutex. diff --git a/runtime/base/stringpiece.cc b/runtime/base/stringpiece.cc index 824ee4863d..2570bad85d 100644 --- a/runtime/base/stringpiece.cc +++ b/runtime/base/stringpiece.cc @@ -16,7 +16,7 @@ #include "stringpiece.h" -#include <iostream> +#include <ostream> #include <utility> #include "logging.h" diff --git a/runtime/base/logging_linux.cc b/runtime/base/to_str.h index 0399128d65..6b1c84c411 100644 --- a/runtime/base/logging_linux.cc +++ b/runtime/base/to_str.h @@ -14,25 +14,37 @@ * limitations under the License. */ -#include "logging.h" +#ifndef ART_RUNTIME_BASE_TO_STR_H_ +#define ART_RUNTIME_BASE_TO_STR_H_ -#include <sys/types.h> -#include <unistd.h> +#include <sstream> -#include <cstdio> -#include <cstring> -#include <iostream> +namespace art { -#include "base/stringprintf.h" -#include "utils.h" +// Helps you use operator<< in a const char*-like context such as our various 'F' methods with +// format strings. +template<typename T> +class ToStr { + public: + explicit ToStr(const T& value) { + std::ostringstream os; + os << value; + s_ = os.str(); + } -namespace art { + const char* c_str() const { + return s_.c_str(); + } -void LogMessage::LogLine(const LogMessageData& data, const char* message) { - char severity = "VDIWEFF"[data.severity]; - fprintf(stderr, "%s %c %5d %5d %s:%d] %s\n", - ProgramInvocationShortName(), severity, getpid(), ::art::GetTid(), - data.file, data.line_number, message); -} + const std::string& str() const { + return s_; + } + + private: + std::string s_; + DISALLOW_COPY_AND_ASSIGN(ToStr); +}; } // namespace art + +#endif // ART_RUNTIME_BASE_TO_STR_H_ diff --git a/runtime/base/unix_file/mapped_file.cc b/runtime/base/unix_file/mapped_file.cc index 63927b1216..77f4d020a9 100644 --- a/runtime/base/unix_file/mapped_file.cc +++ b/runtime/base/unix_file/mapped_file.cc @@ -42,7 +42,7 @@ bool MappedFile::MapReadOnly() { struct stat st; int result = TEMP_FAILURE_RETRY(fstat(Fd(), &st)); if (result == -1) { - PLOG(WARNING) << "Failed to stat file '" << GetPath() << "'"; + PLOG(::art::WARNING) << "Failed to stat file '" << GetPath() << "'"; return false; } file_size_ = st.st_size; @@ -50,8 +50,8 @@ bool MappedFile::MapReadOnly() { mapped_file_ = mmap(NULL, file_size_, PROT_READ, MAP_PRIVATE, Fd(), 0); } while (mapped_file_ == MAP_FAILED && errno == EINTR); if (mapped_file_ == MAP_FAILED) { - PLOG(WARNING) << "Failed to mmap file '" << GetPath() << "' of size " - << file_size_ << " bytes to memory"; + PLOG(::art::WARNING) << "Failed to mmap file '" << GetPath() << "' of size " + << file_size_ << " bytes to memory"; return false; } map_mode_ = kMapReadOnly; @@ -67,8 +67,7 @@ bool MappedFile::MapReadWrite(int64_t file_size) { int result = TEMP_FAILURE_RETRY(ftruncate(Fd(), file_size)); #endif if (result == -1) { - PLOG(ERROR) << "Failed to truncate file '" << GetPath() - << "' to size " << file_size; + PLOG(::art::ERROR) << "Failed to truncate file '" << GetPath() << "' to size " << file_size; return false; } file_size_ = file_size; @@ -77,7 +76,7 @@ bool MappedFile::MapReadWrite(int64_t file_size) { mmap(NULL, file_size_, PROT_READ | PROT_WRITE, MAP_SHARED, Fd(), 0); } while (mapped_file_ == MAP_FAILED && errno == EINTR); if (mapped_file_ == MAP_FAILED) { - PLOG(WARNING) << "Failed to mmap file '" << GetPath() << "' of size " + PLOG(::art::WARNING) << "Failed to mmap file '" << GetPath() << "' of size " << file_size_ << " bytes to memory"; return false; } @@ -89,8 +88,7 @@ bool MappedFile::Unmap() { CHECK(IsMapped()); int result = TEMP_FAILURE_RETRY(munmap(mapped_file_, file_size_)); if (result == -1) { - PLOG(WARNING) << "Failed unmap file '" << GetPath() << "' of size " - << file_size_; + PLOG(::art::WARNING) << "Failed unmap file '" << GetPath() << "' of size " << file_size_; return false; } else { mapped_file_ = NULL; diff --git a/runtime/base/value_object.h b/runtime/base/value_object.h index ee0e2a0dcc..8c752a923d 100644 --- a/runtime/base/value_object.h +++ b/runtime/base/value_object.h @@ -17,19 +17,13 @@ #ifndef ART_RUNTIME_BASE_VALUE_OBJECT_H_ #define ART_RUNTIME_BASE_VALUE_OBJECT_H_ -#include "base/logging.h" +#include "base/macros.h" namespace art { class ValueObject { - public: - void* operator new(size_t size) { - LOG(FATAL) << "UNREACHABLE"; - abort(); - } - void operator delete(void*, size_t) { - LOG(FATAL) << "UNREACHABLE"; - } + private: + DISALLOW_ALLOCATION(); }; } // namespace art diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index fec1824a9e..b2df09136d 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -20,6 +20,7 @@ #include <zlib.h> #include "base/logging.h" +#include "base/to_str.h" #include "class_linker.h" #include "class_linker-inl.h" #include "dex_file-inl.h" diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h index 1a78d72657..9d2d59c9a4 100644 --- a/runtime/check_reference_map_visitor.h +++ b/runtime/check_reference_map_visitor.h @@ -37,7 +37,7 @@ class CheckReferenceMapVisitor : public StackVisitor { CHECK_EQ(GetDexPc(), DexFile::kDexNoIndex); } - if (!m || m->IsNative() || m->IsRuntimeMethod() || IsShadowFrame()) { + if (m == nullptr || m->IsNative() || m->IsRuntimeMethod() || IsShadowFrame()) { return true; } @@ -84,8 +84,12 @@ class CheckReferenceMapVisitor : public StackVisitor { case DexRegisterMap::kInRegister: CHECK_NE(register_mask & dex_register_map.GetValue(reg), 0u); break; + case DexRegisterMap::kInFpuRegister: + // In Fpu register, should not be a reference. + CHECK(false); + break; case DexRegisterMap::kConstant: - CHECK_EQ(dex_register_map.GetValue(0), 0); + CHECK_EQ(dex_register_map.GetValue(reg), 0); break; } } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index cf3a581668..f6717fb006 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -17,6 +17,7 @@ #include "class_linker.h" #include <deque> +#include <iostream> #include <memory> #include <queue> #include <string> @@ -32,6 +33,7 @@ #include "compiler_callbacks.h" #include "debugger.h" #include "dex_file-inl.h" +#include "entrypoints/runtime_asm_entrypoints.h" #include "gc_root-inl.h" #include "gc/accounting/card_table-inl.h" #include "gc/accounting/heap_bitmap.h" @@ -115,7 +117,17 @@ static void ThrowEarlierClassFailure(mirror::Class* c) } } -static void WrapExceptionInInitializer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +static void VlogClassInitializationFailure(Handle<mirror::Class> klass) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (VLOG_IS_ON(class_linker)) { + std::string temp; + LOG(INFO) << "Failed to initialize class " << klass->GetDescriptor(&temp) << " from " + << klass->GetLocation() << "\n" << Thread::Current()->GetException(nullptr)->Dump(); + } +} + +static void WrapExceptionInInitializer(Handle<mirror::Class> klass) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Thread* self = Thread::Current(); JNIEnv* env = self->GetJniEnv(); @@ -132,6 +144,7 @@ static void WrapExceptionInInitializer() SHARED_LOCKS_REQUIRED(Locks::mutator_lo self->ThrowNewWrappedException(throw_location, "Ljava/lang/ExceptionInInitializerError;", nullptr); } + VlogClassInitializationFailure(klass); } static size_t Hash(const char* s) { @@ -225,44 +238,6 @@ static void ShuffleForward(const size_t num_fields, size_t* current_field_idx, } } -const char* ClassLinker::class_roots_descriptors_[] = { - "Ljava/lang/Class;", - "Ljava/lang/Object;", - "[Ljava/lang/Class;", - "[Ljava/lang/Object;", - "Ljava/lang/String;", - "Ljava/lang/DexCache;", - "Ljava/lang/ref/Reference;", - "Ljava/lang/reflect/ArtField;", - "Ljava/lang/reflect/ArtMethod;", - "Ljava/lang/reflect/Proxy;", - "[Ljava/lang/String;", - "[Ljava/lang/reflect/ArtField;", - "[Ljava/lang/reflect/ArtMethod;", - "Ljava/lang/ClassLoader;", - "Ljava/lang/Throwable;", - "Ljava/lang/ClassNotFoundException;", - "Ljava/lang/StackTraceElement;", - "Z", - "B", - "C", - "D", - "F", - "I", - "J", - "S", - "V", - "[Z", - "[B", - "[C", - "[D", - "[F", - "[I", - "[J", - "[S", - "[Ljava/lang/StackTraceElement;", -}; - ClassLinker::ClassLinker(InternTable* intern_table) // dex_lock_ is recursive as it may be used in stack dumping. : dex_lock_("ClassLinker dex lock", kDefaultMutexLevel), @@ -281,16 +256,9 @@ ClassLinker::ClassLinker(InternTable* intern_table) quick_imt_conflict_trampoline_(nullptr), quick_generic_jni_trampoline_(nullptr), quick_to_interpreter_bridge_trampoline_(nullptr) { - CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax)); memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*)); } -// To set a value for generic JNI. May be necessary in compiler tests. -extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*); -extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*); -extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*); -extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*); - void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class_path) { VLOG(startup) << "ClassLinker::Init"; CHECK(!Runtime::Current()->GetHeap()->HasImageSpace()) << "Runtime has image. We should use it."; @@ -471,12 +439,12 @@ void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class // Set up GenericJNI entrypoint. That is mainly a hack for common_compiler_test.h so that // we do not need friend classes or a publicly exposed setter. - quick_generic_jni_trampoline_ = reinterpret_cast<void*>(art_quick_generic_jni_trampoline); + quick_generic_jni_trampoline_ = GetQuickGenericJniStub(); if (!runtime->IsCompiler()) { // We need to set up the generic trampolines since we don't have an image. - quick_resolution_trampoline_ = reinterpret_cast<void*>(art_quick_resolution_trampoline); - quick_imt_conflict_trampoline_ = reinterpret_cast<void*>(art_quick_imt_conflict_trampoline); - quick_to_interpreter_bridge_trampoline_ = reinterpret_cast<void*>(art_quick_to_interpreter_bridge); + quick_resolution_trampoline_ = GetQuickResolutionStub(); + quick_imt_conflict_trampoline_ = GetQuickImtConflictStub(); + quick_to_interpreter_bridge_trampoline_ = GetQuickToInterpreterBridge(); } // Object, String and DexCache need to be rerun through FindSystemClass to finish init @@ -560,15 +528,15 @@ void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class CHECK_EQ(java_lang_reflect_ArtField.Get(), Art_field_class); mirror::Class* String_array_class = - FindSystemClass(self, class_roots_descriptors_[kJavaLangStringArrayClass]); + FindSystemClass(self, GetClassRootDescriptor(kJavaLangStringArrayClass)); CHECK_EQ(object_array_string.Get(), String_array_class); mirror::Class* Art_method_array_class = - FindSystemClass(self, class_roots_descriptors_[kJavaLangReflectArtMethodArrayClass]); + FindSystemClass(self, GetClassRootDescriptor(kJavaLangReflectArtMethodArrayClass)); CHECK_EQ(object_array_art_method.Get(), Art_method_array_class); mirror::Class* Art_field_array_class = - FindSystemClass(self, class_roots_descriptors_[kJavaLangReflectArtFieldArrayClass]); + FindSystemClass(self, GetClassRootDescriptor(kJavaLangReflectArtFieldArrayClass)); CHECK_EQ(object_array_art_field.Get(), Art_field_array_class); // End of special init trickery, subsequent classes may be loaded via FindSystemClass. @@ -1655,7 +1623,7 @@ static void InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg) if (obj->IsArtMethod()) { mirror::ArtMethod* method = obj->AsArtMethod(); if (!method->IsNative()) { - method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge); + method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge); if (method != Runtime::Current()->GetResolutionMethod()) { method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge()); @@ -2524,7 +2492,7 @@ const void* ClassLinker::GetQuickOatCodeFor(mirror::ArtMethod* method) { if (result == nullptr) { if (method->IsNative()) { // No code and native? Use generic trampoline. - result = GetQuickGenericJniTrampoline(); + result = GetQuickGenericJniStub(); } else if (method->IsPortableCompiled()) { // No code? Do we expect portable code? result = GetQuickToPortableBridge(); @@ -2678,7 +2646,7 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { // Use interpreter entry point. // Check whether the method is native, in which case it's generic JNI. if (quick_code == nullptr && portable_code == nullptr && method->IsNative()) { - quick_code = GetQuickGenericJniTrampoline(); + quick_code = GetQuickGenericJniStub(); portable_code = GetPortableToQuickBridge(); } else { portable_code = GetPortableToInterpreterBridge(); @@ -2704,7 +2672,8 @@ void ClassLinker::LinkCode(Handle<mirror::ArtMethod> method, const OatFile::OatClass* oat_class, const DexFile& dex_file, uint32_t dex_method_index, uint32_t method_index) { - if (Runtime::Current()->IsCompiler()) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsCompiler()) { // The following code only applies to a non-compiler runtime. return; } @@ -2723,7 +2692,7 @@ void ClassLinker::LinkCode(Handle<mirror::ArtMethod> method, method->GetEntryPointFromQuickCompiledCode(), method->GetEntryPointFromPortableCompiledCode()); if (enter_interpreter && !method->IsNative()) { - method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge); + method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge); } else { method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); } @@ -2739,15 +2708,15 @@ void ClassLinker::LinkCode(Handle<mirror::ArtMethod> method, // For static methods excluding the class initializer, install the trampoline. // It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines // after initializing class (see ClassLinker::InitializeClass method). - method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionTrampoline()); - method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionTrampoline()); + method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub()); + method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionStub()); } else if (enter_interpreter) { if (!method->IsNative()) { // Set entry point from compiled code if there's no code or in interpreter only mode. method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge()); } else { - method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniTrampoline()); + method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub()); method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge()); } } else if (method->GetEntryPointFromPortableCompiledCode() != nullptr) { @@ -2761,18 +2730,18 @@ void ClassLinker::LinkCode(Handle<mirror::ArtMethod> method, if (method->IsNative()) { // Unregistering restores the dlsym lookup stub. - method->UnregisterNative(Thread::Current()); + method->UnregisterNative(); if (enter_interpreter) { - // We have a native method here without code. Then it should have either the GenericJni - // trampoline as entrypoint (non-static), or the Resolution trampoline (static). - DCHECK(method->GetEntryPointFromQuickCompiledCode() == GetQuickResolutionTrampoline() - || method->GetEntryPointFromQuickCompiledCode() == GetQuickGenericJniTrampoline()); + // We have a native method here without code. Then it should have either the generic JNI + // trampoline as entrypoint (non-static), or the resolution trampoline (static). + // TODO: this doesn't handle all the cases where trampolines may be installed. + const void* entry_point = method->GetEntryPointFromQuickCompiledCode(); + DCHECK(IsQuickGenericJniStub(entry_point) || IsQuickResolutionStub(entry_point)); } } // Allow instrumentation its chance to hijack code. - Runtime* runtime = Runtime::Current(); runtime->GetInstrumentation()->UpdateMethodsCode(method.Get(), method->GetEntryPointFromQuickCompiledCode(), method->GetEntryPointFromPortableCompiledCode(), @@ -3213,13 +3182,13 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto new_class.Assign(GetClassRoot(kClassArrayClass)); } else if (strcmp(descriptor, "[Ljava/lang/Object;") == 0) { new_class.Assign(GetClassRoot(kObjectArrayClass)); - } else if (strcmp(descriptor, class_roots_descriptors_[kJavaLangStringArrayClass]) == 0) { + } else if (strcmp(descriptor, GetClassRootDescriptor(kJavaLangStringArrayClass)) == 0) { new_class.Assign(GetClassRoot(kJavaLangStringArrayClass)); } else if (strcmp(descriptor, - class_roots_descriptors_[kJavaLangReflectArtMethodArrayClass]) == 0) { + GetClassRootDescriptor(kJavaLangReflectArtMethodArrayClass)) == 0) { new_class.Assign(GetClassRoot(kJavaLangReflectArtMethodArrayClass)); } else if (strcmp(descriptor, - class_roots_descriptors_[kJavaLangReflectArtFieldArrayClass]) == 0) { + GetClassRootDescriptor(kJavaLangReflectArtFieldArrayClass)) == 0) { new_class.Assign(GetClassRoot(kJavaLangReflectArtFieldArrayClass)); } else if (strcmp(descriptor, "[C") == 0) { new_class.Assign(GetClassRoot(kCharArrayClass)); @@ -4151,6 +4120,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, // Was the class already found to be erroneous? Done under the lock to match the JLS. if (klass->IsErroneous()) { ThrowEarlierClassFailure(klass.Get()); + VlogClassInitializationFailure(klass); return false; } @@ -4163,6 +4133,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, // compile time. if (klass->IsErroneous()) { CHECK(self->IsExceptionPending()); + VlogClassInitializationFailure(klass); } else { CHECK(Runtime::Current()->IsCompiler()); CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime); @@ -4181,6 +4152,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, if (klass->GetStatus() == mirror::Class::kStatusInitializing) { // Could have got an exception during verification. if (self->IsExceptionPending()) { + VlogClassInitializationFailure(klass); return false; } // We caught somebody else in the act; was it us? @@ -4277,7 +4249,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, ObjectLock<mirror::Class> lock(self, klass); if (self->IsExceptionPending()) { - WrapExceptionInInitializer(); + WrapExceptionInInitializer(klass); klass->SetStatus(mirror::Class::kStatusError, self); success = false; } else { @@ -4311,9 +4283,9 @@ bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass, Thread* se // When we wake up, repeat the test for init-in-progress. If // there's an exception pending (only possible if - // "interruptShouldThrow" was set), bail out. + // we were not using WaitIgnoringInterrupts), bail out. if (self->IsExceptionPending()) { - WrapExceptionInInitializer(); + WrapExceptionInInitializer(klass); klass->SetStatus(mirror::Class::kStatusError, self); return false; } @@ -4330,6 +4302,7 @@ bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass, Thread* se // different thread. Synthesize one here. ThrowNoClassDefFoundError("<clinit> failed for class %s; see exception in other thread", PrettyDescriptor(klass.Get()).c_str()); + VlogClassInitializationFailure(klass); return false; } if (klass->IsInitialized()) { @@ -5138,7 +5111,7 @@ bool ClassLinker::LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_ bool seen_non_ref = false; for (size_t i = 0; i < num_fields; i++) { mirror::ArtField* field = fields->Get(i); - if (false) { // enable to debug field layout + if ((false)) { // enable to debug field layout LOG(INFO) << "LinkFields: " << (is_static ? "static" : "instance") << " class=" << PrettyClass(klass.Get()) << " field=" << PrettyField(field) @@ -5531,6 +5504,84 @@ void ClassLinker::DumpAllClasses(int flags) { } } +static OatFile::OatMethod CreateOatMethod(const void* code, const uint8_t* gc_map, + bool is_portable) { + CHECK_EQ(kUsePortableCompiler, is_portable); + CHECK(code != nullptr); + const uint8_t* base; + uint32_t code_offset, gc_map_offset; + if (gc_map == nullptr) { + base = reinterpret_cast<const uint8_t*>(code); // Base of data points at code. + base -= sizeof(void*); // Move backward so that code_offset != 0. + code_offset = sizeof(void*); + gc_map_offset = 0; + } else { + // TODO: 64bit support. + base = nullptr; // Base of data in oat file, ie 0. + code_offset = PointerToLowMemUInt32(code); + gc_map_offset = PointerToLowMemUInt32(gc_map); + } + return OatFile::OatMethod(base, code_offset, gc_map_offset); +} + +bool ClassLinker::IsPortableResolutionStub(const void* entry_point) const { + return (entry_point == GetPortableResolutionStub()) || + (portable_resolution_trampoline_ == entry_point); +} + +bool ClassLinker::IsQuickResolutionStub(const void* entry_point) const { + return (entry_point == GetQuickResolutionStub()) || + (quick_resolution_trampoline_ == entry_point); +} + +bool ClassLinker::IsPortableToInterpreterBridge(const void* entry_point) const { + return (entry_point == GetPortableToInterpreterBridge()); + // TODO: portable_to_interpreter_bridge_trampoline_ == entry_point; +} + +bool ClassLinker::IsQuickToInterpreterBridge(const void* entry_point) const { + return (entry_point == GetQuickToInterpreterBridge()) || + (quick_to_interpreter_bridge_trampoline_ == entry_point); +} + +bool ClassLinker::IsQuickGenericJniStub(const void* entry_point) const { + return (entry_point == GetQuickGenericJniStub()) || + (quick_generic_jni_trampoline_ == entry_point); +} + +const void* ClassLinker::GetRuntimeQuickGenericJniStub() const { + return GetQuickGenericJniStub(); +} + +void ClassLinker::SetEntryPointsToCompiledCode(mirror::ArtMethod* method, const void* method_code, + bool is_portable) const { + OatFile::OatMethod oat_method = CreateOatMethod(method_code, nullptr, is_portable); + oat_method.LinkMethod(method); + method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); + // Create bridges to transition between different kinds of compiled bridge. + if (method->GetEntryPointFromPortableCompiledCode() == nullptr) { + method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge()); + } else { + CHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr); + method->SetEntryPointFromQuickCompiledCode(GetQuickToPortableBridge()); + method->SetIsPortableCompiled(); + } +} + +void ClassLinker::SetEntryPointsToInterpreter(mirror::ArtMethod* method) const { + if (!method->IsNative()) { + method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge); + method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge()); + method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); + } else { + const void* quick_method_code = GetQuickGenericJniStub(); + OatFile::OatMethod oat_method = CreateOatMethod(quick_method_code, nullptr, false); + oat_method.LinkMethod(method); + method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); + method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge()); + } +} + void ClassLinker::DumpForSigQuit(std::ostream& os) { Thread* self = Thread::Current(); if (dex_cache_image_class_lookup_required_) { @@ -5569,4 +5620,50 @@ void ClassLinker::SetClassRoot(ClassRoot class_root, mirror::Class* klass) { class_roots->Set<false>(class_root, klass); } +const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) { + static const char* class_roots_descriptors[] = { + "Ljava/lang/Class;", + "Ljava/lang/Object;", + "[Ljava/lang/Class;", + "[Ljava/lang/Object;", + "Ljava/lang/String;", + "Ljava/lang/DexCache;", + "Ljava/lang/ref/Reference;", + "Ljava/lang/reflect/ArtField;", + "Ljava/lang/reflect/ArtMethod;", + "Ljava/lang/reflect/Proxy;", + "[Ljava/lang/String;", + "[Ljava/lang/reflect/ArtField;", + "[Ljava/lang/reflect/ArtMethod;", + "Ljava/lang/ClassLoader;", + "Ljava/lang/Throwable;", + "Ljava/lang/ClassNotFoundException;", + "Ljava/lang/StackTraceElement;", + "Z", + "B", + "C", + "D", + "F", + "I", + "J", + "S", + "V", + "[Z", + "[B", + "[C", + "[D", + "[F", + "[I", + "[J", + "[S", + "[Ljava/lang/StackTraceElement;", + }; + COMPILE_ASSERT(arraysize(class_roots_descriptors) == size_t(kClassRootsMax), + mismatch_between_class_descriptors_and_class_root_enum); + + const char* descriptor = class_roots_descriptors[class_root]; + CHECK(descriptor != nullptr); + return descriptor; +} + } // namespace art diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 373fa893ea..18479268ed 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -27,7 +27,6 @@ #include "base/mutex.h" #include "dex_file.h" #include "gc_root.h" -#include "gtest/gtest.h" #include "jni.h" #include "oat_file.h" #include "object_callbacks.h" @@ -60,6 +59,46 @@ enum VisitRootFlags : uint8_t; class ClassLinker { public: + // Well known mirror::Class roots accessed via GetClassRoot. + enum ClassRoot { + kJavaLangClass, + kJavaLangObject, + kClassArrayClass, + kObjectArrayClass, + kJavaLangString, + kJavaLangDexCache, + kJavaLangRefReference, + kJavaLangReflectArtField, + kJavaLangReflectArtMethod, + kJavaLangReflectProxy, + kJavaLangStringArrayClass, + kJavaLangReflectArtFieldArrayClass, + kJavaLangReflectArtMethodArrayClass, + kJavaLangClassLoader, + kJavaLangThrowable, + kJavaLangClassNotFoundException, + kJavaLangStackTraceElement, + kPrimitiveBoolean, + kPrimitiveByte, + kPrimitiveChar, + kPrimitiveDouble, + kPrimitiveFloat, + kPrimitiveInt, + kPrimitiveLong, + kPrimitiveShort, + kPrimitiveVoid, + kBooleanArrayClass, + kByteArrayClass, + kCharArrayClass, + kDoubleArrayClass, + kFloatArrayClass, + kIntArrayClass, + kLongArrayClass, + kShortArrayClass, + kJavaLangStackTraceElementArrayClass, + kClassRootsMax, + }; + explicit ClassLinker(InternTable* intern_table); ~ClassLinker(); @@ -371,34 +410,38 @@ class ClassLinker { pid_t GetClassesLockOwner(); // For SignalCatcher. pid_t GetDexLockOwner(); // For SignalCatcher. - const void* GetPortableResolutionTrampoline() const { - return portable_resolution_trampoline_; - } + mirror::Class* GetClassRoot(ClassRoot class_root) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const void* GetQuickGenericJniTrampoline() const { - return quick_generic_jni_trampoline_; - } + static const char* GetClassRootDescriptor(ClassRoot class_root); - const void* GetQuickResolutionTrampoline() const { - return quick_resolution_trampoline_; - } + // Is the given entry point portable code to run the resolution stub? + bool IsPortableResolutionStub(const void* entry_point) const; - const void* GetPortableImtConflictTrampoline() const { - return portable_imt_conflict_trampoline_; - } + // Is the given entry point quick code to run the resolution stub? + bool IsQuickResolutionStub(const void* entry_point) const; - const void* GetQuickImtConflictTrampoline() const { - return quick_imt_conflict_trampoline_; - } + // Is the given entry point portable code to bridge into the interpreter? + bool IsPortableToInterpreterBridge(const void* entry_point) const; - const void* GetQuickToInterpreterBridgeTrampoline() const { - return quick_to_interpreter_bridge_trampoline_; - } + // Is the given entry point quick code to bridge into the interpreter? + bool IsQuickToInterpreterBridge(const void* entry_point) const; + + // Is the given entry point quick code to run the generic JNI stub? + bool IsQuickGenericJniStub(const void* entry_point) const; InternTable* GetInternTable() const { return intern_table_; } + // Set the entrypoints up for method to the given code. + void SetEntryPointsToCompiledCode(mirror::ArtMethod* method, const void* method_code, + bool is_portable) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Set the entrypoints up for method to the enter the interpreter. + void SetEntryPointsToInterpreter(mirror::ArtMethod* method) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Attempts to insert a class into a class table. Returns NULL if // the class was inserted, otherwise returns an existing class with // the same descriptor and ClassLoader. @@ -668,6 +711,12 @@ class ClassLinker { void FixupTemporaryDeclaringClass(mirror::Class* temp_class, mirror::Class* new_class) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void SetClassRoot(ClassRoot class_root, mirror::Class* klass) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Return the quick generic JNI stub for testing. + const void* GetRuntimeQuickGenericJniStub() const; + std::vector<const DexFile*> boot_class_path_; mutable ReaderWriterMutex dex_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; @@ -691,61 +740,9 @@ class ClassLinker { // the classes into the class_table_ to avoid dex cache based searches. Atomic<uint32_t> failed_dex_cache_class_lookups_; - // indexes into class_roots_. - // needs to be kept in sync with class_roots_descriptors_. - enum ClassRoot { - kJavaLangClass, - kJavaLangObject, - kClassArrayClass, - kObjectArrayClass, - kJavaLangString, - kJavaLangDexCache, - kJavaLangRefReference, - kJavaLangReflectArtField, - kJavaLangReflectArtMethod, - kJavaLangReflectProxy, - kJavaLangStringArrayClass, - kJavaLangReflectArtFieldArrayClass, - kJavaLangReflectArtMethodArrayClass, - kJavaLangClassLoader, - kJavaLangThrowable, - kJavaLangClassNotFoundException, - kJavaLangStackTraceElement, - kPrimitiveBoolean, - kPrimitiveByte, - kPrimitiveChar, - kPrimitiveDouble, - kPrimitiveFloat, - kPrimitiveInt, - kPrimitiveLong, - kPrimitiveShort, - kPrimitiveVoid, - kBooleanArrayClass, - kByteArrayClass, - kCharArrayClass, - kDoubleArrayClass, - kFloatArrayClass, - kIntArrayClass, - kLongArrayClass, - kShortArrayClass, - kJavaLangStackTraceElementArrayClass, - kClassRootsMax, - }; + // Well known mirror::Class roots. GcRoot<mirror::ObjectArray<mirror::Class>> class_roots_; - mirror::Class* GetClassRoot(ClassRoot class_root) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - void SetClassRoot(ClassRoot class_root, mirror::Class* klass) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - static const char* class_roots_descriptors_[]; - - const char* GetClassRootDescriptor(ClassRoot class_root) { - const char* descriptor = class_roots_descriptors_[class_root]; - CHECK(descriptor != NULL); - return descriptor; - } - // The interface table used by all arrays. GcRoot<mirror::IfTable> array_iftable_; @@ -773,12 +770,11 @@ class ClassLinker { friend class ImageWriter; // for GetClassRoots friend class ImageDumper; // for FindOpenedOatFileFromOatLocation friend class ElfPatcher; // for FindOpenedOatFileForDexFile & FindOpenedOatFileFromOatLocation + friend class JniCompilerTest; // for GetRuntimeQuickGenericJniStub friend class NoDex2OatTest; // for FindOpenedOatFileForDexFile friend class NoPatchoatTest; // for FindOpenedOatFileForDexFile - FRIEND_TEST(ClassLinkerTest, ClassRootDescriptors); - FRIEND_TEST(mirror::DexCacheTest, Open); - FRIEND_TEST(ExceptionTest, FindExceptionHandler); - FRIEND_TEST(ObjectTest, AllocObjectArray); + ART_FRIEND_TEST(mirror::DexCacheTest, Open); // for AllocDexCache + DISALLOW_COPY_AND_ASSIGN(ClassLinker); }; diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index eed6f7184c..6e3ebc24d8 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -44,7 +44,7 @@ int main(int argc, char **argv) { art::InitLogging(argv); - LOG(INFO) << "Running main() from common_runtime_test.cc..."; + LOG(::art::INFO) << "Running main() from common_runtime_test.cc..."; testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } @@ -185,6 +185,8 @@ void CommonRuntimeTest::SetUp() { int mkdir_result = mkdir(dalvik_cache_.c_str(), 0700); ASSERT_EQ(mkdir_result, 0); + MemMap::Init(); // For LoadExpectSingleDexFile + std::string error_msg; java_lang_dex_file_ = LoadExpectSingleDexFile(GetLibCoreDexFileName().c_str()); boot_class_path_.push_back(java_lang_dex_file_); diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 6ccb53aa18..d9061c8e68 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -617,7 +617,7 @@ bool Dbg::ParseJdwpOptions(const std::string& options) { VLOG(jdwp) << "ParseJdwpOptions: " << options; std::vector<std::string> pairs; - Split(options, ',', pairs); + Split(options, ',', &pairs); for (size_t i = 0; i < pairs.size(); ++i) { std::string::size_type equals = pairs[i].find('='); @@ -2017,7 +2017,7 @@ JDWP::JdwpError Dbg::GetThreadGroup(JDWP::ObjectId thread_id, JDWP::ExpandBuf* p } else if (error == JDWP::ERR_NONE) { mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread); CHECK(c != nullptr); - mirror::ArtField* f = c->FindInstanceField("group", "Ljava/lang/ThreadGroup;"); + mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_Thread_group); CHECK(f != nullptr); mirror::Object* group = f->GetObject(thread_object); CHECK(group != nullptr); @@ -2058,8 +2058,7 @@ JDWP::JdwpError Dbg::GetThreadGroupName(JDWP::ObjectId thread_group_id, JDWP::Ex return error; } ScopedAssertNoThreadSuspension ants(soa.Self(), "Debugger: GetThreadGroupName"); - mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup); - mirror::ArtField* f = c->FindInstanceField("name", "Ljava/lang/String;"); + mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_ThreadGroup_name); CHECK(f != nullptr); mirror::String* s = reinterpret_cast<mirror::String*>(f->GetObject(thread_group)); @@ -2078,9 +2077,7 @@ JDWP::JdwpError Dbg::GetThreadGroupParent(JDWP::ObjectId thread_group_id, JDWP:: mirror::Object* parent; { ScopedAssertNoThreadSuspension ants(soa.Self(), "Debugger: GetThreadGroupParent"); - mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup); - CHECK(c != nullptr); - mirror::ArtField* f = c->FindInstanceField("parent", "Ljava/lang/ThreadGroup;"); + mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_ThreadGroup_parent); CHECK(f != nullptr); parent = f->GetObject(thread_group); } @@ -2095,12 +2092,20 @@ static void GetChildThreadGroups(ScopedObjectAccessUnchecked& soa, mirror::Objec CHECK(thread_group != nullptr); // Get the ArrayList<ThreadGroup> "groups" out of this thread group... - mirror::ArtField* groups_field = thread_group->GetClass()->FindInstanceField("groups", "Ljava/util/List;"); + mirror::ArtField* groups_field = soa.DecodeField(WellKnownClasses::java_lang_ThreadGroup_groups); mirror::Object* groups_array_list = groups_field->GetObject(thread_group); + { + // The "groups" field is declared as a java.util.List: check it really is + // an instance of java.util.ArrayList. + CHECK(groups_array_list != nullptr); + mirror::Class* java_util_ArrayList_class = + soa.Decode<mirror::Class*>(WellKnownClasses::java_util_ArrayList); + CHECK(groups_array_list->InstanceOf(java_util_ArrayList_class)); + } // Get the array and size out of the ArrayList<ThreadGroup>... - mirror::ArtField* array_field = groups_array_list->GetClass()->FindInstanceField("array", "[Ljava/lang/Object;"); - mirror::ArtField* size_field = groups_array_list->GetClass()->FindInstanceField("size", "I"); + mirror::ArtField* array_field = soa.DecodeField(WellKnownClasses::java_util_ArrayList_array); + mirror::ArtField* size_field = soa.DecodeField(WellKnownClasses::java_util_ArrayList_size); mirror::ObjectArray<mirror::Object>* groups_array = array_field->GetObject(groups_array_list)->AsObjectArray<mirror::Object>(); const int32_t size = size_field->GetInt(groups_array_list); @@ -4116,7 +4121,6 @@ class HeapChunkContext { HeapChunkContext(bool merge, bool native) : buf_(16384 - 16), type_(0), - merge_(merge), chunk_overhead_(0) { Reset(); if (native) { @@ -4327,7 +4331,6 @@ class HeapChunkContext { void* startOfNextMemoryChunk_; size_t totalAllocationUnits_; uint32_t type_; - bool merge_; bool needHeader_; size_t chunk_overhead_; @@ -4678,7 +4681,7 @@ static const char* GetMethodSourceFile(mirror::ArtMethod* method) * between the contents of these tables. */ jbyteArray Dbg::GetRecentAllocations() { - if (false) { + if ((false)) { DumpRecentAllocations(); } diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index f408386617..78cbe58416 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -23,7 +23,9 @@ #include <string.h> #include <sys/file.h> #include <sys/stat.h> + #include <memory> +#include <sstream> #include "base/logging.h" #include "base/stringprintf.h" diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc index 7e775f4ed3..af38433abc 100644 --- a/runtime/dex_instruction.cc +++ b/runtime/dex_instruction.cc @@ -19,6 +19,7 @@ #include <inttypes.h> #include <iomanip> +#include <sstream> #include "base/stringprintf.h" #include "dex_file-inl.h" diff --git a/runtime/dex_instruction_visitor_test.cc b/runtime/dex_instruction_visitor_test.cc index c5e63eb06e..5273084a9a 100644 --- a/runtime/dex_instruction_visitor_test.cc +++ b/runtime/dex_instruction_visitor_test.cc @@ -16,7 +16,6 @@ #include "dex_instruction_visitor.h" -#include <iostream> #include <memory> #include "gtest/gtest.h" diff --git a/runtime/dex_method_iterator_test.cc b/runtime/dex_method_iterator_test.cc index b8f180b46f..c6f333f675 100644 --- a/runtime/dex_method_iterator_test.cc +++ b/runtime/dex_method_iterator_test.cc @@ -38,8 +38,8 @@ TEST_F(DexMethodIteratorTest, Basic) { const DexFile& dex_file = it.GetDexFile(); InvokeType invoke_type = it.GetInvokeType(); uint32_t method_idx = it.GetMemberIndex(); - if (false) { - LG << invoke_type << " " << PrettyMethod(method_idx, dex_file); + if ((false)) { + LOG(INFO) << invoke_type << " " << PrettyMethod(method_idx, dex_file); } it.Next(); } diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index c3a25595cd..18053c3fb9 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -23,7 +23,10 @@ #include "base/logging.h" #include "base/stringprintf.h" #include "base/stl_util.h" +#include "base/unix_file/fd_file.h" #include "dwarf.h" +#include "elf_file_impl.h" +#include "elf_utils.h" #include "leb128.h" #include "utils.h" #include "instruction_set.h" @@ -1661,7 +1664,7 @@ struct PACKED(1) DebugLineHeader { } }; -class DebugLineInstructionIterator { +class DebugLineInstructionIterator FINAL { public: static DebugLineInstructionIterator* Create(DebugLineHeader* header, size_t section_size) { std::unique_ptr<DebugLineInstructionIterator> line_iter( @@ -1688,11 +1691,11 @@ class DebugLineInstructionIterator { } } - uint8_t* GetInstruction() { + uint8_t* GetInstruction() const { return current_instruction_; } - bool IsExtendedOpcode() { + bool IsExtendedOpcode() const { return header_->IsExtendedOpcode(current_instruction_); } @@ -1719,8 +1722,8 @@ class DebugLineInstructionIterator { : header_(header), last_instruction_(reinterpret_cast<uint8_t*>(header) + size), current_instruction_(header->GetDebugLineData()) {} - DebugLineHeader* header_; - uint8_t* last_instruction_; + DebugLineHeader* const header_; + uint8_t* const last_instruction_; uint8_t* current_instruction_; }; @@ -1781,9 +1784,8 @@ static int32_t FormLength(uint32_t att) { } } -class DebugTag { +class DebugTag FINAL { public: - const uint32_t index_; ~DebugTag() {} // Creates a new tag and moves data pointer up to the start of the next one. // nullptr means error. @@ -1820,14 +1822,18 @@ class DebugTag { return size_; } - bool HasChild() { + bool HasChild() const { return has_child_; } - uint32_t GetTagNumber() { + uint32_t GetTagNumber() const { return tag_; } + uint32_t GetIndex() const { + return index_; + } + // Gets the offset of a particular attribute in this tag structure. // Interpretation of the data is left to the consumer. 0 is returned if the // tag does not contain the attribute. @@ -1857,6 +1863,8 @@ class DebugTag { size_map_.insert(std::pair<uint32_t, uint32_t>(type, attr_size)); size_ += attr_size; } + + const uint32_t index_; std::map<uint32_t, uint32_t> off_map_; std::map<uint32_t, uint32_t> size_map_; uint32_t size_; @@ -1884,7 +1892,7 @@ class DebugAbbrev { if (tag.get() == nullptr) { return false; } else { - tags_.insert(std::pair<uint32_t, uint32_t>(tag->index_, tag_list_.size())); + tags_.insert(std::pair<uint32_t, uint32_t>(tag->GetIndex(), tag_list_.size())); tag_list_.push_back(std::move(tag)); } } @@ -1904,8 +1912,8 @@ class DebugAbbrev { private: DebugAbbrev(const uint8_t* begin, const uint8_t* end) : begin_(begin), end_(end) {} - const uint8_t* begin_; - const uint8_t* end_; + const uint8_t* const begin_; + const uint8_t* const end_; std::map<uint32_t, uint32_t> tags_; std::vector<std::unique_ptr<DebugTag>> tag_list_; }; @@ -1983,10 +1991,10 @@ class DebugInfoIterator { last_entry_(reinterpret_cast<uint8_t*>(header) + frame_size), current_entry_(reinterpret_cast<uint8_t*>(header) + sizeof(DebugInfoHeader)), current_tag_(abbrev_->ReadTag(current_entry_)) {} - DebugAbbrev* abbrev_; + DebugAbbrev* const abbrev_; DebugInfoHeader* current_cu_; DebugInfoHeader* next_cu_; - uint8_t* last_entry_; + uint8_t* const last_entry_; uint8_t* current_entry_; DebugTag* current_tag_; }; @@ -2406,24 +2414,15 @@ template class ElfFileImpl<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Word, template class ElfFileImpl<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Word, Elf64_Sword, Elf64_Addr, Elf64_Sym, Elf64_Rel, Elf64_Rela, Elf64_Dyn, Elf64_Off>; -ElfFile::ElfFile(ElfFileImpl32* elf32) : is_elf64_(false) { - CHECK_NE(elf32, static_cast<ElfFileImpl32*>(nullptr)); - elf_.elf32_ = elf32; +ElfFile::ElfFile(ElfFileImpl32* elf32) : elf32_(elf32), elf64_(nullptr) { } -ElfFile::ElfFile(ElfFileImpl64* elf64) : is_elf64_(true) { - CHECK_NE(elf64, static_cast<ElfFileImpl64*>(nullptr)); - elf_.elf64_ = elf64; +ElfFile::ElfFile(ElfFileImpl64* elf64) : elf32_(nullptr), elf64_(elf64) { } ElfFile::~ElfFile() { - if (is_elf64_) { - CHECK_NE(elf_.elf64_, static_cast<ElfFileImpl64*>(nullptr)); - delete elf_.elf64_; - } else { - CHECK_NE(elf_.elf32_, static_cast<ElfFileImpl32*>(nullptr)); - delete elf_.elf32_; - } + // Should never have 32 and 64-bit impls. + CHECK_NE(elf32_.get() == nullptr, elf64_.get() == nullptr); } ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only, std::string* error_msg) { @@ -2445,8 +2444,9 @@ ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only, std: return new ElfFile(elf_file_impl); } else if (header[EI_CLASS] == ELFCLASS32) { ElfFileImpl32* elf_file_impl = ElfFileImpl32::Open(file, writable, program_header_only, error_msg); - if (elf_file_impl == nullptr) + if (elf_file_impl == nullptr) { return nullptr; + } return new ElfFile(elf_file_impl); } else { *error_msg = StringPrintf("Failed to find expected EI_CLASS value %d or %d in %s, found %d", @@ -2471,13 +2471,15 @@ ElfFile* ElfFile::Open(File* file, int mmap_prot, int mmap_flags, std::string* e uint8_t* header = map->Begin(); if (header[EI_CLASS] == ELFCLASS64) { ElfFileImpl64* elf_file_impl = ElfFileImpl64::Open(file, mmap_prot, mmap_flags, error_msg); - if (elf_file_impl == nullptr) + if (elf_file_impl == nullptr) { return nullptr; + } return new ElfFile(elf_file_impl); } else if (header[EI_CLASS] == ELFCLASS32) { ElfFileImpl32* elf_file_impl = ElfFileImpl32::Open(file, mmap_prot, mmap_flags, error_msg); - if (elf_file_impl == nullptr) + if (elf_file_impl == nullptr) { return nullptr; + } return new ElfFile(elf_file_impl); } else { *error_msg = StringPrintf("Failed to find expected EI_CLASS value %d or %d in %s, found %d", @@ -2489,12 +2491,11 @@ ElfFile* ElfFile::Open(File* file, int mmap_prot, int mmap_flags, std::string* e } #define DELEGATE_TO_IMPL(func, ...) \ - if (is_elf64_) { \ - CHECK_NE(elf_.elf64_, static_cast<ElfFileImpl64*>(nullptr)); \ - return elf_.elf64_->func(__VA_ARGS__); \ + if (elf64_.get() != nullptr) { \ + return elf64_->func(__VA_ARGS__); \ } else { \ - CHECK_NE(elf_.elf32_, static_cast<ElfFileImpl32*>(nullptr)); \ - return elf_.elf32_->func(__VA_ARGS__); \ + DCHECK(elf32_.get() != nullptr); \ + return elf32_->func(__VA_ARGS__); \ } bool ElfFile::Load(bool executable, std::string* error_msg) { @@ -2522,29 +2523,31 @@ const File& ElfFile::GetFile() const { } bool ElfFile::GetSectionOffsetAndSize(const char* section_name, uint64_t* offset, uint64_t* size) { - if (is_elf64_) { - CHECK_NE(elf_.elf64_, static_cast<ElfFileImpl64*>(nullptr)); + if (elf32_.get() == nullptr) { + CHECK(elf64_.get() != nullptr); - Elf64_Shdr *shdr = elf_.elf64_->FindSectionByName(section_name); - if (shdr == nullptr) + Elf64_Shdr *shdr = elf64_->FindSectionByName(section_name); + if (shdr == nullptr) { return false; - - if (offset != nullptr) + } + if (offset != nullptr) { *offset = shdr->sh_offset; - if (size != nullptr) + } + if (size != nullptr) { *size = shdr->sh_size; + } return true; } else { - CHECK_NE(elf_.elf32_, static_cast<ElfFileImpl32*>(nullptr)); - - Elf32_Shdr *shdr = elf_.elf32_->FindSectionByName(section_name); - if (shdr == nullptr) + Elf32_Shdr *shdr = elf32_->FindSectionByName(section_name); + if (shdr == nullptr) { return false; - - if (offset != nullptr) + } + if (offset != nullptr) { *offset = shdr->sh_offset; - if (size != nullptr) + } + if (size != nullptr) { *size = shdr->sh_size; + } return true; } } @@ -2565,26 +2568,14 @@ bool ElfFile::Strip(File* file, std::string* error_msg) { return false; } - if (elf_file->is_elf64_) - return elf_file->elf_.elf64_->Strip(error_msg); + if (elf_file->elf64_.get() != nullptr) + return elf_file->elf64_->Strip(error_msg); else - return elf_file->elf_.elf32_->Strip(error_msg); + return elf_file->elf32_->Strip(error_msg); } bool ElfFile::Fixup(uintptr_t base_address) { DELEGATE_TO_IMPL(Fixup, base_address); } -ElfFileImpl32* ElfFile::GetImpl32() const { - CHECK(!is_elf64_); - CHECK_NE(elf_.elf32_, static_cast<ElfFileImpl32*>(nullptr)); - return elf_.elf32_; -} - -ElfFileImpl64* ElfFile::GetImpl64() const { - CHECK(is_elf64_); - CHECK_NE(elf_.elf64_, static_cast<ElfFileImpl64*>(nullptr)); - return elf_.elf64_; -} - } // namespace art diff --git a/runtime/elf_file.h b/runtime/elf_file.h index a7f3056cae..10d6360fcc 100644 --- a/runtime/elf_file.h +++ b/runtime/elf_file.h @@ -17,12 +17,25 @@ #ifndef ART_RUNTIME_ELF_FILE_H_ #define ART_RUNTIME_ELF_FILE_H_ +#include <memory> #include <string> -#include "base/unix_file/fd_file.h" -#include "elf_file_impl.h" +#include "base/macros.h" +// Explicitly include our own elf.h to avoid Linux and other dependencies. +#include "./elf.h" +#include "os.h" namespace art { +template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word, + typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel, + typename Elf_Rela, typename Elf_Dyn, typename Elf_Off> +class ElfFileImpl; + +// Explicitly instantiated in elf_file.cc +typedef ElfFileImpl<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Word, Elf32_Sword, + Elf32_Addr, Elf32_Sym, Elf32_Rel, Elf32_Rela, Elf32_Dyn, Elf32_Off> ElfFileImpl32; +typedef ElfFileImpl<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Word, Elf64_Sword, + Elf64_Addr, Elf64_Sym, Elf64_Rel, Elf64_Rela, Elf64_Dyn, Elf64_Off> ElfFileImpl64; // Used for compile time and runtime for ElfFile access. Because of // the need for use at runtime, cannot directly use LLVM classes such as @@ -35,8 +48,6 @@ class ElfFile { static ElfFile* Open(File* file, int mmap_prot, int mmap_flags, std::string* error_msg); ~ElfFile(); - const bool is_elf64_; - // Load segments into memory based on PT_LOAD program headers bool Load(bool executable, std::string* error_msg); @@ -68,17 +79,26 @@ class ElfFile { bool Fixup(uintptr_t base_address); - ElfFileImpl32* GetImpl32() const; - ElfFileImpl64* GetImpl64() const; + bool Is64Bit() const { + return elf64_.get() != nullptr; + } + + ElfFileImpl32* GetImpl32() const { + return elf32_.get(); + } + + ElfFileImpl64* GetImpl64() const { + return elf64_.get(); + } private: explicit ElfFile(ElfFileImpl32* elf32); explicit ElfFile(ElfFileImpl64* elf64); - union ElfFileContainer { - ElfFileImpl32* elf32_; - ElfFileImpl64* elf64_; - } elf_; + const std::unique_ptr<ElfFileImpl32> elf32_; + const std::unique_ptr<ElfFileImpl64> elf64_; + + DISALLOW_COPY_AND_ASSIGN(ElfFile); }; } // namespace art diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h index a2fc422cea..a8bb465817 100644 --- a/runtime/elf_file_impl.h +++ b/runtime/elf_file_impl.h @@ -21,11 +21,9 @@ #include <memory> #include <vector> -#include "base/unix_file/fd_file.h" -#include "globals.h" -#include "elf_utils.h" +// Explicitly include our own elf.h to avoid Linux and other dependencies. +#include "./elf.h" #include "mem_map.h" -#include "os.h" namespace art { @@ -207,13 +205,9 @@ class ElfFileImpl { Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>> gdb_file_mapping_; void GdbJITSupport(); -}; -// Explicitly instantiated in elf_file.cc -typedef ElfFileImpl<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Word, Elf32_Sword, - Elf32_Addr, Elf32_Sym, Elf32_Rel, Elf32_Rela, Elf32_Dyn, Elf32_Off> ElfFileImpl32; -typedef ElfFileImpl<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Word, Elf64_Sword, - Elf64_Addr, Elf64_Sym, Elf64_Rel, Elf64_Rela, Elf64_Dyn, Elf64_Off> ElfFileImpl64; + DISALLOW_COPY_AND_ASSIGN(ElfFileImpl); +}; } // namespace art diff --git a/runtime/elf_utils.h b/runtime/elf_utils.h index 5966d05996..7b00bada47 100644 --- a/runtime/elf_utils.h +++ b/runtime/elf_utils.h @@ -24,6 +24,8 @@ #include "base/logging.h" +namespace art { + // Architecture dependent flags for the ELF header. #define EF_ARM_EABI_VER5 0x05000000 #define EF_MIPS_ABI_O32 0x00001000 @@ -67,11 +69,11 @@ // Patching section type #define SHT_OAT_PATCH SHT_LOUSER -inline void SetBindingAndType(Elf32_Sym* sym, unsigned char b, unsigned char t) { +static inline void SetBindingAndType(Elf32_Sym* sym, unsigned char b, unsigned char t) { sym->st_info = (b << 4) + (t & 0x0f); } -inline bool IsDynamicSectionPointer(Elf32_Word d_tag, Elf32_Word e_machine) { +static inline bool IsDynamicSectionPointer(Elf32_Word d_tag, Elf32_Word e_machine) { switch (d_tag) { // case 1: well known d_tag values that imply Elf32_Dyn.d_un contains an address in d_ptr case DT_PLTGOT: @@ -163,4 +165,6 @@ inline bool IsDynamicSectionPointer(Elf32_Word d_tag, Elf32_Word e_machine) { } } +} // namespace art + #endif // ART_RUNTIME_ELF_UTILS_H_ diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index ce34993950..c46d8871d5 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -183,59 +183,6 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons bool FillArrayData(mirror::Object* obj, const Instruction::ArrayDataPayload* payload) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -// Entry point for deoptimization. -extern "C" void art_quick_deoptimize(); -static inline uintptr_t GetQuickDeoptimizationEntryPoint() { - return reinterpret_cast<uintptr_t>(art_quick_deoptimize); -} - -// Return address of instrumentation stub. -extern "C" void art_quick_instrumentation_entry(void*); -static inline void* GetQuickInstrumentationEntryPoint() { - return reinterpret_cast<void*>(art_quick_instrumentation_entry); -} - -// The return_pc of instrumentation exit stub. -extern "C" void art_quick_instrumentation_exit(); -static inline uintptr_t GetQuickInstrumentationExitPc() { - return reinterpret_cast<uintptr_t>(art_quick_instrumentation_exit); -} - -extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*); -static inline const void* GetPortableToInterpreterBridge() { - return reinterpret_cast<void*>(art_portable_to_interpreter_bridge); -} - -static inline const void* GetPortableToQuickBridge() { - // TODO: portable to quick bridge. Bug: 8196384 - return GetPortableToInterpreterBridge(); -} - -extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*); -static inline const void* GetQuickToInterpreterBridge() { - return reinterpret_cast<void*>(art_quick_to_interpreter_bridge); -} - -static inline const void* GetQuickToPortableBridge() { - // TODO: quick to portable bridge. Bug: 8196384 - return GetQuickToInterpreterBridge(); -} - -extern "C" void art_portable_proxy_invoke_handler(); -static inline const void* GetPortableProxyInvokeHandler() { - return reinterpret_cast<void*>(art_portable_proxy_invoke_handler); -} - -extern "C" void art_quick_proxy_invoke_handler(); -static inline const void* GetQuickProxyInvokeHandler() { - return reinterpret_cast<void*>(art_quick_proxy_invoke_handler); -} - -extern "C" void* art_jni_dlsym_lookup_stub(JNIEnv*, jobject); -static inline void* GetJniDlsymLookupStub() { - return reinterpret_cast<void*>(art_jni_dlsym_lookup_stub); -} - template <typename INT_TYPE, typename FLOAT_TYPE> static inline INT_TYPE art_float_to_integral(FLOAT_TYPE f); diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc index b617636d13..908d3cd43c 100644 --- a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc +++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc @@ -25,7 +25,7 @@ namespace art { // TODO: Make the MethodHelper here be compaction safe. -extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& mh, +extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper* mh, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result) { mirror::ArtMethod* method = shadow_frame->GetMethod(); @@ -54,7 +54,7 @@ extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& m } else { method->Invoke(self, shadow_frame->GetVRegArgs(arg_offset), (shadow_frame->NumberOfVRegs() - arg_offset) * sizeof(uint32_t), - result, mh.GetShorty()); + result, mh->GetShorty()); } } diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.h b/runtime/entrypoints/interpreter/interpreter_entrypoints.h index d8b22046de..5d646e905f 100644 --- a/runtime/entrypoints/interpreter/interpreter_entrypoints.h +++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.h @@ -33,10 +33,10 @@ class Thread; // Pointers to functions that are called by interpreter trampolines via thread-local storage. struct PACKED(4) InterpreterEntryPoints { - void (*pInterpreterToInterpreterBridge)(Thread* self, MethodHelper& mh, + void (*pInterpreterToInterpreterBridge)(Thread* self, MethodHelper* mh, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result); - void (*pInterpreterToCompiledCodeBridge)(Thread* self, MethodHelper& mh, + void (*pInterpreterToCompiledCodeBridge)(Thread* self, MethodHelper* mh, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result); }; diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc index edb3b723ef..2752407750 100644 --- a/runtime/entrypoints/jni/jni_entrypoints.cc +++ b/runtime/entrypoints/jni/jni_entrypoints.cc @@ -45,7 +45,7 @@ extern "C" void* artFindNativeMethod(Thread* self) { return NULL; } else { // Register so that future calls don't come here - method->RegisterNative(self, native_code, false); + method->RegisterNative(native_code, false); return native_code; } } diff --git a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc b/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc index 642c94a01e..c3664bfac6 100644 --- a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc @@ -19,6 +19,7 @@ #include "dex_instruction-inl.h" #include "entrypoints/entrypoint_utils-inl.h" +#include "entrypoints/runtime_asm_entrypoints.h" #include "interpreter/interpreter.h" #include "mirror/art_method-inl.h" #include "mirror/object-inl.h" @@ -222,7 +223,7 @@ extern "C" uint64_t artPortableToInterpreterBridge(mirror::ArtMethod* method, Th } } - JValue result = interpreter::EnterInterpreterFromStub(self, mh, code_item, *shadow_frame); + JValue result = interpreter::EnterInterpreterFromEntryPoint(self, &mh, code_item, shadow_frame); // Pop transition. self->PopManagedStackFragment(fragment); return result.GetJ(); @@ -323,7 +324,7 @@ extern "C" const void* artPortableResolutionTrampoline(mirror::ArtMethod* called uint32_t dex_pc; mirror::ArtMethod* caller = self->GetCurrentMethod(&dex_pc); - ClassLinker* linker = Runtime::Current()->GetClassLinker(); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); InvokeType invoke_type; bool is_range; if (called->IsRuntimeMethod()) { @@ -379,7 +380,7 @@ extern "C" const void* artPortableResolutionTrampoline(mirror::ArtMethod* called is_range = true; } uint32_t dex_method_idx = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c(); - called = linker->ResolveMethod(Thread::Current(), dex_method_idx, &caller, invoke_type); + called = class_linker->ResolveMethod(Thread::Current(), dex_method_idx, &caller, invoke_type); // Incompatible class change should have been handled in resolve method. CHECK(!called->CheckIncompatibleClassChange(invoke_type)); // Refine called method based on receiver. @@ -399,27 +400,27 @@ extern "C" const void* artPortableResolutionTrampoline(mirror::ArtMethod* called // Ensure that the called method's class is initialized. StackHandleScope<1> hs(self); Handle<mirror::Class> called_class(hs.NewHandle(called->GetDeclaringClass())); - linker->EnsureInitialized(self, called_class, true, true); + class_linker->EnsureInitialized(self, called_class, true, true); if (LIKELY(called_class->IsInitialized())) { code = called->GetEntryPointFromPortableCompiledCode(); // TODO: remove this after we solve the link issue. if (code == nullptr) { bool have_portable_code; - code = linker->GetPortableOatCodeFor(called, &have_portable_code); + code = class_linker->GetPortableOatCodeFor(called, &have_portable_code); } } else if (called_class->IsInitializing()) { if (invoke_type == kStatic) { // Class is still initializing, go to oat and grab code (trampoline must be left in place // until class is initialized to stop races between threads). bool have_portable_code; - code = linker->GetPortableOatCodeFor(called, &have_portable_code); + code = class_linker->GetPortableOatCodeFor(called, &have_portable_code); } else { // No trampoline for non-static methods. code = called->GetEntryPointFromPortableCompiledCode(); // TODO: remove this after we solve the link issue. if (code == nullptr) { bool have_portable_code; - code = linker->GetPortableOatCodeFor(called, &have_portable_code); + code = class_linker->GetPortableOatCodeFor(called, &have_portable_code); } } } else { @@ -430,7 +431,7 @@ extern "C" const void* artPortableResolutionTrampoline(mirror::ArtMethod* called // Expect class to at least be initializing. DCHECK(called->GetDeclaringClass()->IsInitializing()); // Don't want infinite recursion. - DCHECK(code != linker->GetPortableResolutionTrampoline()); + DCHECK(!class_linker->IsPortableResolutionStub(code)); // Set up entry into main method *called_addr = called; } diff --git a/runtime/entrypoints/quick/callee_save_frame.h b/runtime/entrypoints/quick/callee_save_frame.h index e728f7dd11..49357ad276 100644 --- a/runtime/entrypoints/quick/callee_save_frame.h +++ b/runtime/entrypoints/quick/callee_save_frame.h @@ -18,9 +18,8 @@ #define ART_RUNTIME_ENTRYPOINTS_QUICK_CALLEE_SAVE_FRAME_H_ #include "base/mutex.h" -#include "gc_root-inl.h" #include "instruction_set.h" -#include "runtime-inl.h" +#include "runtime.h" #include "thread-inl.h" // Specific frame size code is in architecture-specific files. We include this to compile-time @@ -36,16 +35,41 @@ namespace mirror { class ArtMethod; } // namespace mirror -// Place a special frame at the TOS that will save the callee saves for the given type. -static inline void FinishCalleeSaveFrameSetup(Thread* self, StackReference<mirror::ArtMethod>* sp, - Runtime::CalleeSaveType type) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // Be aware the store below may well stomp on an incoming argument. - Locks::mutator_lock_->AssertSharedHeld(self); - sp->Assign(Runtime::Current()->GetCalleeSaveMethod(type)); - self->SetTopOfStack(sp, 0); - self->VerifyStack(); -} +class ScopedQuickEntrypointChecks { + public: + explicit ScopedQuickEntrypointChecks(Thread *self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : self_(self) { + if (kIsDebugBuild) { + TestsOnEntry(); + } + } + + explicit ScopedQuickEntrypointChecks() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : self_(kIsDebugBuild ? Thread::Current() : nullptr) { + if (kIsDebugBuild) { + TestsOnEntry(); + } + } + + ~ScopedQuickEntrypointChecks() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (kIsDebugBuild) { + TestsOnExit(); + } + } + + private: + void TestsOnEntry() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Locks::mutator_lock_->AssertSharedHeld(self_); + self_->VerifyStack(); + } + + void TestsOnExit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Locks::mutator_lock_->AssertSharedHeld(self_); + self_->VerifyStack(); + } + + Thread* const self_; +}; static constexpr size_t GetCalleeSaveFrameSize(InstructionSet isa, Runtime::CalleeSaveType type) { // constexpr must be a return statement. @@ -71,7 +95,8 @@ static constexpr size_t GetConstExprPointerSize(InstructionSet isa) { } // Note: this specialized statement is sanity-checked in the quick-trampoline gtest. -static constexpr size_t GetCalleeSavePCOffset(InstructionSet isa, Runtime::CalleeSaveType type) { +static constexpr size_t GetCalleeSaveReturnPcOffset(InstructionSet isa, + Runtime::CalleeSaveType type) { return GetCalleeSaveFrameSize(isa, type) - GetConstExprPointerSize(isa); } diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index d8da463981..a2869ecc45 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -32,6 +32,7 @@ extern "C" mirror::Object* artAllocObjectFromCode ##suffix##suffix2( \ uint32_t type_idx, mirror::ArtMethod* method, Thread* self, \ StackReference<mirror::ArtMethod>* sp) \ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \ + ScopedQuickEntrypointChecks sqec(self); \ if (kUseTlabFastPath && !instrumented_bool && allocator_type == gc::kAllocatorTypeTLAB) { \ mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx); \ if (LIKELY(klass != nullptr && klass->IsInitialized() && !klass->IsFinalizable())) { \ @@ -53,13 +54,12 @@ extern "C" mirror::Object* artAllocObjectFromCode ##suffix##suffix2( \ } \ } \ } \ - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \ return AllocObjectFromCode<false, instrumented_bool>(type_idx, method, self, allocator_type); \ } \ extern "C" mirror::Object* artAllocObjectFromCodeResolved##suffix##suffix2( \ - mirror::Class* klass, mirror::ArtMethod* method, Thread* self, \ - StackReference<mirror::ArtMethod>* sp) \ + mirror::Class* klass, mirror::ArtMethod* method, Thread* self) \ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \ + ScopedQuickEntrypointChecks sqec(self); \ if (kUseTlabFastPath && !instrumented_bool && allocator_type == gc::kAllocatorTypeTLAB) { \ if (LIKELY(klass->IsInitialized())) { \ size_t byte_count = klass->GetObjectSize(); \ @@ -80,13 +80,12 @@ extern "C" mirror::Object* artAllocObjectFromCodeResolved##suffix##suffix2( \ } \ } \ } \ - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \ return AllocObjectFromCodeResolved<instrumented_bool>(klass, method, self, allocator_type); \ } \ extern "C" mirror::Object* artAllocObjectFromCodeInitialized##suffix##suffix2( \ - mirror::Class* klass, mirror::ArtMethod* method, Thread* self, \ - StackReference<mirror::ArtMethod>* sp) \ + mirror::Class* klass, mirror::ArtMethod* method, Thread* self) \ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \ + ScopedQuickEntrypointChecks sqec(self); \ if (kUseTlabFastPath && !instrumented_bool && allocator_type == gc::kAllocatorTypeTLAB) { \ size_t byte_count = klass->GetObjectSize(); \ byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment); \ @@ -105,45 +104,39 @@ extern "C" mirror::Object* artAllocObjectFromCodeInitialized##suffix##suffix2( \ return obj; \ } \ } \ - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \ return AllocObjectFromCodeInitialized<instrumented_bool>(klass, method, self, allocator_type); \ } \ extern "C" mirror::Object* artAllocObjectFromCodeWithAccessCheck##suffix##suffix2( \ - uint32_t type_idx, mirror::ArtMethod* method, Thread* self, \ - StackReference<mirror::ArtMethod>* sp) \ + uint32_t type_idx, mirror::ArtMethod* method, Thread* self) \ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \ - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \ + ScopedQuickEntrypointChecks sqec(self); \ return AllocObjectFromCode<true, instrumented_bool>(type_idx, method, self, allocator_type); \ } \ extern "C" mirror::Array* artAllocArrayFromCode##suffix##suffix2( \ - uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self, \ - StackReference<mirror::ArtMethod>* sp) \ + uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self) \ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \ - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \ + ScopedQuickEntrypointChecks sqec(self); \ return AllocArrayFromCode<false, instrumented_bool>(type_idx, method, component_count, self, \ allocator_type); \ } \ extern "C" mirror::Array* artAllocArrayFromCodeResolved##suffix##suffix2( \ - mirror::Class* klass, mirror::ArtMethod* method, int32_t component_count, Thread* self, \ - StackReference<mirror::ArtMethod>* sp) \ + mirror::Class* klass, mirror::ArtMethod* method, int32_t component_count, Thread* self) \ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \ - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \ + ScopedQuickEntrypointChecks sqec(self); \ return AllocArrayFromCodeResolved<false, instrumented_bool>(klass, method, component_count, self, \ allocator_type); \ } \ extern "C" mirror::Array* artAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \ - uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self, \ - StackReference<mirror::ArtMethod>* sp) \ + uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self) \ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \ - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \ + ScopedQuickEntrypointChecks sqec(self); \ return AllocArrayFromCode<true, instrumented_bool>(type_idx, method, component_count, self, \ allocator_type); \ } \ extern "C" mirror::Array* artCheckAndAllocArrayFromCode##suffix##suffix2( \ - uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self, \ - StackReference<mirror::ArtMethod>* sp) \ + uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self) \ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \ - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \ + ScopedQuickEntrypointChecks sqec(self); \ if (!instrumented_bool) { \ return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, false, allocator_type); \ } else { \ @@ -151,10 +144,9 @@ extern "C" mirror::Array* artCheckAndAllocArrayFromCode##suffix##suffix2( \ } \ } \ extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \ - uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self, \ - StackReference<mirror::ArtMethod>* sp) \ + uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self) \ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \ - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \ + ScopedQuickEntrypointChecks sqec(self); \ if (!instrumented_bool) { \ return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, true, allocator_type); \ } else { \ diff --git a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc index f9f62c2721..14ab320b97 100644 --- a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc @@ -27,9 +27,8 @@ namespace art { -extern "C" void artDeoptimize(Thread* self, StackReference<mirror::ArtMethod>* sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); +extern "C" void artDeoptimize(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); self->SetException(ThrowLocation(), Thread::GetDeoptimizationException()); self->QuickDeliverException(); } diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index 704db0503f..2e7c8bab43 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -27,42 +27,39 @@ namespace art { extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, mirror::ArtMethod* referrer, - Thread* self, - StackReference<mirror::ArtMethod>* sp) + Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // Called to ensure static storage base is initialized for direct static field reads and writes. // A class may be accessing another class' fields when it doesn't have access, as access has been // given by inheritance. - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); + ScopedQuickEntrypointChecks sqec(self); return ResolveVerifyAndClinit(type_idx, referrer, self, true, false); } extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, mirror::ArtMethod* referrer, - Thread* self, - StackReference<mirror::ArtMethod>* sp) + Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // Called when method->dex_cache_resolved_types_[] misses. - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); + ScopedQuickEntrypointChecks sqec(self); return ResolveVerifyAndClinit(type_idx, referrer, self, false, false); } extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx, - mirror::ArtMethod* referrer, - Thread* self, - StackReference<mirror::ArtMethod>* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::ArtMethod* referrer, + Thread* self) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // Called when caller isn't guaranteed to have access to a type and the dex cache may be // unpopulated. - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); + ScopedQuickEntrypointChecks sqec(self); return ResolveVerifyAndClinit(type_idx, referrer, self, false, true); } extern "C" mirror::String* artResolveStringFromCode(mirror::ArtMethod* referrer, int32_t string_idx, - Thread* self, - StackReference<mirror::ArtMethod>* sp) + Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); + ScopedQuickEntrypointChecks sqec(self); return ResolveStringFromCode(referrer, string_idx); } diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc index b89c015cd6..7326fcfc69 100644 --- a/runtime/entrypoints/quick/quick_field_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc @@ -25,295 +25,284 @@ namespace art { -extern "C" int8_t artGetByteStaticFromCode(uint32_t field_idx, - mirror::ArtMethod* referrer, - Thread* self, StackReference<mirror::ArtMethod>* sp) +extern "C" int8_t artGetByteStaticFromCode(uint32_t field_idx, mirror::ArtMethod* referrer, + Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(int8_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { return field->GetByte(field->GetDeclaringClass()); } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int8_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { return field->GetByte(field->GetDeclaringClass()); } - return 0; // Will throw exception by checking with Thread::Current + return 0; // Will throw exception by checking with Thread::Current. } -extern "C" uint8_t artGetBooleanStaticFromCode(uint32_t field_idx, - mirror::ArtMethod* referrer, - Thread* self, StackReference<mirror::ArtMethod>* sp) +extern "C" uint8_t artGetBooleanStaticFromCode(uint32_t field_idx, mirror::ArtMethod* referrer, + Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(int8_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { return field->GetBoolean(field->GetDeclaringClass()); } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int8_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { return field->GetBoolean(field->GetDeclaringClass()); } - return 0; // Will throw exception by checking with Thread::Current + return 0; // Will throw exception by checking with Thread::Current. } -extern "C" int16_t artGetShortStaticFromCode(uint32_t field_idx, - mirror::ArtMethod* referrer, - Thread* self, StackReference<mirror::ArtMethod>* sp) +extern "C" int16_t artGetShortStaticFromCode(uint32_t field_idx, mirror::ArtMethod* referrer, + Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(int16_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { return field->GetShort(field->GetDeclaringClass()); } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int16_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { return field->GetShort(field->GetDeclaringClass()); } - return 0; // Will throw exception by checking with Thread::Current + return 0; // Will throw exception by checking with Thread::Current. } extern "C" uint16_t artGetCharStaticFromCode(uint32_t field_idx, mirror::ArtMethod* referrer, - Thread* self, StackReference<mirror::ArtMethod>* sp) + Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(int16_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { return field->GetChar(field->GetDeclaringClass()); } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int16_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { return field->GetChar(field->GetDeclaringClass()); } - return 0; // Will throw exception by checking with Thread::Current + return 0; // Will throw exception by checking with Thread::Current. } extern "C" uint32_t artGet32StaticFromCode(uint32_t field_idx, mirror::ArtMethod* referrer, - Thread* self, StackReference<mirror::ArtMethod>* sp) + Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(int32_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { return field->Get32(field->GetDeclaringClass()); } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int32_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { return field->Get32(field->GetDeclaringClass()); } - return 0; // Will throw exception by checking with Thread::Current + return 0; // Will throw exception by checking with Thread::Current. } extern "C" uint64_t artGet64StaticFromCode(uint32_t field_idx, mirror::ArtMethod* referrer, - Thread* self, StackReference<mirror::ArtMethod>* sp) + Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(int64_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { return field->Get64(field->GetDeclaringClass()); } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int64_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { return field->Get64(field->GetDeclaringClass()); } - return 0; // Will throw exception by checking with Thread::Current + return 0; // Will throw exception by checking with Thread::Current. } extern "C" mirror::Object* artGetObjStaticFromCode(uint32_t field_idx, mirror::ArtMethod* referrer, - Thread* self, - StackReference<mirror::ArtMethod>* sp) + Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticObjectRead, sizeof(mirror::HeapReference<mirror::Object>)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { return field->GetObj(field->GetDeclaringClass()); } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<StaticObjectRead, true>(field_idx, referrer, self, sizeof(mirror::HeapReference<mirror::Object>)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { return field->GetObj(field->GetDeclaringClass()); } - return NULL; // Will throw exception by checking with Thread::Current + return nullptr; // Will throw exception by checking with Thread::Current. } extern "C" int8_t artGetByteInstanceFromCode(uint32_t field_idx, mirror::Object* obj, - mirror::ArtMethod* referrer, Thread* self, - StackReference<mirror::ArtMethod>* sp) + mirror::ArtMethod* referrer, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(int8_t)); - if (LIKELY(field != NULL && obj != NULL)) { + if (LIKELY(field != nullptr && obj != nullptr)) { return field->GetByte(obj); } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self, sizeof(int8_t)); - if (LIKELY(field != NULL)) { - if (UNLIKELY(obj == NULL)) { + if (LIKELY(field != nullptr)) { + if (UNLIKELY(obj == nullptr)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionForFieldAccess(throw_location, field, true); } else { return field->GetByte(obj); } } - return 0; // Will throw exception by checking with Thread::Current + return 0; // Will throw exception by checking with Thread::Current. } extern "C" uint8_t artGetBooleanInstanceFromCode(uint32_t field_idx, mirror::Object* obj, - mirror::ArtMethod* referrer, Thread* self, - StackReference<mirror::ArtMethod>* sp) + mirror::ArtMethod* referrer, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(int8_t)); - if (LIKELY(field != NULL && obj != NULL)) { + if (LIKELY(field != nullptr && obj != nullptr)) { return field->GetBoolean(obj); } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self, sizeof(int8_t)); - if (LIKELY(field != NULL)) { - if (UNLIKELY(obj == NULL)) { + if (LIKELY(field != nullptr)) { + if (UNLIKELY(obj == nullptr)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionForFieldAccess(throw_location, field, true); } else { return field->GetBoolean(obj); } } - return 0; // Will throw exception by checking with Thread::Current + return 0; // Will throw exception by checking with Thread::Current. } extern "C" int16_t artGetShortInstanceFromCode(uint32_t field_idx, mirror::Object* obj, - mirror::ArtMethod* referrer, Thread* self, - StackReference<mirror::ArtMethod>* sp) + mirror::ArtMethod* referrer, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(int16_t)); - if (LIKELY(field != NULL && obj != NULL)) { + if (LIKELY(field != nullptr && obj != nullptr)) { return field->GetShort(obj); } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self, sizeof(int16_t)); - if (LIKELY(field != NULL)) { - if (UNLIKELY(obj == NULL)) { + if (LIKELY(field != nullptr)) { + if (UNLIKELY(obj == nullptr)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionForFieldAccess(throw_location, field, true); } else { return field->GetShort(obj); } } - return 0; // Will throw exception by checking with Thread::Current + return 0; // Will throw exception by checking with Thread::Current. } extern "C" uint16_t artGetCharInstanceFromCode(uint32_t field_idx, mirror::Object* obj, - mirror::ArtMethod* referrer, Thread* self, - StackReference<mirror::ArtMethod>* sp) + mirror::ArtMethod* referrer, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(int16_t)); - if (LIKELY(field != NULL && obj != NULL)) { + if (LIKELY(field != nullptr && obj != nullptr)) { return field->GetChar(obj); } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self, sizeof(int16_t)); - if (LIKELY(field != NULL)) { - if (UNLIKELY(obj == NULL)) { + if (LIKELY(field != nullptr)) { + if (UNLIKELY(obj == nullptr)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionForFieldAccess(throw_location, field, true); } else { return field->GetChar(obj); } } - return 0; // Will throw exception by checking with Thread::Current + return 0; // Will throw exception by checking with Thread::Current. } extern "C" uint32_t artGet32InstanceFromCode(uint32_t field_idx, mirror::Object* obj, - mirror::ArtMethod* referrer, Thread* self, - StackReference<mirror::ArtMethod>* sp) + mirror::ArtMethod* referrer, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(int32_t)); - if (LIKELY(field != NULL && obj != NULL)) { + if (LIKELY(field != nullptr && obj != nullptr)) { return field->Get32(obj); } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self, sizeof(int32_t)); - if (LIKELY(field != NULL)) { - if (UNLIKELY(obj == NULL)) { + if (LIKELY(field != nullptr)) { + if (UNLIKELY(obj == nullptr)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionForFieldAccess(throw_location, field, true); } else { return field->Get32(obj); } } - return 0; // Will throw exception by checking with Thread::Current + return 0; // Will throw exception by checking with Thread::Current. } extern "C" uint64_t artGet64InstanceFromCode(uint32_t field_idx, mirror::Object* obj, - mirror::ArtMethod* referrer, Thread* self, - StackReference<mirror::ArtMethod>* sp) + mirror::ArtMethod* referrer, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(int64_t)); - if (LIKELY(field != NULL && obj != NULL)) { + if (LIKELY(field != nullptr && obj != nullptr)) { return field->Get64(obj); } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self, sizeof(int64_t)); - if (LIKELY(field != NULL)) { - if (UNLIKELY(obj == NULL)) { + if (LIKELY(field != nullptr)) { + if (UNLIKELY(obj == nullptr)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionForFieldAccess(throw_location, field, true); } else { return field->Get64(obj); } } - return 0; // Will throw exception by checking with Thread::Current + return 0; // Will throw exception by checking with Thread::Current. } extern "C" mirror::Object* artGetObjInstanceFromCode(uint32_t field_idx, mirror::Object* obj, mirror::ArtMethod* referrer, - Thread* self, - StackReference<mirror::ArtMethod>* sp) + Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectRead, sizeof(mirror::HeapReference<mirror::Object>)); - if (LIKELY(field != NULL && obj != NULL)) { + if (LIKELY(field != nullptr && obj != nullptr)) { return field->GetObj(obj); } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<InstanceObjectRead, true>(field_idx, referrer, self, sizeof(mirror::HeapReference<mirror::Object>)); - if (LIKELY(field != NULL)) { - if (UNLIKELY(obj == NULL)) { + if (LIKELY(field != nullptr)) { + if (UNLIKELY(obj == nullptr)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionForFieldAccess(throw_location, field, true); } else { return field->GetObj(obj); } } - return NULL; // Will throw exception by checking with Thread::Current + return nullptr; // Will throw exception by checking with Thread::Current. } extern "C" int artSet8StaticFromCode(uint32_t field_idx, uint32_t new_value, - mirror::ArtMethod* referrer, Thread* self, - StackReference<mirror::ArtMethod>* sp) + mirror::ArtMethod* referrer, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(int8_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { Primitive::Type type = field->GetTypeAsPrimitiveType(); // Compiled code can't use transactional mode. if (type == Primitive::kPrimBoolean) { @@ -324,9 +313,8 @@ extern "C" int artSet8StaticFromCode(uint32_t field_idx, uint32_t new_value, } return 0; // success } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int8_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { Primitive::Type type = field->GetTypeAsPrimitiveType(); // Compiled code can't use transactional mode. if (type == Primitive::kPrimBoolean) { @@ -341,12 +329,12 @@ extern "C" int artSet8StaticFromCode(uint32_t field_idx, uint32_t new_value, } extern "C" int artSet16StaticFromCode(uint32_t field_idx, uint16_t new_value, - mirror::ArtMethod* referrer, Thread* self, - StackReference<mirror::ArtMethod>* sp) + mirror::ArtMethod* referrer, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(int16_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { Primitive::Type type = field->GetTypeAsPrimitiveType(); // Compiled code can't use transactional mode. if (type == Primitive::kPrimChar) { @@ -357,9 +345,8 @@ extern "C" int artSet16StaticFromCode(uint32_t field_idx, uint16_t new_value, } return 0; // success } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int16_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { Primitive::Type type = field->GetTypeAsPrimitiveType(); // Compiled code can't use transactional mode. if (type == Primitive::kPrimChar) { @@ -374,19 +361,18 @@ extern "C" int artSet16StaticFromCode(uint32_t field_idx, uint16_t new_value, } extern "C" int artSet32StaticFromCode(uint32_t field_idx, uint32_t new_value, - mirror::ArtMethod* referrer, Thread* self, - StackReference<mirror::ArtMethod>* sp) + mirror::ArtMethod* referrer, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(int32_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { // Compiled code can't use transactional mode. field->Set32<false>(field->GetDeclaringClass(), new_value); return 0; // success } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int32_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { // Compiled code can't use transactional mode. field->Set32<false>(field->GetDeclaringClass(), new_value); return 0; // success @@ -395,19 +381,18 @@ extern "C" int artSet32StaticFromCode(uint32_t field_idx, uint32_t new_value, } extern "C" int artSet64StaticFromCode(uint32_t field_idx, mirror::ArtMethod* referrer, - uint64_t new_value, Thread* self, - StackReference<mirror::ArtMethod>* sp) + uint64_t new_value, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(int64_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { // Compiled code can't use transactional mode. field->Set64<false>(field->GetDeclaringClass(), new_value); return 0; // success } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int64_t)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { // Compiled code can't use transactional mode. field->Set64<false>(field->GetDeclaringClass(), new_value); return 0; // success @@ -416,22 +401,21 @@ extern "C" int artSet64StaticFromCode(uint32_t field_idx, mirror::ArtMethod* ref } extern "C" int artSetObjStaticFromCode(uint32_t field_idx, mirror::Object* new_value, - mirror::ArtMethod* referrer, Thread* self, - StackReference<mirror::ArtMethod>* sp) + mirror::ArtMethod* referrer, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticObjectWrite, sizeof(mirror::HeapReference<mirror::Object>)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { if (LIKELY(!field->IsPrimitiveType())) { // Compiled code can't use transactional mode. field->SetObj<false>(field->GetDeclaringClass(), new_value); return 0; // success } } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<StaticObjectWrite, true>(field_idx, referrer, self, sizeof(mirror::HeapReference<mirror::Object>)); - if (LIKELY(field != NULL)) { + if (LIKELY(field != nullptr)) { // Compiled code can't use transactional mode. field->SetObj<false>(field->GetDeclaringClass(), new_value); return 0; // success @@ -440,12 +424,12 @@ extern "C" int artSetObjStaticFromCode(uint32_t field_idx, mirror::Object* new_v } extern "C" int artSet8InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint8_t new_value, - mirror::ArtMethod* referrer, Thread* self, - StackReference<mirror::ArtMethod>* sp) + mirror::ArtMethod* referrer, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(int8_t)); - if (LIKELY(field != NULL && obj != NULL)) { + if (LIKELY(field != nullptr && obj != nullptr)) { Primitive::Type type = field->GetTypeAsPrimitiveType(); // Compiled code can't use transactional mode. if (type == Primitive::kPrimBoolean) { @@ -456,15 +440,14 @@ extern "C" int artSet8InstanceFromCode(uint32_t field_idx, mirror::Object* obj, } return 0; // success } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); { StackHandleScope<1> hs(self); HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper(&obj)); field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, self, sizeof(int8_t)); } - if (LIKELY(field != NULL)) { - if (UNLIKELY(obj == NULL)) { + if (LIKELY(field != nullptr)) { + if (UNLIKELY(obj == nullptr)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionForFieldAccess(throw_location, field, false); } else { @@ -482,12 +465,12 @@ extern "C" int artSet8InstanceFromCode(uint32_t field_idx, mirror::Object* obj, } extern "C" int artSet16InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint16_t new_value, - mirror::ArtMethod* referrer, Thread* self, - StackReference<mirror::ArtMethod>* sp) + mirror::ArtMethod* referrer, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(int16_t)); - if (LIKELY(field != NULL && obj != NULL)) { + if (LIKELY(field != nullptr && obj != nullptr)) { Primitive::Type type = field->GetTypeAsPrimitiveType(); // Compiled code can't use transactional mode. if (type == Primitive::kPrimChar) { @@ -498,15 +481,14 @@ extern "C" int artSet16InstanceFromCode(uint32_t field_idx, mirror::Object* obj, } return 0; // success } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); { StackHandleScope<1> hs(self); HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper(&obj)); field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, self, sizeof(int16_t)); } - if (LIKELY(field != NULL)) { - if (UNLIKELY(obj == NULL)) { + if (LIKELY(field != nullptr)) { + if (UNLIKELY(obj == nullptr)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionForFieldAccess(throw_location, field, false); } else { @@ -525,25 +507,24 @@ extern "C" int artSet16InstanceFromCode(uint32_t field_idx, mirror::Object* obj, } extern "C" int artSet32InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint32_t new_value, - mirror::ArtMethod* referrer, Thread* self, - StackReference<mirror::ArtMethod>* sp) + mirror::ArtMethod* referrer, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(int32_t)); - if (LIKELY(field != NULL && obj != NULL)) { + if (LIKELY(field != nullptr && obj != nullptr)) { // Compiled code can't use transactional mode. field->Set32<false>(obj, new_value); return 0; // success } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); { StackHandleScope<1> hs(self); HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper(&obj)); field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, self, sizeof(int32_t)); } - if (LIKELY(field != NULL)) { - if (UNLIKELY(obj == NULL)) { + if (LIKELY(field != nullptr)) { + if (UNLIKELY(obj == nullptr)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionForFieldAccess(throw_location, field, false); } else { @@ -556,25 +537,20 @@ extern "C" int artSet32InstanceFromCode(uint32_t field_idx, mirror::Object* obj, } extern "C" int artSet64InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint64_t new_value, - Thread* self, StackReference<mirror::ArtMethod>* sp) + mirror::ArtMethod* referrer, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - constexpr size_t frame_size = GetCalleeSaveFrameSize(kRuntimeISA, Runtime::kRefsOnly); - mirror::ArtMethod* referrer = - reinterpret_cast<StackReference<mirror::ArtMethod>*>( - reinterpret_cast<uint8_t*>(sp) + frame_size)->AsMirrorPtr(); + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(int64_t)); - if (LIKELY(field != NULL && obj != NULL)) { + if (LIKELY(field != nullptr && obj != nullptr)) { // Compiled code can't use transactional mode. field->Set64<false>(obj, new_value); return 0; // success } - sp->Assign(Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsOnly)); - self->SetTopOfStack(sp, 0); field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, self, sizeof(int64_t)); - if (LIKELY(field != NULL)) { - if (UNLIKELY(obj == NULL)) { + if (LIKELY(field != nullptr)) { + if (UNLIKELY(obj == nullptr)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionForFieldAccess(throw_location, field, false); } else { @@ -588,21 +564,20 @@ extern "C" int artSet64InstanceFromCode(uint32_t field_idx, mirror::Object* obj, extern "C" int artSetObjInstanceFromCode(uint32_t field_idx, mirror::Object* obj, mirror::Object* new_value, - mirror::ArtMethod* referrer, Thread* self, - StackReference<mirror::ArtMethod>* sp) + mirror::ArtMethod* referrer, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectWrite, sizeof(mirror::HeapReference<mirror::Object>)); - if (LIKELY(field != NULL && obj != NULL)) { + if (LIKELY(field != nullptr && obj != nullptr)) { // Compiled code can't use transactional mode. field->SetObj<false>(obj, new_value); return 0; // success } - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<InstanceObjectWrite, true>(field_idx, referrer, self, sizeof(mirror::HeapReference<mirror::Object>)); - if (LIKELY(field != NULL)) { - if (UNLIKELY(obj == NULL)) { + if (LIKELY(field != nullptr)) { + if (UNLIKELY(obj == nullptr)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionForFieldAccess(throw_location, field, false); } else { diff --git a/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc b/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc index 06bbabcbe2..e3365431ce 100644 --- a/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc @@ -25,10 +25,9 @@ namespace art { * Handle fill array data by copying appropriate part of dex file into array. */ extern "C" int artHandleFillArrayDataFromCode(uint32_t payload_offset, mirror::Array* array, - mirror::ArtMethod* method, Thread* self, - StackReference<mirror::ArtMethod>* sp) + mirror::ArtMethod* method, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); + ScopedQuickEntrypointChecks sqec(self); const uint16_t* const insns = method->GetCodeItem()->insns_; const Instruction::ArrayDataPayload* payload = reinterpret_cast<const Instruction::ArrayDataPayload*>(insns + payload_offset); diff --git a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc index 42ace40637..f78273f24c 100644 --- a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc @@ -15,6 +15,7 @@ */ #include "callee_save_frame.h" +#include "entrypoints/runtime_asm_entrypoints.h" #include "instruction_set.h" #include "instrumentation.h" #include "mirror/art_method-inl.h" @@ -27,10 +28,9 @@ namespace art { extern "C" const void* artInstrumentationMethodEntryFromCode(mirror::ArtMethod* method, mirror::Object* this_object, Thread* self, - StackReference<mirror::ArtMethod>* sp, uintptr_t lr) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs); + ScopedQuickEntrypointChecks sqec(self); instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); const void* result; if (instrumentation->IsDeoptimized(method)) { @@ -38,8 +38,7 @@ extern "C" const void* artInstrumentationMethodEntryFromCode(mirror::ArtMethod* } else { result = instrumentation->GetQuickCodeFor(method); } - DCHECK((result != Runtime::Current()->GetClassLinker()->GetQuickToInterpreterBridgeTrampoline()) - || !Runtime::Current()->GetHeap()->HasImageSpace()); + DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(result)); bool interpreter_entry = (result == GetQuickToInterpreterBridge()); instrumentation->PushInstrumentationStackFrame(self, method->IsStatic() ? nullptr : this_object, method, lr, interpreter_entry); @@ -52,23 +51,19 @@ extern "C" TwoWordReturn artInstrumentationMethodExitFromCode(Thread* self, uint64_t gpr_result, uint64_t fpr_result) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // TODO: use FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly) not the hand inlined below. - // We use the hand inline version to ensure the return_pc is assigned before verifying the - // stack. - // Be aware the store below may well stomp on an incoming argument. - Locks::mutator_lock_->AssertSharedHeld(self); - Runtime* runtime = Runtime::Current(); - sp->Assign(runtime->GetCalleeSaveMethod(Runtime::kRefsOnly)); - uint32_t return_pc_offset = GetCalleeSavePCOffset(kRuntimeISA, Runtime::kRefsOnly); + // Compute address of return PC and sanity check that it currently holds 0. + size_t return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, Runtime::kRefsOnly); uintptr_t* return_pc = reinterpret_cast<uintptr_t*>(reinterpret_cast<uint8_t*>(sp) + return_pc_offset); CHECK_EQ(*return_pc, 0U); - self->SetTopOfStack(sp, 0); - self->VerifyStack(); + + // Pop the frame filling in the return pc. The low half of the return value is 0 when + // deoptimization shouldn't be performed with the high-half having the return address. When + // deoptimization should be performed the low half is zero and the high-half the address of the + // deoptimization entry point. instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); TwoWordReturn return_or_deoptimize_pc = instrumentation->PopInstrumentationStackFrame( self, return_pc, gpr_result, fpr_result); - self->VerifyStack(); return return_or_deoptimize_pc; } diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc index c2395352bc..c1276b5bf2 100644 --- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc @@ -15,6 +15,7 @@ */ #include "entrypoints/entrypoint_utils-inl.h" +#include "mirror/art_method-inl.h" #include "mirror/object-inl.h" #include "thread-inl.h" #include "verify_object-inl.h" diff --git a/runtime/entrypoints/quick/quick_lock_entrypoints.cc b/runtime/entrypoints/quick/quick_lock_entrypoints.cc index 92c0841dd9..8ceac971e1 100644 --- a/runtime/entrypoints/quick/quick_lock_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_lock_entrypoints.cc @@ -20,12 +20,11 @@ namespace art { -extern "C" int artLockObjectFromCode(mirror::Object* obj, Thread* self, - StackReference<mirror::ArtMethod>* sp) +extern "C" int artLockObjectFromCode(mirror::Object* obj, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) NO_THREAD_SAFETY_ANALYSIS /* EXCLUSIVE_LOCK_FUNCTION(Monitor::monitor_lock_) */ { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - if (UNLIKELY(obj == NULL)) { + ScopedQuickEntrypointChecks sqec(self); + if (UNLIKELY(obj == nullptr)) { ThrowLocation throw_location(self->GetCurrentLocationForThrow()); ThrowNullPointerException(&throw_location, "Null reference used for synchronization (monitor-enter)"); @@ -43,12 +42,11 @@ extern "C" int artLockObjectFromCode(mirror::Object* obj, Thread* self, } } -extern "C" int artUnlockObjectFromCode(mirror::Object* obj, Thread* self, - StackReference<mirror::ArtMethod>* sp) +extern "C" int artUnlockObjectFromCode(mirror::Object* obj, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) NO_THREAD_SAFETY_ANALYSIS /* UNLOCK_FUNCTION(Monitor::monitor_lock_) */ { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - if (UNLIKELY(obj == NULL)) { + ScopedQuickEntrypointChecks sqec(self); + if (UNLIKELY(obj == nullptr)) { ThrowLocation throw_location(self->GetCurrentLocationForThrow()); ThrowNullPointerException(&throw_location, "Null reference used for synchronization (monitor-exit)"); diff --git a/runtime/entrypoints/quick/quick_thread_entrypoints.cc b/runtime/entrypoints/quick/quick_thread_entrypoints.cc index ea75fb6c4e..87e0c6eecd 100644 --- a/runtime/entrypoints/quick/quick_thread_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_thread_entrypoints.cc @@ -19,10 +19,9 @@ namespace art { -extern "C" void artTestSuspendFromCode(Thread* self, StackReference<mirror::ArtMethod>* sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +extern "C" void artTestSuspendFromCode(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // Called when suspend count check value is 0 and thread->suspend_count_ != 0 - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); + ScopedQuickEntrypointChecks sqec(self); self->CheckSuspend(); } diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc index 13decc8068..25df40b6c0 100644 --- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc @@ -24,16 +24,14 @@ namespace art { // Deliver an exception that's pending on thread helping set up a callee save frame on the way. -extern "C" void artDeliverPendingExceptionFromCode(Thread* thread, - StackReference<mirror::ArtMethod>* sp) +extern "C" void artDeliverPendingExceptionFromCode(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll); - thread->QuickDeliverException(); + ScopedQuickEntrypointChecks sqec(self); + self->QuickDeliverException(); } // Called by generated call to throw an exception. -extern "C" void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* self, - StackReference<mirror::ArtMethod>* sp) +extern "C" void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { /* * exception may be NULL, in which case this routine should @@ -42,9 +40,9 @@ extern "C" void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread * and threw a NPE if NULL. This routine responsible for setting * exception_ in thread and delivering the exception. */ - FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); + ScopedQuickEntrypointChecks sqec(self); ThrowLocation throw_location = self->GetCurrentLocationForThrow(); - if (exception == NULL) { + if (exception == nullptr) { self->ThrowNewException(throw_location, "Ljava/lang/NullPointerException;", "throw with null exception"); } else { @@ -54,10 +52,9 @@ extern "C" void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread } // Called by generated call to throw a NPE exception. -extern "C" void artThrowNullPointerExceptionFromCode(Thread* self, - StackReference<mirror::ArtMethod>* sp) +extern "C" void artThrowNullPointerExceptionFromCode(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); + ScopedQuickEntrypointChecks sqec(self); self->NoteSignalBeingHandled(); ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionFromDexPC(throw_location); @@ -66,52 +63,50 @@ extern "C" void artThrowNullPointerExceptionFromCode(Thread* self, } // Called by generated call to throw an arithmetic divide by zero exception. -extern "C" void artThrowDivZeroFromCode(Thread* self, StackReference<mirror::ArtMethod>* sp) +extern "C" void artThrowDivZeroFromCode(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); + ScopedQuickEntrypointChecks sqec(self); ThrowArithmeticExceptionDivideByZero(); self->QuickDeliverException(); } // Called by generated call to throw an array index out of bounds exception. -extern "C" void artThrowArrayBoundsFromCode(int index, int length, Thread* self, - StackReference<mirror::ArtMethod>*sp) +extern "C" void artThrowArrayBoundsFromCode(int index, int length, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); + ScopedQuickEntrypointChecks sqec(self); ThrowArrayIndexOutOfBoundsException(index, length); self->QuickDeliverException(); } -extern "C" void artThrowStackOverflowFromCode(Thread* self, StackReference<mirror::ArtMethod>* sp) +extern "C" void artThrowStackOverflowFromCode(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); + ScopedQuickEntrypointChecks sqec(self); self->NoteSignalBeingHandled(); ThrowStackOverflowError(self); self->NoteSignalHandlerDone(); self->QuickDeliverException(); } -extern "C" void artThrowNoSuchMethodFromCode(int32_t method_idx, Thread* self, - StackReference<mirror::ArtMethod>* sp) +extern "C" void artThrowNoSuchMethodFromCode(int32_t method_idx, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); + ScopedQuickEntrypointChecks sqec(self); ThrowNoSuchMethodError(method_idx); self->QuickDeliverException(); } extern "C" void artThrowClassCastException(mirror::Class* dest_type, mirror::Class* src_type, - Thread* self, StackReference<mirror::ArtMethod>* sp) + Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); - CHECK(!dest_type->IsAssignableFrom(src_type)); + ScopedQuickEntrypointChecks sqec(self); + DCHECK(!dest_type->IsAssignableFrom(src_type)); ThrowClassCastException(dest_type, src_type); self->QuickDeliverException(); } extern "C" void artThrowArrayStoreException(mirror::Object* array, mirror::Object* value, - Thread* self, StackReference<mirror::ArtMethod>* sp) + Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); + ScopedQuickEntrypointChecks sqec(self); ThrowArrayStoreException(value->GetClass(), array->GetClass()); self->QuickDeliverException(); } diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 96903db414..af341bb1fa 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -19,6 +19,7 @@ #include "dex_file-inl.h" #include "dex_instruction-inl.h" #include "entrypoints/entrypoint_utils-inl.h" +#include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" #include "instruction_set.h" #include "interpreter/interpreter.h" @@ -465,7 +466,7 @@ extern "C" uint64_t artQuickToInterpreterBridge(mirror::ArtMethod* method, Threa SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // Ensure we don't get thread suspension until the object arguments are safely in the shadow // frame. - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs); + ScopedQuickEntrypointChecks sqec(self); if (method->IsAbstract()) { ThrowAbstractMethodError(method); @@ -504,7 +505,7 @@ extern "C" uint64_t artQuickToInterpreterBridge(mirror::ArtMethod* method, Threa return 0; } } - JValue result = interpreter::EnterInterpreterFromStub(self, mh, code_item, *shadow_frame); + JValue result = interpreter::EnterInterpreterFromEntryPoint(self, &mh, code_item, shadow_frame); // Pop transition. self->PopManagedStackFragment(fragment); // No need to restore the args since the method has already been run by the interpreter. @@ -592,7 +593,6 @@ extern "C" uint64_t artQuickProxyInvokeHandler(mirror::ArtMethod* proxy_method, self->StartAssertNoThreadSuspension("Adding to IRT proxy object arguments"); // Register the top of the managed stack, making stack crawlable. DCHECK_EQ(sp->AsMirrorPtr(), proxy_method) << PrettyMethod(proxy_method); - self->SetTopOfStack(sp, 0); DCHECK_EQ(proxy_method->GetFrameSizeInBytes(), Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes()) << PrettyMethod(proxy_method); @@ -677,7 +677,7 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called, Thread* self, StackReference<mirror::ArtMethod>* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs); + ScopedQuickEntrypointChecks sqec(self); // Start new JNI local reference state JNIEnvExt* env = self->GetJniEnv(); ScopedObjectAccessUnchecked soa(env); @@ -949,7 +949,7 @@ template<class T> class BuildNativeCallFrameStateMachine { virtual ~BuildNativeCallFrameStateMachine() {} - bool HavePointerGpr() { + bool HavePointerGpr() const { return gpr_index_ > 0; } @@ -964,7 +964,7 @@ template<class T> class BuildNativeCallFrameStateMachine { } } - bool HaveHandleScopeGpr() { + bool HaveHandleScopeGpr() const { return gpr_index_ > 0; } @@ -980,7 +980,7 @@ template<class T> class BuildNativeCallFrameStateMachine { } } - bool HaveIntGpr() { + bool HaveIntGpr() const { return gpr_index_ > 0; } @@ -995,17 +995,17 @@ template<class T> class BuildNativeCallFrameStateMachine { } } - bool HaveLongGpr() { + bool HaveLongGpr() const { return gpr_index_ >= kRegistersNeededForLong + (LongGprNeedsPadding() ? 1 : 0); } - bool LongGprNeedsPadding() { + bool LongGprNeedsPadding() const { return kRegistersNeededForLong > 1 && // only pad when using multiple registers kAlignLongOnStack && // and when it needs alignment (gpr_index_ & 1) == 1; // counter is odd, see constructor } - bool LongStackNeedsPadding() { + bool LongStackNeedsPadding() const { return kRegistersNeededForLong > 1 && // only pad when using multiple registers kAlignLongOnStack && // and when it needs 8B alignment (stack_entries_ & 1) == 1; // counter is odd @@ -1041,7 +1041,7 @@ template<class T> class BuildNativeCallFrameStateMachine { } } - bool HaveFloatFpr() { + bool HaveFloatFpr() const { return fpr_index_ > 0; } @@ -1076,17 +1076,17 @@ template<class T> class BuildNativeCallFrameStateMachine { } } - bool HaveDoubleFpr() { + bool HaveDoubleFpr() const { return fpr_index_ >= kRegistersNeededForDouble + (DoubleFprNeedsPadding() ? 1 : 0); } - bool DoubleFprNeedsPadding() { + bool DoubleFprNeedsPadding() const { return kRegistersNeededForDouble > 1 && // only pad when using multiple registers kAlignDoubleOnStack && // and when it needs alignment (fpr_index_ & 1) == 1; // counter is odd, see constructor } - bool DoubleStackNeedsPadding() { + bool DoubleStackNeedsPadding() const { return kRegistersNeededForDouble > 1 && // only pad when using multiple registers kAlignDoubleOnStack && // and when it needs 8B alignment (stack_entries_ & 1) == 1; // counter is odd @@ -1121,15 +1121,15 @@ template<class T> class BuildNativeCallFrameStateMachine { } } - uint32_t getStackEntries() { + uint32_t GetStackEntries() const { return stack_entries_; } - uint32_t getNumberOfUsedGprs() { + uint32_t GetNumberOfUsedGprs() const { return kNumNativeGprArgs - gpr_index_; } - uint32_t getNumberOfUsedFprs() { + uint32_t GetNumberOfUsedFprs() const { return kNumNativeFprArgs - fpr_index_; } @@ -1154,7 +1154,7 @@ template<class T> class BuildNativeCallFrameStateMachine { uint32_t fpr_index_; // Number of free FPRs uint32_t stack_entries_; // Stack entries are in multiples of 32b, as floats are usually not // extended - T* delegate_; // What Push implementation gets called + T* const delegate_; // What Push implementation gets called }; // Computes the sizes of register stacks and call stack area. Handling of references can be extended @@ -1168,18 +1168,19 @@ class ComputeNativeCallFrameSize { virtual ~ComputeNativeCallFrameSize() {} - uint32_t GetStackSize() { + uint32_t GetStackSize() const { return num_stack_entries_ * sizeof(uintptr_t); } - uint8_t* LayoutCallStack(uint8_t* sp8) { + uint8_t* LayoutCallStack(uint8_t* sp8) const { sp8 -= GetStackSize(); // Align by kStackAlignment. sp8 = reinterpret_cast<uint8_t*>(RoundDown(reinterpret_cast<uintptr_t>(sp8), kStackAlignment)); return sp8; } - uint8_t* LayoutCallRegisterStacks(uint8_t* sp8, uintptr_t** start_gpr, uint32_t** start_fpr) { + uint8_t* LayoutCallRegisterStacks(uint8_t* sp8, uintptr_t** start_gpr, uint32_t** start_fpr) + const { // Assumption is OK right now, as we have soft-float arm size_t fregs = BuildNativeCallFrameStateMachine<ComputeNativeCallFrameSize>::kNumNativeFprArgs; sp8 -= fregs * sizeof(uintptr_t); @@ -1191,7 +1192,7 @@ class ComputeNativeCallFrameSize { } uint8_t* LayoutNativeCall(uint8_t* sp8, uintptr_t** start_stack, uintptr_t** start_gpr, - uint32_t** start_fpr) { + uint32_t** start_fpr) const { // Native call stack. sp8 = LayoutCallStack(sp8); *start_stack = reinterpret_cast<uintptr_t*>(sp8); @@ -1215,6 +1216,7 @@ class ComputeNativeCallFrameSize { Primitive::Type cur_type_ = Primitive::GetType(shorty[i]); switch (cur_type_) { case Primitive::kPrimNot: + // TODO: fix abuse of mirror types. sm.AdvanceHandleScope( reinterpret_cast<mirror::Object*>(0x12345678)); break; @@ -1240,7 +1242,7 @@ class ComputeNativeCallFrameSize { } } - num_stack_entries_ = sm.getStackEntries(); + num_stack_entries_ = sm.GetStackEntries(); } void PushGpr(uintptr_t /* val */) { @@ -1310,7 +1312,7 @@ class ComputeGenericJniFrameSize FINAL : public ComputeNativeCallFrameSize { } // Adds space for the cookie. Note: may leave stack unaligned. - void LayoutCookie(uint8_t** sp) { + void LayoutCookie(uint8_t** sp) const { // Reference cookie and padding *sp -= 8; } @@ -1457,11 +1459,11 @@ class BuildGenericJniFrameVisitor FINAL : public QuickArgumentVisitor { return handle_scope_->GetHandle(0).GetReference(); } - jobject GetFirstHandleScopeJObject() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + jobject GetFirstHandleScopeJObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return handle_scope_->GetHandle(0).ToJObject(); } - void* GetBottomOfUsedArea() { + void* GetBottomOfUsedArea() const { return bottom_of_used_area_; } @@ -1608,13 +1610,13 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, uint32_t shorty_len = 0; const char* shorty = called->GetShorty(&shorty_len); - // Run the visitor. + // Run the visitor and update sp. BuildGenericJniFrameVisitor visitor(self, called->IsStatic(), shorty, shorty_len, &sp); visitor.VisitArguments(); visitor.FinalizeHandleScope(self); // Fix up managed-stack things in Thread. - self->SetTopOfStack(sp, 0); + self->SetTopOfStack(sp); self->VerifyStack(); @@ -1743,10 +1745,11 @@ template<InvokeType type, bool access_check> static TwoWordReturn artInvokeCommon(uint32_t method_idx, mirror::Object* this_object, mirror::ArtMethod* caller_method, Thread* self, StackReference<mirror::ArtMethod>* sp) { + ScopedQuickEntrypointChecks sqec(self); + DCHECK_EQ(sp->AsMirrorPtr(), Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)); mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check, type); if (UNLIKELY(method == nullptr)) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs); const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache()->GetDexFile(); uint32_t shorty_len; const char* shorty = dex_file->GetMethodShorty(dex_file->GetMethodId(method_idx), &shorty_len); @@ -1851,21 +1854,20 @@ extern "C" TwoWordReturn artInvokeInterfaceTrampoline(mirror::ArtMethod* interfa Thread* self, StackReference<mirror::ArtMethod>* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); mirror::ArtMethod* method; if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex)) { method = this_object->GetClass()->FindVirtualMethodForInterface(interface_method); if (UNLIKELY(method == NULL)) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs); ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(interface_method, this_object, caller_method); return GetTwoWordFailureValue(); // Failure. } } else { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs); DCHECK(interface_method == Runtime::Current()->GetResolutionMethod()); // Find the caller PC. - constexpr size_t pc_offset = GetCalleeSavePCOffset(kRuntimeISA, Runtime::kRefsAndArgs); + constexpr size_t pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, Runtime::kRefsAndArgs); uintptr_t caller_pc = *reinterpret_cast<uintptr_t*>(reinterpret_cast<uint8_t*>(sp) + pc_offset); // Map the caller PC to a dex PC. diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc index 02b8a5b605..a9af75401e 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc @@ -55,9 +55,10 @@ class QuickTrampolineEntrypointsTest : public CommonRuntimeTest { NO_THREAD_SAFETY_ANALYSIS { mirror::ArtMethod* save_method = CreateCalleeSaveMethod(isa, type); QuickMethodFrameInfo frame_info = save_method->GetQuickFrameInfo(); - EXPECT_EQ(save_method->GetReturnPcOffsetInBytes(), pc_offset) << "Expected and real pc offset" - " differs for " << type << " core spills=" << std::hex << frame_info.CoreSpillMask() << - " fp spills=" << frame_info.FpSpillMask() << std::dec << " ISA " << isa; + EXPECT_EQ(save_method->GetReturnPcOffset().SizeValue(), pc_offset) + << "Expected and real pc offset differs for " << type + << " core spills=" << std::hex << frame_info.CoreSpillMask() + << " fp spills=" << frame_info.FpSpillMask() << std::dec << " ISA " << isa; } }; @@ -97,11 +98,11 @@ TEST_F(QuickTrampolineEntrypointsTest, ReturnPC) { // Note: we can only check against the kRuntimeISA, because the ArtMethod computation uses // sizeof(void*), which is wrong when the target bitwidth is not the same as the host's. CheckPCOffset(kRuntimeISA, Runtime::kRefsAndArgs, - GetCalleeSavePCOffset(kRuntimeISA, Runtime::kRefsAndArgs)); + GetCalleeSaveReturnPcOffset(kRuntimeISA, Runtime::kRefsAndArgs)); CheckPCOffset(kRuntimeISA, Runtime::kRefsOnly, - GetCalleeSavePCOffset(kRuntimeISA, Runtime::kRefsOnly)); + GetCalleeSaveReturnPcOffset(kRuntimeISA, Runtime::kRefsOnly)); CheckPCOffset(kRuntimeISA, Runtime::kSaveAll, - GetCalleeSavePCOffset(kRuntimeISA, Runtime::kSaveAll)); + GetCalleeSaveReturnPcOffset(kRuntimeISA, Runtime::kSaveAll)); } } // namespace art diff --git a/runtime/entrypoints/runtime_asm_entrypoints.h b/runtime/entrypoints/runtime_asm_entrypoints.h new file mode 100644 index 0000000000..db36a73956 --- /dev/null +++ b/runtime/entrypoints/runtime_asm_entrypoints.h @@ -0,0 +1,117 @@ +/* + * 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_RUNTIME_ENTRYPOINTS_RUNTIME_ASM_ENTRYPOINTS_H_ +#define ART_RUNTIME_ENTRYPOINTS_RUNTIME_ASM_ENTRYPOINTS_H_ + +namespace art { + +#ifndef BUILDING_LIBART +#error "File and symbols only for use within libart." +#endif + +extern "C" void* art_jni_dlsym_lookup_stub(JNIEnv*, jobject); +static inline const void* GetJniDlsymLookupStub() { + return reinterpret_cast<const void*>(art_jni_dlsym_lookup_stub); +} + +// Return the address of portable stub code for handling IMT conflicts. +extern "C" void art_portable_imt_conflict_trampoline(mirror::ArtMethod*); +static inline const void* GetPortableImtConflictStub() { + return reinterpret_cast<const void*>(art_portable_imt_conflict_trampoline); +} + +// Return the address of quick stub code for handling IMT conflicts. +extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*); +static inline const void* GetQuickImtConflictStub() { + return reinterpret_cast<const void*>(art_quick_imt_conflict_trampoline); +} + +// Return the address of portable stub code for bridging from portable code to the interpreter. +extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*); +static inline const void* GetPortableToInterpreterBridge() { + return reinterpret_cast<const void*>(art_portable_to_interpreter_bridge); +} + +// Return the address of quick stub code for bridging from quick code to the interpreter. +extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*); +static inline const void* GetQuickToInterpreterBridge() { + return reinterpret_cast<const void*>(art_quick_to_interpreter_bridge); +} + +// Return the address of portable stub code for bridging from portable code to quick. +static inline const void* GetPortableToQuickBridge() { + // TODO: portable to quick bridge. Bug: 8196384 + return GetPortableToInterpreterBridge(); +} + +// Return the address of quick stub code for bridging from quick code to portable. +static inline const void* GetQuickToPortableBridge() { + // TODO: quick to portable bridge. Bug: 8196384 + return GetQuickToInterpreterBridge(); +} + +// Return the address of quick stub code for handling JNI calls. +extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*); +static inline const void* GetQuickGenericJniStub() { + return reinterpret_cast<const void*>(art_quick_generic_jni_trampoline); +} + +// Return the address of portable stub code for handling transitions into the proxy invoke handler. +extern "C" void art_portable_proxy_invoke_handler(); +static inline const void* GetPortableProxyInvokeHandler() { + return reinterpret_cast<const void*>(art_portable_proxy_invoke_handler); +} + +// Return the address of quick stub code for handling transitions into the proxy invoke handler. +extern "C" void art_quick_proxy_invoke_handler(); +static inline const void* GetQuickProxyInvokeHandler() { + return reinterpret_cast<const void*>(art_quick_proxy_invoke_handler); +} + +// Return the address of portable stub code for resolving a method at first call. +extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*); +static inline const void* GetPortableResolutionStub() { + return reinterpret_cast<const void*>(art_portable_resolution_trampoline); +} + +// Return the address of quick stub code for resolving a method at first call. +extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*); +static inline const void* GetQuickResolutionStub() { + return reinterpret_cast<const void*>(art_quick_resolution_trampoline); +} + +// Entry point for quick code that performs deoptimization. +extern "C" void art_quick_deoptimize(); +static inline const void* GetQuickDeoptimizationEntryPoint() { + return reinterpret_cast<const void*>(art_quick_deoptimize); +} + +// Return address of instrumentation entry point used by non-interpreter based tracing. +extern "C" void art_quick_instrumentation_entry(void*); +static inline const void* GetQuickInstrumentationEntryPoint() { + return reinterpret_cast<const void*>(art_quick_instrumentation_entry); +} + +// The return_pc of instrumentation exit stub. +extern "C" void art_quick_instrumentation_exit(); +static inline const void* GetQuickInstrumentationExitPc() { + return reinterpret_cast<const void*>(art_quick_instrumentation_exit); +} + +} // namespace art + +#endif // ART_RUNTIME_ENTRYPOINTS_RUNTIME_ASM_ENTRYPOINTS_H_ diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc index 3a17ecaa57..1365cd4bef 100644 --- a/runtime/exception_test.cc +++ b/runtime/exception_test.cc @@ -43,7 +43,7 @@ class ExceptionTest : public CommonRuntimeTest { Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("ExceptionHandle")))); my_klass_ = class_linker_->FindClass(soa.Self(), "LExceptionHandle;", class_loader); - ASSERT_TRUE(my_klass_ != NULL); + ASSERT_TRUE(my_klass_ != nullptr); Handle<mirror::Class> klass(hs.NewHandle(my_klass_)); class_linker_->EnsureInitialized(soa.Self(), klass, true, true); my_klass_ = klass.Get(); @@ -93,12 +93,12 @@ class ExceptionTest : public CommonRuntimeTest { const uint8_t* code_ptr = &fake_header_code_and_maps_[mapping_table_offset]; method_f_ = my_klass_->FindVirtualMethod("f", "()I"); - ASSERT_TRUE(method_f_ != NULL); + ASSERT_TRUE(method_f_ != nullptr); method_f_->SetEntryPointFromQuickCompiledCode(code_ptr); method_f_->SetNativeGcMap(&fake_gc_map_[0]); method_g_ = my_klass_->FindVirtualMethod("g", "(I)V"); - ASSERT_TRUE(method_g_ != NULL); + ASSERT_TRUE(method_g_ != nullptr); method_g_->SetEntryPointFromQuickCompiledCode(code_ptr); method_g_->SetNativeGcMap(&fake_gc_map_[0]); } @@ -122,7 +122,7 @@ TEST_F(ExceptionTest, FindCatchHandler) { ScopedObjectAccess soa(Thread::Current()); const DexFile::CodeItem* code_item = dex_->GetCodeItem(method_f_->GetCodeItemOffset()); - ASSERT_TRUE(code_item != NULL); + ASSERT_TRUE(code_item != nullptr); ASSERT_EQ(2u, code_item->tries_size_); ASSERT_NE(0u, code_item->insns_size_in_code_units_); @@ -163,19 +163,35 @@ TEST_F(ExceptionTest, StackTraceElement) { ScopedObjectAccess soa(env); std::vector<uintptr_t> fake_stack; + Runtime* r = Runtime::Current(); + r->SetInstructionSet(kRuntimeISA); + mirror::ArtMethod* save_method = r->CreateCalleeSaveMethod(Runtime::kSaveAll); + r->SetCalleeSaveMethod(save_method, Runtime::kSaveAll); + QuickMethodFrameInfo frame_info = save_method->GetQuickFrameInfo(); + ASSERT_EQ(kStackAlignment, 16U); // ASSERT_EQ(sizeof(uintptr_t), sizeof(uint32_t)); + if (!kUsePortableCompiler) { - // Create two fake stack frames with mapping data created in SetUp. We map offset 3 in the code - // to dex pc 3. + // Create three fake stack frames with mapping data created in SetUp. We map offset 3 in the + // code to dex pc 3. const uint32_t dex_pc = 3; + // Create the stack frame for the callee save method, expected by the runtime. + fake_stack.push_back(reinterpret_cast<uintptr_t>(save_method)); + for (size_t i = 0; i < frame_info.FrameSizeInBytes() - 2 * sizeof(uintptr_t); + i += sizeof(uintptr_t)) { + fake_stack.push_back(0); + } + + fake_stack.push_back(method_g_->ToNativeQuickPc(dex_pc)); // return pc + // Create/push fake 16byte stack frame for method g fake_stack.push_back(reinterpret_cast<uintptr_t>(method_g_)); fake_stack.push_back(0); fake_stack.push_back(0); - fake_stack.push_back(method_f_->ToNativePc(dex_pc)); // return pc + fake_stack.push_back(method_f_->ToNativeQuickPc(dex_pc)); // return pc // Create/push fake 16byte stack frame for method f fake_stack.push_back(reinterpret_cast<uintptr_t>(method_f_)); @@ -183,7 +199,7 @@ TEST_F(ExceptionTest, StackTraceElement) { fake_stack.push_back(0); fake_stack.push_back(0xEBAD6070); // return pc - // Pull Method* of NULL to terminate the trace + // Push Method* of NULL to terminate the trace fake_stack.push_back(0); // Push null values which will become null incoming arguments. @@ -192,9 +208,7 @@ TEST_F(ExceptionTest, StackTraceElement) { fake_stack.push_back(0); // Set up thread to appear as if we called out of method_g_ at pc dex 3 - thread->SetTopOfStack( - reinterpret_cast<StackReference<mirror::ArtMethod>*>(&fake_stack[0]), - method_g_->ToNativePc(dex_pc)); // return pc + thread->SetTopOfStack(reinterpret_cast<StackReference<mirror::ArtMethod>*>(&fake_stack[0])); } else { // Create/push fake 20-byte shadow frame for method g fake_stack.push_back(0); @@ -215,33 +229,35 @@ TEST_F(ExceptionTest, StackTraceElement) { } jobject internal = thread->CreateInternalStackTrace<false>(soa); - ASSERT_TRUE(internal != NULL); + ASSERT_TRUE(internal != nullptr); jobjectArray ste_array = Thread::InternalStackTraceToStackTraceElementArray(soa, internal); - ASSERT_TRUE(ste_array != NULL); + ASSERT_TRUE(ste_array != nullptr); mirror::ObjectArray<mirror::StackTraceElement>* trace_array = soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(ste_array); - ASSERT_TRUE(trace_array != NULL); - ASSERT_TRUE(trace_array->Get(0) != NULL); + ASSERT_TRUE(trace_array != nullptr); + ASSERT_TRUE(trace_array->Get(0) != nullptr); EXPECT_STREQ("ExceptionHandle", trace_array->Get(0)->GetDeclaringClass()->ToModifiedUtf8().c_str()); - EXPECT_STREQ("ExceptionHandle.java", trace_array->Get(0)->GetFileName()->ToModifiedUtf8().c_str()); + EXPECT_STREQ("ExceptionHandle.java", + trace_array->Get(0)->GetFileName()->ToModifiedUtf8().c_str()); EXPECT_STREQ("g", trace_array->Get(0)->GetMethodName()->ToModifiedUtf8().c_str()); EXPECT_EQ(37, trace_array->Get(0)->GetLineNumber()); - ASSERT_TRUE(trace_array->Get(1) != NULL); + ASSERT_TRUE(trace_array->Get(1) != nullptr); EXPECT_STREQ("ExceptionHandle", trace_array->Get(1)->GetDeclaringClass()->ToModifiedUtf8().c_str()); - EXPECT_STREQ("ExceptionHandle.java", trace_array->Get(1)->GetFileName()->ToModifiedUtf8().c_str()); + EXPECT_STREQ("ExceptionHandle.java", + trace_array->Get(1)->GetFileName()->ToModifiedUtf8().c_str()); EXPECT_STREQ("f", trace_array->Get(1)->GetMethodName()->ToModifiedUtf8().c_str()); EXPECT_EQ(22, trace_array->Get(1)->GetLineNumber()); -#if !defined(ART_USE_PORTABLE_COMPILER) - thread->SetTopOfStack(NULL, 0); // Disarm the assertion that no code is running when we detach. -#else - thread->PopShadowFrame(); - thread->PopShadowFrame(); -#endif + if (!kUsePortableCompiler) { + thread->SetTopOfStack(nullptr); // Disarm the assertion that no code is running when we detach. + } else { + thread->PopShadowFrame(); + thread->PopShadowFrame(); + } } } // namespace art diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc index fede2f8ec3..4ae929b0cc 100644 --- a/runtime/fault_handler.cc +++ b/runtime/fault_handler.cc @@ -95,16 +95,29 @@ FaultManager::FaultManager() : initialized_(false) { FaultManager::~FaultManager() { } +static void SetUpArtAction(struct sigaction* action) { + action->sa_sigaction = art_fault_handler; + sigemptyset(&action->sa_mask); + action->sa_flags = SA_SIGINFO | SA_ONSTACK; +#if !defined(__APPLE__) && !defined(__mips__) + action->sa_restorer = nullptr; +#endif +} + +void FaultManager::EnsureArtActionInFrontOfSignalChain() { + if (initialized_) { + struct sigaction action; + SetUpArtAction(&action); + EnsureFrontOfChain(SIGSEGV, &action); + } else { + LOG(WARNING) << "Can't call " << __FUNCTION__ << " due to unitialized fault manager"; + } +} void FaultManager::Init() { CHECK(!initialized_); struct sigaction action; - action.sa_sigaction = art_fault_handler; - sigemptyset(&action.sa_mask); - action.sa_flags = SA_SIGINFO | SA_ONSTACK; -#if !defined(__APPLE__) && !defined(__mips__) - action.sa_restorer = nullptr; -#endif + SetUpArtAction(&action); // Set our signal handler now. int e = sigaction(SIGSEGV, &action, &oldaction_); @@ -138,7 +151,6 @@ void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) { // // If malloc calls abort, it will be holding its lock. // If the handler tries to call malloc, it will deadlock. - VLOG(signals) << "Handling fault"; if (IsInGeneratedCode(info, context, true)) { VLOG(signals) << "in generated code, looking for handler"; @@ -388,7 +400,7 @@ bool JavaStackTraceHandler::Action(int sig, siginfo_t* siginfo, void* context) { // Inside of generated code, sp[0] is the method, so sp is the frame. StackReference<mirror::ArtMethod>* frame = reinterpret_cast<StackReference<mirror::ArtMethod>*>(sp); - self->SetTopOfStack(frame, 0); // Since we don't necessarily have a dex pc, pass in 0. + self->SetTopOfStack(frame); #ifdef TEST_NESTED_SIGNAL // To test the nested signal handler we raise a signal here. This will cause the // nested signal handler to be called and perform a longjmp back to the setjmp diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h index 8b66a6f323..adac4c276a 100644 --- a/runtime/fault_handler.h +++ b/runtime/fault_handler.h @@ -45,6 +45,7 @@ class FaultManager { // Unclaim signals and delete registered handlers. void Shutdown(); + void EnsureArtActionInFrontOfSignalChain(); void HandleFault(int sig, siginfo_t* info, void* context); void HandleNestedSignal(int sig, siginfo_t* info, void* context); @@ -128,7 +129,6 @@ class JavaStackTraceHandler FINAL : public FaultHandler { DISALLOW_COPY_AND_ASSIGN(JavaStackTraceHandler); }; - // Statically allocated so the the signal handler can Get access to it. extern FaultManager fault_manager; diff --git a/runtime/gc/allocator/dlmalloc.cc b/runtime/gc/allocator/dlmalloc.cc index a6a3ee7a57..fbeba7f447 100644 --- a/runtime/gc/allocator/dlmalloc.cc +++ b/runtime/gc/allocator/dlmalloc.cc @@ -39,11 +39,11 @@ static void art_heap_usage_error(const char* function, void* p); static void art_heap_corruption(const char* function) { - LOG(FATAL) << "Corrupt heap detected in: " << function; + LOG(::art::FATAL) << "Corrupt heap detected in: " << function; } static void art_heap_usage_error(const char* function, void* p) { - LOG(FATAL) << "Incorrect use of function '" << function << "' argument " << p << " not expected"; + LOG(::art::FATAL) << "Incorrect use of function '" << function << "' argument " << p << " not expected"; } #include "globals.h" @@ -63,7 +63,7 @@ extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_byte int rc = madvise(start, length, MADV_DONTNEED); if (UNLIKELY(rc != 0)) { errno = rc; - PLOG(FATAL) << "madvise failed during heap trimming"; + PLOG(::art::FATAL) << "madvise failed during heap trimming"; } size_t* reclaimed = reinterpret_cast<size_t*>(arg); *reclaimed += length; diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc index 0cea89dc17..fa531a7c60 100644 --- a/runtime/gc/allocator/rosalloc.cc +++ b/runtime/gc/allocator/rosalloc.cc @@ -24,6 +24,7 @@ #include <map> #include <list> +#include <sstream> #include <vector> namespace art { @@ -264,7 +265,7 @@ void* RosAlloc::AllocPages(Thread* self, size_t num_pages, uint8_t page_map_type } break; default: - LOG(FATAL) << "Unreachable - page map type: " << page_map_type; + LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(page_map_type); break; } if (kIsDebugBuild) { @@ -499,7 +500,7 @@ size_t RosAlloc::FreeInternal(Thread* self, void* ptr) { case kPageMapLargeObject: return FreePages(self, ptr, false); case kPageMapLargeObjectPart: - LOG(FATAL) << "Unreachable - page map type: " << page_map_[pm_idx]; + LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(page_map_[pm_idx]); return 0; case kPageMapRunPart: { // Find the beginning of the run. @@ -514,11 +515,11 @@ size_t RosAlloc::FreeInternal(Thread* self, void* ptr) { break; case kPageMapReleased: case kPageMapEmpty: - LOG(FATAL) << "Unreachable - page map type: " << page_map_[pm_idx]; + LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(page_map_[pm_idx]); return 0; } default: - LOG(FATAL) << "Unreachable - page map type: " << page_map_[pm_idx]; + LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(page_map_[pm_idx]); return 0; } } @@ -1143,7 +1144,7 @@ static constexpr bool kReadPageMapEntryWithoutLockInBulkFree = true; size_t RosAlloc::BulkFree(Thread* self, void** ptrs, size_t num_ptrs) { size_t freed_bytes = 0; - if (false) { + if ((false)) { // Used only to test Free() as GC uses only BulkFree(). for (size_t i = 0; i < num_ptrs; ++i) { freed_bytes += FreeInternal(self, ptrs[i]); @@ -1189,7 +1190,7 @@ size_t RosAlloc::BulkFree(Thread* self, void** ptrs, size_t num_ptrs) { freed_bytes += FreePages(self, ptr, false); continue; } else { - LOG(FATAL) << "Unreachable - page map type: " << page_map_entry; + LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(page_map_entry); } } else { // Read the page map entries with a lock. @@ -1215,7 +1216,7 @@ size_t RosAlloc::BulkFree(Thread* self, void** ptrs, size_t num_ptrs) { freed_bytes += FreePages(self, ptr, false); continue; } else { - LOG(FATAL) << "Unreachable - page map type: " << page_map_entry; + LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(page_map_entry); } } DCHECK(run != nullptr); @@ -1477,7 +1478,7 @@ size_t RosAlloc::UsableSize(void* ptr) { return IndexToBracketSize(idx); } default: { - LOG(FATAL) << "Unreachable - page map type: " << page_map_[pm_idx]; + LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(page_map_[pm_idx]); break; } } @@ -1593,7 +1594,7 @@ void RosAlloc::InspectAll(void (*handler)(void* start, void* end, size_t used_by break; } case kPageMapLargeObjectPart: - LOG(FATAL) << "Unreachable - page map type: " << pm; + LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(pm); break; case kPageMapRun: { // The start of a run. @@ -1613,10 +1614,10 @@ void RosAlloc::InspectAll(void (*handler)(void* start, void* end, size_t used_by break; } case kPageMapRunPart: - LOG(FATAL) << "Unreachable - page map type: " << pm; + LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(pm); break; default: - LOG(FATAL) << "Unreachable - page map type: " << pm; + LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(pm); break; } } @@ -1929,7 +1930,7 @@ void RosAlloc::Verify() { break; } case kPageMapLargeObjectPart: - LOG(FATAL) << "Unreachable - page map type: " << pm << std::endl << DumpPageMap(); + LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(pm) << std::endl << DumpPageMap(); break; case kPageMapRun: { // The start of a run. @@ -1957,7 +1958,7 @@ void RosAlloc::Verify() { case kPageMapRunPart: // Fall-through. default: - LOG(FATAL) << "Unreachable - page map type: " << pm << std::endl << DumpPageMap(); + LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(pm) << std::endl << DumpPageMap(); break; } } @@ -2146,7 +2147,7 @@ size_t RosAlloc::ReleasePages() { ++i; break; // Skip. default: - LOG(FATAL) << "Unreachable - page map type: " << pm; + LOG(FATAL) << "Unreachable - page map type: " << static_cast<int>(pm); break; } } diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index 07b61e6cf0..9e6a8003f4 100644 --- a/runtime/gc/collector/garbage_collector.cc +++ b/runtime/gc/collector/garbage_collector.cc @@ -18,6 +18,10 @@ #include "garbage_collector.h" +#define ATRACE_TAG ATRACE_TAG_DALVIK +#include "cutils/trace.h" + +#include "base/dumpable.h" #include "base/histogram-inl.h" #include "base/logging.h" #include "base/mutex-inl.h" @@ -188,7 +192,7 @@ void GarbageCollector::DumpPerformanceInfo(std::ostream& os) { if (iterations == 0) { return; } - os << ConstDumpable<CumulativeLogger>(logger); + os << Dumpable<CumulativeLogger>(logger); const uint64_t total_ns = logger.GetTotalNs(); double seconds = NsToMs(logger.GetTotalNs()) / 1000.0; const uint64_t freed_bytes = GetTotalFreedBytes(); diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 942b556a7e..83da0639ea 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -21,6 +21,9 @@ #include <climits> #include <vector> +#define ATRACE_TAG ATRACE_TAG_DALVIK +#include "cutils/trace.h" + #include "base/bounded_fifo.h" #include "base/logging.h" #include "base/macros.h" diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index 9459a3b829..e141b6f4ab 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -16,9 +16,10 @@ #include "semi_space-inl.h" +#include <climits> #include <functional> #include <numeric> -#include <climits> +#include <sstream> #include <vector> #include "base/logging.h" diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index b9d69d55c7..bceac4403d 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -24,6 +24,7 @@ #include <vector> #include "base/allocator.h" +#include "base/dumpable.h" #include "base/histogram-inl.h" #include "base/stl_util.h" #include "common_throws.h" @@ -436,7 +437,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max MemMap* Heap::MapAnonymousPreferredAddress(const char* name, uint8_t* request_begin, size_t capacity, int prot_flags, std::string* out_error_str) { while (true) { - MemMap* map = MemMap::MapAnonymous(kMemMapSpaceName[0], request_begin, capacity, + MemMap* map = MemMap::MapAnonymous(name, request_begin, capacity, PROT_READ | PROT_WRITE, true, out_error_str); if (map != nullptr || request_begin == nullptr) { return map; @@ -2137,6 +2138,13 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus } else { LOG(FATAL) << "Invalid current allocator " << current_allocator_; } + if (IsGcConcurrent()) { + // Disable concurrent GC check so that we don't have spammy JNI requests. + // This gets recalculated in GrowForUtilization. It is important that it is disabled / + // calculated in the same thread so that there aren't any races that can cause it to become + // permanantly disabled. b/17942071 + concurrent_start_bytes_ = std::numeric_limits<size_t>::max(); + } CHECK(collector != nullptr) << "Could not find garbage collector with collector_type=" << static_cast<size_t>(collector_type_) << " and gc_type=" << gc_type; @@ -2178,7 +2186,7 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus << percent_free << "% free, " << PrettySize(current_heap_size) << "/" << PrettySize(total_memory) << ", " << "paused " << pause_string.str() << " total " << PrettyDuration((duration / 1000) * 1000); - VLOG(heap) << ConstDumpable<TimingLogger>(*current_gc_iteration_.GetTimings()); + VLOG(heap) << Dumpable<TimingLogger>(*current_gc_iteration_.GetTimings()); } FinishGC(self, gc_type); // Inform DDMS that a GC completed. @@ -2955,9 +2963,6 @@ void Heap::RequestConcurrentGC(Thread* self) { self->IsHandlingStackOverflow()) { return; } - // We already have a request pending, no reason to start more until we update - // concurrent_start_bytes_. - concurrent_start_bytes_ = std::numeric_limits<size_t>::max(); JNIEnv* env = self->GetJniEnv(); DCHECK(WellKnownClasses::java_lang_Daemons != nullptr); DCHECK(WellKnownClasses::java_lang_Daemons_requestGC != nullptr); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index c09dca8c4d..ff1e38b91a 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -32,7 +32,6 @@ #include "gc/collector_type.h" #include "gc/space/large_object_space.h" #include "globals.h" -#include "gtest/gtest.h" #include "instruction_set.h" #include "jni.h" #include "object_callbacks.h" @@ -602,9 +601,6 @@ class Heap { void RemoveRememberedSet(space::Space* space); bool IsCompilingBoot() const; - bool RunningOnValgrind() const { - return running_on_valgrind_; - } bool HasImageSpace() const; ReferenceProcessor* GetReferenceProcessor() { diff --git a/runtime/gc/reference_queue.h b/runtime/gc/reference_queue.h index dbf4abc238..4ef8478752 100644 --- a/runtime/gc/reference_queue.h +++ b/runtime/gc/reference_queue.h @@ -24,7 +24,6 @@ #include "atomic.h" #include "base/timing_logger.h" #include "globals.h" -#include "gtest/gtest.h" #include "jni.h" #include "object_callbacks.h" #include "offsets.h" @@ -45,44 +44,56 @@ class Heap; class ReferenceQueue { public: explicit ReferenceQueue(Mutex* lock); + // Enqueue a reference if is not already enqueued. Thread safe to call from multiple threads // since it uses a lock to avoid a race between checking for the references presence and adding // it. void AtomicEnqueueIfNotEnqueued(Thread* self, mirror::Reference* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(lock_); + // Enqueue a reference, unlike EnqueuePendingReference, enqueue reference checks that the // reference IsEnqueueable. Not thread safe, used when mutators are paused to minimize lock // overhead. void EnqueueReference(mirror::Reference* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void EnqueuePendingReference(mirror::Reference* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Reference* DequeuePendingReference() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Enqueues finalizer references with white referents. White referents are blackened, moved to the // zombie field, and the referent field is cleared. void EnqueueFinalizerReferences(ReferenceQueue* cleared_references, IsHeapReferenceMarkedCallback* is_marked_callback, MarkObjectCallback* mark_object_callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Walks the reference list marking any references subject to the reference clearing policy. // References with a black referent are removed from the list. References with white referents // biased toward saving are blackened and also removed from the list. void ForwardSoftReferences(IsHeapReferenceMarkedCallback* preserve_callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Unlink the reference list clearing references objects with white referents. Cleared references // registered to a reference queue are scheduled for appending by the heap worker thread. void ClearWhiteReferences(ReferenceQueue* cleared_references, IsHeapReferenceMarkedCallback* is_marked_callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void Dump(std::ostream& os) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool IsEmpty() const { return list_ == nullptr; } + void Clear() { list_ = nullptr; } + mirror::Reference* GetList() { return list_; } + // Visits list_, currently only used for the mark compact GC. void UpdateRoots(IsMarkedCallback* callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -90,10 +101,13 @@ class ReferenceQueue { private: // Lock, used for parallel GC reference enqueuing. It allows for multiple threads simultaneously // calling AtomicEnqueueIfNotEnqueued. - Mutex* lock_; + Mutex* const lock_; + // The actual reference list. Only a root for the mark compact GC since it will be null for other // GC types. mirror::Reference* list_; + + DISALLOW_COPY_AND_ASSIGN(ReferenceQueue); }; } // namespace gc diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 452af90750..d4790385d1 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -21,6 +21,7 @@ #include <random> +#include "base/macros.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "base/scoped_flock.h" @@ -118,7 +119,7 @@ static bool GenerateImage(const std::string& image_filename, InstructionSet imag std::string* error_msg) { const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString()); std::vector<std::string> boot_class_path; - Split(boot_class_path_string, ':', boot_class_path); + Split(boot_class_path_string, ':', &boot_class_path); if (boot_class_path.empty()) { *error_msg = "Failed to generate image because no boot class path specified"; return false; diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h index 7230116106..cfde460034 100644 --- a/runtime/gc/space/malloc_space.h +++ b/runtime/gc/space/malloc_space.h @@ -19,7 +19,7 @@ #include "space.h" -#include <iostream> +#include <ostream> #include <valgrind.h> #include <memcheck/memcheck.h> diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index d25694ad24..161eba9c1d 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -17,6 +17,9 @@ #include "rosalloc_space-inl.h" +#define ATRACE_TAG ATRACE_TAG_DALVIK +#include "cutils/trace.h" + #include "gc/accounting/card_table.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/heap.h" @@ -73,8 +76,9 @@ RosAllocSpace* RosAllocSpace::CreateFromMemMap(MemMap* mem_map, const std::strin uint8_t* begin = mem_map->Begin(); // TODO: Fix RosAllocSpace to support valgrind. There is currently some issues with // AllocationSize caused by redzones. b/12944686 - if (false && Runtime::Current()->GetHeap()->RunningOnValgrind()) { - LOG(FATAL) << "Unimplemented"; + if (Runtime::Current()->RunningOnValgrind()) { + UNIMPLEMENTED(FATAL); + UNREACHABLE(); } else { return new RosAllocSpace(name, mem_map, rosalloc, begin, end, begin + capacity, growth_limit, can_move_objects, starting_size, initial_size, low_memory_mode); diff --git a/runtime/handle.h b/runtime/handle.h index addb6638b6..6af3220561 100644 --- a/runtime/handle.h +++ b/runtime/handle.h @@ -20,6 +20,7 @@ #include "base/casts.h" #include "base/logging.h" #include "base/macros.h" +#include "base/value_object.h" #include "stack.h" namespace art { @@ -33,7 +34,7 @@ template<class T> class Handle; // a wrap pointer. Handles are generally allocated within HandleScopes. Handle is a super-class // of MutableHandle and doesn't support assignment operations. template<class T> -class Handle { +class Handle : public ValueObject { public: Handle() : reference_(nullptr) { } @@ -58,7 +59,7 @@ class Handle { } ALWAYS_INLINE T* Get() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return reference_->AsMirrorPtr(); + return down_cast<T*>(reference_->AsMirrorPtr()); } ALWAYS_INLINE jobject ToJObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -70,25 +71,25 @@ class Handle { } protected: - StackReference<T>* reference_; - template<typename S> explicit Handle(StackReference<S>* reference) - : reference_(reinterpret_cast<StackReference<T>*>(reference)) { + : reference_(reference) { } template<typename S> explicit Handle(const Handle<S>& handle) - : reference_(reinterpret_cast<StackReference<T>*>(handle.reference_)) { + : reference_(handle.reference_) { } - StackReference<T>* GetReference() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE { + StackReference<mirror::Object>* GetReference() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE { return reference_; } - ALWAYS_INLINE const StackReference<T>* GetReference() const + ALWAYS_INLINE const StackReference<mirror::Object>* GetReference() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return reference_; } + StackReference<mirror::Object>* reference_; + private: friend class BuildGenericJniFrameVisitor; template<class S> friend class Handle; @@ -121,8 +122,8 @@ class MutableHandle : public Handle<T> { } ALWAYS_INLINE T* Assign(T* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - StackReference<T>* ref = Handle<T>::GetReference(); - T* const old = ref->AsMirrorPtr(); + StackReference<mirror::Object>* ref = Handle<T>::GetReference(); + T* old = down_cast<T*>(ref->AsMirrorPtr()); ref->Assign(reference); return old; } @@ -132,7 +133,6 @@ class MutableHandle : public Handle<T> { : Handle<T>(handle) { } - protected: template<typename S> explicit MutableHandle(StackReference<S>* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : Handle<T>(reference) { @@ -153,7 +153,7 @@ class NullHandle : public Handle<T> { } private: - StackReference<T> null_ref_; + StackReference<mirror::Object> null_ref_; }; } // namespace art diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h index f795e387f0..13c939fc3c 100644 --- a/runtime/handle_scope.h +++ b/runtime/handle_scope.h @@ -159,7 +159,7 @@ class HandleWrapper : public MutableHandle<T> { } private: - T** obj_; + T** const obj_; }; // Scoped handle storage of a fixed size that is usually stack allocated. @@ -169,31 +169,10 @@ class PACKED(4) StackHandleScope FINAL : public HandleScope { explicit StackHandleScope(Thread* self); ~StackHandleScope(); - // Currently unused, using this GetReference instead of the one in HandleScope is preferred to - // avoid compiler optimizations incorrectly optimizing out of bound array accesses. - // TODO: Remove this when it is un-necessary. - ALWAYS_INLINE mirror::Object* GetReference(size_t i) const - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK_LT(i, kNumReferences); - return GetReferences()[i].AsMirrorPtr(); - } - - ALWAYS_INLINE MutableHandle<mirror::Object> GetHandle(size_t i) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK_LT(i, kNumReferences); - return MutableHandle<mirror::Object>(&GetReferences()[i]); - } - - ALWAYS_INLINE void SetReference(size_t i, mirror::Object* object) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK_LT(i, kNumReferences); - GetReferences()[i].Assign(object); - } - template<class T> MutableHandle<T> NewHandle(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { SetReference(pos_, object); - MutableHandle<T> h(GetHandle(pos_)); + MutableHandle<T> h(GetHandle<T>(pos_)); pos_++; return h; } @@ -201,12 +180,25 @@ class PACKED(4) StackHandleScope FINAL : public HandleScope { template<class T> HandleWrapper<T> NewHandleWrapper(T** object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { SetReference(pos_, *object); - MutableHandle<T> h(GetHandle(pos_)); + MutableHandle<T> h(GetHandle<T>(pos_)); pos_++; return HandleWrapper<T>(object, h); } private: + template<class T> + ALWAYS_INLINE MutableHandle<T> GetHandle(size_t i) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK_LT(i, kNumReferences); + return MutableHandle<T>(&GetReferences()[i]); + } + + ALWAYS_INLINE void SetReference(size_t i, mirror::Object* object) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK_LT(i, kNumReferences); + GetReferences()[i].Assign(object); + } + // Reference storage needs to be first as expected by the HandleScope layout. StackReference<mirror::Object> storage_[kNumReferences]; diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc index c1455fd0bc..4d177a32d8 100644 --- a/runtime/indirect_reference_table.cc +++ b/runtime/indirect_reference_table.cc @@ -126,7 +126,7 @@ IndirectRef IndirectReferenceTable::Add(uint32_t cookie, mirror::Object* obj) { } table_[index].Add(obj); result = ToIndirectRef(index); - if (false) { + if ((false)) { LOG(INFO) << "+++ added at " << ExtractIndex(result) << " top=" << segment_state_.parts.topIndex << " holes=" << segment_state_.parts.numHoles; } @@ -193,7 +193,7 @@ bool IndirectReferenceTable::Remove(uint32_t cookie, IndirectRef iref) { int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles; if (numHoles != 0) { while (--topIndex > bottomIndex && numHoles != 0) { - if (false) { + if ((false)) { LOG(INFO) << "+++ checking for hole at " << topIndex - 1 << " (cookie=" << cookie << ") val=" << table_[topIndex - 1].GetReference()->Read<kWithoutReadBarrier>(); @@ -201,7 +201,7 @@ bool IndirectReferenceTable::Remove(uint32_t cookie, IndirectRef iref) { if (!table_[topIndex - 1].GetReference()->IsNull()) { break; } - if (false) { + if ((false)) { LOG(INFO) << "+++ ate hole at " << (topIndex - 1); } numHoles--; @@ -210,7 +210,7 @@ bool IndirectReferenceTable::Remove(uint32_t cookie, IndirectRef iref) { segment_state_.parts.topIndex = topIndex; } else { segment_state_.parts.topIndex = topIndex-1; - if (false) { + if ((false)) { LOG(INFO) << "+++ ate last entry " << topIndex - 1; } } @@ -228,7 +228,7 @@ bool IndirectReferenceTable::Remove(uint32_t cookie, IndirectRef iref) { *table_[idx].GetReference() = GcRoot<mirror::Object>(nullptr); segment_state_.parts.numHoles++; - if (false) { + if ((false)) { LOG(INFO) << "+++ left hole at " << idx << ", holes=" << segment_state_.parts.numHoles; } } diff --git a/runtime/instruction_set.cc b/runtime/instruction_set.cc index 644e0556b1..0ca32fe705 100644 --- a/runtime/instruction_set.cc +++ b/runtime/instruction_set.cc @@ -16,6 +16,13 @@ #include "instruction_set.h" +#include <signal.h> +#include <fstream> + +#include "base/casts.h" +#include "base/stringprintf.h" +#include "utils.h" + namespace art { const char* GetInstructionSetString(const InstructionSet isa) { @@ -35,7 +42,7 @@ const char* GetInstructionSetString(const InstructionSet isa) { return "none"; default: LOG(FATAL) << "Unknown ISA " << isa; - return nullptr; + UNREACHABLE(); } } @@ -117,15 +124,385 @@ size_t GetStackOverflowReservedBytes(InstructionSet isa) { } } -std::string InstructionSetFeatures::GetFeatureString() const { +const InstructionSetFeatures* InstructionSetFeatures::FromVariant(InstructionSet isa, + const std::string& variant, + std::string* error_msg) { + const InstructionSetFeatures* result; + switch (isa) { + case kArm: + case kThumb2: + result = ArmInstructionSetFeatures::FromVariant(variant, error_msg); + break; + default: + result = UnknownInstructionSetFeatures::Unknown(isa); + break; + } + CHECK_EQ(result == nullptr, error_msg->size() != 0); + return result; +} + +const InstructionSetFeatures* InstructionSetFeatures::FromFeatureString(InstructionSet isa, + const std::string& feature_list, + std::string* error_msg) { + const InstructionSetFeatures* result; + switch (isa) { + case kArm: + case kThumb2: + result = ArmInstructionSetFeatures::FromFeatureString(feature_list, error_msg); + break; + default: + result = UnknownInstructionSetFeatures::Unknown(isa); + break; + } + // TODO: warn if feature_list doesn't agree with result's GetFeatureList(). + CHECK_EQ(result == nullptr, error_msg->size() != 0); + return result; +} + +const InstructionSetFeatures* InstructionSetFeatures::FromBitmap(InstructionSet isa, + uint32_t bitmap) { + const InstructionSetFeatures* result; + switch (isa) { + case kArm: + case kThumb2: + result = ArmInstructionSetFeatures::FromBitmap(bitmap); + break; + default: + result = UnknownInstructionSetFeatures::Unknown(isa); + break; + } + CHECK_EQ(bitmap, result->AsBitmap()); + return result; +} + +const InstructionSetFeatures* InstructionSetFeatures::FromCppDefines() { + const InstructionSetFeatures* result; + switch (kRuntimeISA) { + case kArm: + case kThumb2: + result = ArmInstructionSetFeatures::FromCppDefines(); + break; + default: + result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA); + break; + } + return result; +} + + +const InstructionSetFeatures* InstructionSetFeatures::FromCpuInfo() { + const InstructionSetFeatures* result; + switch (kRuntimeISA) { + case kArm: + case kThumb2: + result = ArmInstructionSetFeatures::FromCpuInfo(); + break; + default: + result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA); + break; + } + return result; +} + +const InstructionSetFeatures* InstructionSetFeatures::FromHwcap() { + const InstructionSetFeatures* result; + switch (kRuntimeISA) { + case kArm: + case kThumb2: + result = ArmInstructionSetFeatures::FromHwcap(); + break; + default: + result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA); + break; + } + return result; +} + +const InstructionSetFeatures* InstructionSetFeatures::FromAssembly() { + const InstructionSetFeatures* result; + switch (kRuntimeISA) { + case kArm: + case kThumb2: + result = ArmInstructionSetFeatures::FromAssembly(); + break; + default: + result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA); + break; + } + return result; +} + +const ArmInstructionSetFeatures* InstructionSetFeatures::AsArmInstructionSetFeatures() const { + DCHECK_EQ(kArm, GetInstructionSet()); + return down_cast<const ArmInstructionSetFeatures*>(this); +} + +std::ostream& operator<<(std::ostream& os, const InstructionSetFeatures& rhs) { + os << "ISA: " << rhs.GetInstructionSet() << " Feature string: " << rhs.GetFeatureString(); + return os; +} + +const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromFeatureString( + const std::string& feature_list, std::string* error_msg) { + std::vector<std::string> features; + Split(feature_list, ',', &features); + bool has_lpae = false; + bool has_div = false; + for (auto i = features.begin(); i != features.end(); i++) { + std::string feature = Trim(*i); + if (feature == "default" || feature == "none") { + // Nothing to do. + } else if (feature == "div") { + has_div = true; + } else if (feature == "nodiv") { + has_div = false; + } else if (feature == "lpae") { + has_lpae = true; + } else if (feature == "nolpae") { + has_lpae = false; + } else { + *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str()); + return nullptr; + } + } + return new ArmInstructionSetFeatures(has_lpae, has_div); +} + +const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromVariant( + const std::string& variant, std::string* error_msg) { + // Look for variants that have divide support. + bool has_div = false; + { + static const char* arm_variants_with_div[] = { + "cortex-a7", "cortex-a12", "cortex-a15", "cortex-a17", "cortex-a53", "cortex-a57", + "cortex-m3", "cortex-m4", "cortex-r4", "cortex-r5", + "cyclone", "denver", "krait", "swift" + }; + for (const char* div_variant : arm_variants_with_div) { + if (variant == div_variant) { + has_div = true; + break; + } + } + } + // Look for variants that have LPAE support. + bool has_lpae = false; + { + static const char* arm_variants_with_lpae[] = { + "cortex-a7", "cortex-a15", "krait", "denver" + }; + for (const char* lpae_variant : arm_variants_with_lpae) { + if (variant == lpae_variant) { + has_lpae = true; + break; + } + } + } + if (has_div == false && has_lpae == false) { + // Avoid unsupported variants. + static const char* unsupported_arm_variants[] = { + // ARM processors that aren't ARMv7 compatible aren't supported. + "arm2", "arm250", "arm3", "arm6", "arm60", "arm600", "arm610", "arm620", + "cortex-m0", "cortex-m0plus", "cortex-m1", + "fa526", "fa626", "fa606te", "fa626te", "fmp626", "fa726te", + "iwmmxt", "iwmmxt2", + "strongarm", "strongarm110", "strongarm1100", "strongarm1110", + "xscale" + }; + for (const char* us_variant : unsupported_arm_variants) { + if (variant == us_variant) { + *error_msg = StringPrintf("Attempt to use unsupported ARM variant: %s", us_variant); + return nullptr; + } + } + // Warn if the variant is unknown. + // TODO: some of the variants below may have feature support, but that support is currently + // unknown so we'll choose conservative (sub-optimal) defaults without warning. + // TODO: some of the architectures may not support all features required by ART and should be + // moved to unsupported_arm_variants[] above. + static const char* arm_variants_without_known_features[] = { + "arm7", "arm7m", "arm7d", "arm7dm", "arm7di", "arm7dmi", "arm70", "arm700", "arm700i", + "arm710", "arm710c", "arm7100", "arm720", "arm7500", "arm7500fe", "arm7tdmi", "arm7tdmi-s", + "arm710t", "arm720t", "arm740t", + "arm8", "arm810", + "arm9", "arm9e", "arm920", "arm920t", "arm922t", "arm946e-s", "arm966e-s", "arm968e-s", + "arm926ej-s", "arm940t", "arm9tdmi", + "arm10tdmi", "arm1020t", "arm1026ej-s", "arm10e", "arm1020e", "arm1022e", + "arm1136j-s", "arm1136jf-s", + "arm1156t2-s", "arm1156t2f-s", "arm1176jz-s", "arm1176jzf-s", + "cortex-a5", "cortex-a8", "cortex-a9", "cortex-a9-mp", "cortex-r4f", + "marvell-pj4", "mpcore", "mpcorenovfp" + }; + bool found = false; + for (const char* ff_variant : arm_variants_without_known_features) { + if (variant == ff_variant) { + found = true; + break; + } + } + if (!found) { + LOG(WARNING) << "Unknown instruction set features for ARM CPU variant (" << variant + << ") using conservative defaults"; + } + } + return new ArmInstructionSetFeatures(has_lpae, has_div); +} + +const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromBitmap(uint32_t bitmap) { + bool has_lpae = (bitmap & kLpaeBitfield) != 0; + bool has_div = (bitmap & kDivBitfield) != 0; + return new ArmInstructionSetFeatures(has_lpae, has_div); +} + +const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromCppDefines() { +#if defined(__ARM_ARCH_EXT_IDIV__) + bool has_div = true; +#else + bool has_div = false; +#endif +#if defined(__ARM_FEATURE_LPAE) + bool has_lpae = true; +#else + bool has_lpae = false; +#endif + return new ArmInstructionSetFeatures(has_lpae, has_div); +} + +const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromCpuInfo() { + // Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that + // the kernel puts the appropriate feature flags in here. Sometimes it doesn't. + bool has_lpae = false; + bool has_div = false; + + std::ifstream in("/proc/cpuinfo"); + if (!in.fail()) { + while (!in.eof()) { + std::string line; + std::getline(in, line); + if (!in.eof()) { + LOG(INFO) << "cpuinfo line: " << line; + if (line.find("Features") != std::string::npos) { + LOG(INFO) << "found features"; + if (line.find("idivt") != std::string::npos) { + // We always expect both ARM and Thumb divide instructions to be available or not + // available. + CHECK_NE(line.find("idiva"), std::string::npos); + has_div = true; + } + if (line.find("lpae") != std::string::npos) { + has_lpae = true; + } + } + } + } + in.close(); + } else { + LOG(INFO) << "Failed to open /proc/cpuinfo"; + } + return new ArmInstructionSetFeatures(has_lpae, has_div); +} + +#if defined(HAVE_ANDROID_OS) && defined(__arm__) +#include <sys/auxv.h> +#include <asm/hwcap.h> +#endif + +const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromHwcap() { + bool has_lpae = false; + bool has_div = false; + +#if defined(HAVE_ANDROID_OS) && defined(__arm__) + uint64_t hwcaps = getauxval(AT_HWCAP); + LOG(INFO) << "hwcaps=" << hwcaps; + if ((hwcaps & HWCAP_IDIVT) != 0) { + // We always expect both ARM and Thumb divide instructions to be available or not + // available. + CHECK_NE(hwcaps & HWCAP_IDIVA, 0U); + has_div = true; + } + if ((hwcaps & HWCAP_LPAE) != 0) { + has_lpae = true; + } +#endif + + return new ArmInstructionSetFeatures(has_lpae, has_div); +} + +// A signal handler called by a fault for an illegal instruction. We record the fact in r0 +// and then increment the PC in the signal context to return to the next instruction. We know the +// instruction is an sdiv (4 bytes long). +static void bad_divide_inst_handle(int signo, siginfo_t* si, void* data) { + UNUSED(signo); + UNUSED(si); +#if defined(__arm__) + struct ucontext *uc = (struct ucontext *)data; + struct sigcontext *sc = &uc->uc_mcontext; + sc->arm_r0 = 0; // Set R0 to #0 to signal error. + sc->arm_pc += 4; // Skip offending instruction. +#else + UNUSED(data); +#endif +} + +#if defined(__arm__) +extern "C" bool artCheckForARMSDIVInstruction(); +#endif + +const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromAssembly() { + // See if have a sdiv instruction. Register a signal handler and try to execute an sdiv + // instruction. If we get a SIGILL then it's not supported. + struct sigaction sa, osa; + sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO; + sa.sa_sigaction = bad_divide_inst_handle; + sigaction(SIGILL, &sa, &osa); + + bool has_div = false; +#if defined(__arm__) + if (artCheckForARMSDIVInstruction()) { + has_div = true; + } +#endif + + // Restore the signal handler. + sigaction(SIGILL, &osa, nullptr); + + // Use compile time features to "detect" LPAE support. + // TODO: write an assembly LPAE support test. +#if defined(__ARM_FEATURE_LPAE) + bool has_lpae = true; +#else + bool has_lpae = false; +#endif + return new ArmInstructionSetFeatures(has_lpae, has_div); +} + + +bool ArmInstructionSetFeatures::Equals(const InstructionSetFeatures* other) const { + if (kArm != other->GetInstructionSet()) { + return false; + } + const ArmInstructionSetFeatures* other_as_arm = other->AsArmInstructionSetFeatures(); + return has_lpae_ == other_as_arm->has_lpae_ && has_div_ == other_as_arm->has_div_; +} + +uint32_t ArmInstructionSetFeatures::AsBitmap() const { + return (has_lpae_ ? kLpaeBitfield : 0) | (has_div_ ? kDivBitfield : 0); +} + +std::string ArmInstructionSetFeatures::GetFeatureString() const { std::string result; - if ((mask_ & kHwDiv) != 0) { - result += "div"; + if (has_div_) { + result += ",div"; + } + if (has_lpae_) { + result += ",lpae"; } if (result.size() == 0) { - result = "none"; + return "none"; + } else { + // Strip leading comma. + return result.substr(1, result.size()); } - return result; } } // namespace art diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h index de6d0f47d9..529fa0c05f 100644 --- a/runtime/instruction_set.h +++ b/runtime/instruction_set.h @@ -22,6 +22,7 @@ #include "base/logging.h" // Logging is required for FATAL in the helper functions. #include "base/macros.h" +#include "base/value_object.h" #include "globals.h" // For KB. namespace art { @@ -177,53 +178,163 @@ static inline size_t GetBytesPerFprSpillLocation(InstructionSet isa) { size_t GetStackOverflowReservedBytes(InstructionSet isa); -enum InstructionFeatures { - kHwDiv = 0x1, // Supports hardware divide. - kHwLpae = 0x2, // Supports Large Physical Address Extension. +class ArmInstructionSetFeatures; + +// Abstraction used to describe features of a different instruction sets. +class InstructionSetFeatures { + public: + // Process a CPU variant string for the given ISA and create an InstructionSetFeatures. + static const InstructionSetFeatures* FromVariant(InstructionSet isa, + const std::string& variant, + std::string* error_msg); + + // Parse a string of the form "div,lpae" and create an InstructionSetFeatures. + static const InstructionSetFeatures* FromFeatureString(InstructionSet isa, + const std::string& feature_list, + std::string* error_msg); + + // Parse a bitmap for the given isa and create an InstructionSetFeatures. + static const InstructionSetFeatures* FromBitmap(InstructionSet isa, uint32_t bitmap); + + // Turn C pre-processor #defines into the equivalent instruction set features for kRuntimeISA. + static const InstructionSetFeatures* FromCppDefines(); + + // Process /proc/cpuinfo and use kRuntimeISA to produce InstructionSetFeatures. + static const InstructionSetFeatures* FromCpuInfo(); + + // Process the auxiliary vector AT_HWCAP entry and use kRuntimeISA to produce + // InstructionSetFeatures. + static const InstructionSetFeatures* FromHwcap(); + + // Use assembly tests of the current runtime (ie kRuntimeISA) to determine the + // InstructionSetFeatures. This works around kernel bugs in AT_HWCAP and /proc/cpuinfo. + static const InstructionSetFeatures* FromAssembly(); + + // Are these features the same as the other given features? + virtual bool Equals(const InstructionSetFeatures* other) const = 0; + + // Return the ISA these features relate to. + virtual InstructionSet GetInstructionSet() const = 0; + + // Return a bitmap that represents the features. ISA specific. + virtual uint32_t AsBitmap() const = 0; + + // Return a string of the form "div,lpae" or "none". + virtual std::string GetFeatureString() const = 0; + + // Down cast this ArmInstructionFeatures. + const ArmInstructionSetFeatures* AsArmInstructionSetFeatures() const; + + virtual ~InstructionSetFeatures() {} + + protected: + InstructionSetFeatures() {} + + private: + DISALLOW_COPY_AND_ASSIGN(InstructionSetFeatures); }; +std::ostream& operator<<(std::ostream& os, const InstructionSetFeatures& rhs); -// This is a bitmask of supported features per architecture. -class PACKED(4) InstructionSetFeatures { +// Instruction set features relevant to the ARM architecture. +class ArmInstructionSetFeatures FINAL : public InstructionSetFeatures { public: - InstructionSetFeatures() : mask_(0) {} - explicit InstructionSetFeatures(uint32_t mask) : mask_(mask) {} + // Process a CPU variant string like "krait" or "cortex-a15" and create InstructionSetFeatures. + static const ArmInstructionSetFeatures* FromVariant(const std::string& variant, + std::string* error_msg); - static InstructionSetFeatures GuessInstructionSetFeatures(); + // Parse a string of the form "div,lpae" and create an InstructionSetFeatures. + static const ArmInstructionSetFeatures* FromFeatureString(const std::string& feature_list, + std::string* error_msg); - bool HasDivideInstruction() const { - return (mask_ & kHwDiv) != 0; + // Parse a bitmap and create an InstructionSetFeatures. + static const ArmInstructionSetFeatures* FromBitmap(uint32_t bitmap); + + // Turn C pre-processor #defines into the equivalent instruction set features. + static const ArmInstructionSetFeatures* FromCppDefines(); + + // Process /proc/cpuinfo and use kRuntimeISA to produce InstructionSetFeatures. + static const ArmInstructionSetFeatures* FromCpuInfo(); + + // Process the auxiliary vector AT_HWCAP entry and use kRuntimeISA to produce + // InstructionSetFeatures. + static const ArmInstructionSetFeatures* FromHwcap(); + + // Use assembly tests of the current runtime (ie kRuntimeISA) to determine the + // InstructionSetFeatures. This works around kernel bugs in AT_HWCAP and /proc/cpuinfo. + static const ArmInstructionSetFeatures* FromAssembly(); + + bool Equals(const InstructionSetFeatures* other) const OVERRIDE; + + InstructionSet GetInstructionSet() const OVERRIDE { + return kArm; } - void SetHasDivideInstruction(bool v) { - mask_ = (mask_ & ~kHwDiv) | (v ? kHwDiv : 0); + uint32_t AsBitmap() const OVERRIDE; + + // Return a string of the form "div,lpae" or "none". + std::string GetFeatureString() const OVERRIDE; + + // Is the divide instruction feature enabled? + bool HasDivideInstruction() const { + return has_div_; } + // Is the Large Physical Address Extension (LPAE) instruction feature enabled? When true code can + // be used that assumes double register loads and stores (ldrd, strd) don't tear. bool HasLpae() const { - return (mask_ & kHwLpae) != 0; + return has_lpae_; } - void SetHasLpae(bool v) { - mask_ = (mask_ & ~kHwLpae) | (v ? kHwLpae : 0); + virtual ~ArmInstructionSetFeatures() {} + + private: + ArmInstructionSetFeatures(bool has_lpae, bool has_div) + : has_lpae_(has_lpae), has_div_(has_div) { } - std::string GetFeatureString() const; + // Bitmap positions for encoding features as a bitmap. + enum { + kDivBitfield = 1, + kLpaeBitfield = 2, + }; - // Other features in here. + const bool has_lpae_; + const bool has_div_; - bool operator==(const InstructionSetFeatures &peer) const { - return mask_ == peer.mask_; + DISALLOW_COPY_AND_ASSIGN(ArmInstructionSetFeatures); +}; + +// A class used for instruction set features on ISAs that don't yet have any features defined. +class UnknownInstructionSetFeatures FINAL : public InstructionSetFeatures { + public: + static const UnknownInstructionSetFeatures* Unknown(InstructionSet isa) { + return new UnknownInstructionSetFeatures(isa); } - bool operator!=(const InstructionSetFeatures &peer) const { - return mask_ != peer.mask_; + bool Equals(const InstructionSetFeatures* other) const OVERRIDE { + return isa_ == other->GetInstructionSet(); } - bool operator<=(const InstructionSetFeatures &peer) const { - return (mask_ & peer.mask_) == mask_; + InstructionSet GetInstructionSet() const OVERRIDE { + return isa_; } + uint32_t AsBitmap() const OVERRIDE { + return 0; + } + + std::string GetFeatureString() const OVERRIDE { + return "none"; + } + + virtual ~UnknownInstructionSetFeatures() {} + private: - uint32_t mask_; + explicit UnknownInstructionSetFeatures(InstructionSet isa) : isa_(isa) {} + + const InstructionSet isa_; + + DISALLOW_COPY_AND_ASSIGN(UnknownInstructionSetFeatures); }; // The following definitions create return types for two word-sized entities that will be passed diff --git a/runtime/instruction_set_test.cc b/runtime/instruction_set_test.cc index 80191b1387..3f2d16bd2c 100644 --- a/runtime/instruction_set_test.cc +++ b/runtime/instruction_set_test.cc @@ -16,6 +16,7 @@ #include "instruction_set.h" +#include "base/stringprintf.h" #include "common_runtime_test.h" namespace art { @@ -50,4 +51,229 @@ TEST_F(InstructionSetTest, PointerSize) { EXPECT_EQ(sizeof(void*), GetInstructionSetPointerSize(kRuntimeISA)); } +TEST_F(InstructionSetTest, X86Features) { + // Build features for a 32-bit x86 atom processor. + std::string error_msg; + std::unique_ptr<const InstructionSetFeatures> x86_features( + InstructionSetFeatures::FromVariant(kX86, "atom", &error_msg)); + ASSERT_TRUE(x86_features.get() != nullptr) << error_msg; + EXPECT_EQ(x86_features->GetInstructionSet(), kX86); + EXPECT_TRUE(x86_features->Equals(x86_features.get())); + EXPECT_STREQ("none", x86_features->GetFeatureString().c_str()); + EXPECT_EQ(x86_features->AsBitmap(), 0U); + + // Build features for a 32-bit x86 default processor. + std::unique_ptr<const InstructionSetFeatures> x86_default_features( + InstructionSetFeatures::FromFeatureString(kX86, "default", &error_msg)); + ASSERT_TRUE(x86_default_features.get() != nullptr) << error_msg; + EXPECT_EQ(x86_default_features->GetInstructionSet(), kX86); + EXPECT_TRUE(x86_default_features->Equals(x86_default_features.get())); + EXPECT_STREQ("none", x86_default_features->GetFeatureString().c_str()); + EXPECT_EQ(x86_default_features->AsBitmap(), 0U); + + // Build features for a 64-bit x86-64 atom processor. + std::unique_ptr<const InstructionSetFeatures> x86_64_features( + InstructionSetFeatures::FromVariant(kX86_64, "atom", &error_msg)); + ASSERT_TRUE(x86_64_features.get() != nullptr) << error_msg; + EXPECT_EQ(x86_64_features->GetInstructionSet(), kX86_64); + EXPECT_TRUE(x86_64_features->Equals(x86_64_features.get())); + EXPECT_STREQ("none", x86_64_features->GetFeatureString().c_str()); + EXPECT_EQ(x86_64_features->AsBitmap(), 0U); + + EXPECT_FALSE(x86_64_features->Equals(x86_features.get())); + EXPECT_FALSE(x86_64_features->Equals(x86_default_features.get())); + EXPECT_TRUE(x86_features->Equals(x86_default_features.get())); +} + +TEST_F(InstructionSetTest, ArmFeaturesFromVariant) { + // Build features for a 32-bit ARM krait processor. + std::string error_msg; + std::unique_ptr<const InstructionSetFeatures> krait_features( + InstructionSetFeatures::FromVariant(kArm, "krait", &error_msg)); + ASSERT_TRUE(krait_features.get() != nullptr) << error_msg; + + ASSERT_EQ(krait_features->GetInstructionSet(), kArm); + EXPECT_TRUE(krait_features->Equals(krait_features.get())); + EXPECT_TRUE(krait_features->AsArmInstructionSetFeatures()->HasDivideInstruction()); + EXPECT_TRUE(krait_features->AsArmInstructionSetFeatures()->HasLpae()); + EXPECT_STREQ("div,lpae", krait_features->GetFeatureString().c_str()); + EXPECT_EQ(krait_features->AsBitmap(), 3U); + + // Build features for a 32-bit ARM denver processor. + std::unique_ptr<const InstructionSetFeatures> denver_features( + InstructionSetFeatures::FromVariant(kArm, "denver", &error_msg)); + ASSERT_TRUE(denver_features.get() != nullptr) << error_msg; + + EXPECT_TRUE(denver_features->Equals(denver_features.get())); + EXPECT_TRUE(denver_features->Equals(krait_features.get())); + EXPECT_TRUE(krait_features->Equals(denver_features.get())); + EXPECT_TRUE(denver_features->AsArmInstructionSetFeatures()->HasDivideInstruction()); + EXPECT_TRUE(denver_features->AsArmInstructionSetFeatures()->HasLpae()); + EXPECT_STREQ("div,lpae", denver_features->GetFeatureString().c_str()); + EXPECT_EQ(denver_features->AsBitmap(), 3U); + + // Build features for a 32-bit ARMv7 processor. + std::unique_ptr<const InstructionSetFeatures> arm7_features( + InstructionSetFeatures::FromVariant(kArm, "arm7", &error_msg)); + ASSERT_TRUE(arm7_features.get() != nullptr) << error_msg; + + EXPECT_TRUE(arm7_features->Equals(arm7_features.get())); + EXPECT_FALSE(arm7_features->Equals(krait_features.get())); + EXPECT_FALSE(krait_features->Equals(arm7_features.get())); + EXPECT_FALSE(arm7_features->AsArmInstructionSetFeatures()->HasDivideInstruction()); + EXPECT_FALSE(arm7_features->AsArmInstructionSetFeatures()->HasLpae()); + EXPECT_STREQ("none", arm7_features->GetFeatureString().c_str()); + EXPECT_EQ(arm7_features->AsBitmap(), 0U); + + // ARM6 is not a supported architecture variant. + std::unique_ptr<const InstructionSetFeatures> arm6_features( + InstructionSetFeatures::FromVariant(kArm, "arm6", &error_msg)); + EXPECT_TRUE(arm6_features.get() == nullptr); + EXPECT_NE(error_msg.size(), 0U); +} + +TEST_F(InstructionSetTest, ArmFeaturesFromString) { + // Build features for a 32-bit ARM with LPAE and div processor. + std::string error_msg; + std::unique_ptr<const InstructionSetFeatures> krait_features( + InstructionSetFeatures::FromFeatureString(kArm, "lpae,div", &error_msg)); + ASSERT_TRUE(krait_features.get() != nullptr) << error_msg; + + ASSERT_EQ(krait_features->GetInstructionSet(), kArm); + EXPECT_TRUE(krait_features->Equals(krait_features.get())); + EXPECT_TRUE(krait_features->AsArmInstructionSetFeatures()->HasDivideInstruction()); + EXPECT_TRUE(krait_features->AsArmInstructionSetFeatures()->HasLpae()); + EXPECT_STREQ("div,lpae", krait_features->GetFeatureString().c_str()); + EXPECT_EQ(krait_features->AsBitmap(), 3U); + + // Build features for a 32-bit ARM processor with LPAE and div flipped. + std::unique_ptr<const InstructionSetFeatures> denver_features( + InstructionSetFeatures::FromFeatureString(kArm, "div,lpae", &error_msg)); + ASSERT_TRUE(denver_features.get() != nullptr) << error_msg; + + EXPECT_TRUE(denver_features->Equals(denver_features.get())); + EXPECT_TRUE(denver_features->Equals(krait_features.get())); + EXPECT_TRUE(krait_features->Equals(denver_features.get())); + EXPECT_TRUE(denver_features->AsArmInstructionSetFeatures()->HasDivideInstruction()); + EXPECT_TRUE(denver_features->AsArmInstructionSetFeatures()->HasLpae()); + EXPECT_STREQ("div,lpae", denver_features->GetFeatureString().c_str()); + EXPECT_EQ(denver_features->AsBitmap(), 3U); + + // Build features for a 32-bit default ARM processor. + std::unique_ptr<const InstructionSetFeatures> arm7_features( + InstructionSetFeatures::FromFeatureString(kArm, "default", &error_msg)); + ASSERT_TRUE(arm7_features.get() != nullptr) << error_msg; + + EXPECT_TRUE(arm7_features->Equals(arm7_features.get())); + EXPECT_FALSE(arm7_features->Equals(krait_features.get())); + EXPECT_FALSE(krait_features->Equals(arm7_features.get())); + EXPECT_FALSE(arm7_features->AsArmInstructionSetFeatures()->HasDivideInstruction()); + EXPECT_FALSE(arm7_features->AsArmInstructionSetFeatures()->HasLpae()); + EXPECT_STREQ("none", arm7_features->GetFeatureString().c_str()); + EXPECT_EQ(arm7_features->AsBitmap(), 0U); +} + +#ifdef HAVE_ANDROID_OS +#include "cutils/properties.h" + +TEST_F(InstructionSetTest, FeaturesFromSystemPropertyVariant) { + // Take the default set of instruction features from the build. + std::unique_ptr<const InstructionSetFeatures> instruction_set_features( + InstructionSetFeatures::FromCppDefines()); + + // Read the features property. + std::string key = StringPrintf("dalvik.vm.isa.%s.variant", GetInstructionSetString(kRuntimeISA)); + char dex2oat_isa_variant[PROPERTY_VALUE_MAX]; + if (property_get(key.c_str(), dex2oat_isa_variant, nullptr) > 0) { + // Use features from property to build InstructionSetFeatures and check against build's + // features. + std::string error_msg; + std::unique_ptr<const InstructionSetFeatures> property_features( + InstructionSetFeatures::FromVariant(kRuntimeISA, dex2oat_isa_variant, &error_msg)); + ASSERT_TRUE(property_features.get() != nullptr) << error_msg; + + EXPECT_TRUE(property_features->Equals(instruction_set_features.get())) + << "System property features: " << *property_features.get() + << "\nFeatures from build: " << *instruction_set_features.get(); + } +} + +TEST_F(InstructionSetTest, FeaturesFromSystemPropertyString) { + // Take the default set of instruction features from the build. + std::unique_ptr<const InstructionSetFeatures> instruction_set_features( + InstructionSetFeatures::FromCppDefines()); + + // Read the features property. + std::string key = StringPrintf("dalvik.vm.isa.%s.features", GetInstructionSetString(kRuntimeISA)); + char dex2oat_isa_features[PROPERTY_VALUE_MAX]; + if (property_get(key.c_str(), dex2oat_isa_features, nullptr) > 0) { + // Use features from property to build InstructionSetFeatures and check against build's + // features. + std::string error_msg; + std::unique_ptr<const InstructionSetFeatures> property_features( + InstructionSetFeatures::FromFeatureString(kRuntimeISA, dex2oat_isa_features, &error_msg)); + ASSERT_TRUE(property_features.get() != nullptr) << error_msg; + + EXPECT_TRUE(property_features->Equals(instruction_set_features.get())) + << "System property features: " << *property_features.get() + << "\nFeatures from build: " << *instruction_set_features.get(); + } +} +#endif + +#if defined(__arm__) +TEST_F(InstructionSetTest, DISABLED_FeaturesFromCpuInfo) { + LOG(WARNING) << "Test disabled due to buggy ARM kernels"; +#else +TEST_F(InstructionSetTest, FeaturesFromCpuInfo) { +#endif + // Take the default set of instruction features from the build. + std::unique_ptr<const InstructionSetFeatures> instruction_set_features( + InstructionSetFeatures::FromCppDefines()); + + // Check we get the same instruction set features using /proc/cpuinfo. + std::unique_ptr<const InstructionSetFeatures> cpuinfo_features( + InstructionSetFeatures::FromCpuInfo()); + EXPECT_TRUE(cpuinfo_features->Equals(instruction_set_features.get())) + << "CPU Info features: " << *cpuinfo_features.get() + << "\nFeatures from build: " << *instruction_set_features.get(); +} + +#if defined(__arm__) +TEST_F(InstructionSetTest, DISABLED_FeaturesFromHwcap) { + LOG(WARNING) << "Test disabled due to buggy ARM kernels"; +#else +TEST_F(InstructionSetTest, FeaturesFromHwcap) { +#endif + // Take the default set of instruction features from the build. + std::unique_ptr<const InstructionSetFeatures> instruction_set_features( + InstructionSetFeatures::FromCppDefines()); + + // Check we get the same instruction set features using AT_HWCAP. + std::unique_ptr<const InstructionSetFeatures> hwcap_features( + InstructionSetFeatures::FromHwcap()); + EXPECT_TRUE(hwcap_features->Equals(instruction_set_features.get())) + << "Hwcap features: " << *hwcap_features.get() + << "\nFeatures from build: " << *instruction_set_features.get(); +} + + +#if defined(__arm__) +TEST_F(InstructionSetTest, DISABLED_FeaturesFromAssembly) { + LOG(WARNING) << "Test disabled due to buggy ARM kernels"; +#else +TEST_F(InstructionSetTest, FeaturesFromAssembly) { +#endif + // Take the default set of instruction features from the build. + std::unique_ptr<const InstructionSetFeatures> instruction_set_features( + InstructionSetFeatures::FromCppDefines()); + + // Check we get the same instruction set features using assembly tests. + std::unique_ptr<const InstructionSetFeatures> assembly_features( + InstructionSetFeatures::FromAssembly()); + EXPECT_TRUE(assembly_features->Equals(instruction_set_features.get())) + << "Assembly features: " << *assembly_features.get() + << "\nFeatures from build: " << *instruction_set_features.get(); +} + } // namespace art diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 15be6b752b..fc3da3635b 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -18,13 +18,17 @@ #include <sys/uio.h> +#include <sstream> + #include "arch/context.h" #include "atomic.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" #include "debugger.h" #include "dex_file-inl.h" +#include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" +#include "entrypoints/runtime_asm_entrypoints.h" #include "gc_root-inl.h" #include "interpreter/interpreter.h" #include "mirror/art_method-inl.h" @@ -33,9 +37,6 @@ #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" #include "nth_caller_visitor.h" -#if !defined(ART_USE_PORTABLE_COMPILER) -#include "entrypoints/quick/quick_entrypoints.h" -#endif #include "os.h" #include "scoped_thread_state_change.h" #include "thread.h" @@ -95,21 +96,20 @@ static void UpdateEntrypoints(mirror::ArtMethod* method, const void* quick_code, } if (!method->IsResolutionMethod()) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - if (quick_code == GetQuickToInterpreterBridge() || - quick_code == class_linker->GetQuickToInterpreterBridgeTrampoline() || - (quick_code == class_linker->GetQuickResolutionTrampoline() && - Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly() - && !method->IsNative() && !method->IsProxyMethod())) { + if (class_linker->IsQuickToInterpreterBridge(quick_code) || + (class_linker->IsQuickResolutionStub(quick_code) && + Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly() && + !method->IsNative() && !method->IsProxyMethod())) { if (kIsDebugBuild) { if (quick_code == GetQuickToInterpreterBridge()) { DCHECK(portable_code == GetPortableToInterpreterBridge()); - } else if (quick_code == class_linker->GetQuickResolutionTrampoline()) { - DCHECK(portable_code == class_linker->GetPortableResolutionTrampoline()); + } else if (class_linker->IsQuickResolutionStub(quick_code)) { + DCHECK(class_linker->IsPortableResolutionStub(portable_code)); } } DCHECK(!method->IsNative()) << PrettyMethod(method); DCHECK(!method->IsProxyMethod()) << PrettyMethod(method); - method->SetEntryPointFromInterpreter(art::interpreter::artInterpreterToInterpreterBridge); + method->SetEntryPointFromInterpreter(art::artInterpreterToInterpreterBridge); } else { method->SetEntryPointFromInterpreter(art::artInterpreterToCompiledCodeBridge); } @@ -140,8 +140,8 @@ void Instrumentation::InstallStubsForMethod(mirror::ArtMethod* method) { new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code); new_quick_code = class_linker->GetQuickOatCodeFor(method); } else { - new_portable_code = class_linker->GetPortableResolutionTrampoline(); - new_quick_code = class_linker->GetQuickResolutionTrampoline(); + new_portable_code = GetPortableResolutionStub(); + new_quick_code = GetQuickResolutionStub(); } } else { // !uninstall if ((interpreter_stubs_installed_ || forced_interpret_only_ || IsDeoptimized(method)) && @@ -159,11 +159,11 @@ void Instrumentation::InstallStubsForMethod(mirror::ArtMethod* method) { } else { new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code); new_quick_code = class_linker->GetQuickOatCodeFor(method); - DCHECK(new_quick_code != class_linker->GetQuickToInterpreterBridgeTrampoline()); + DCHECK(!class_linker->IsQuickToInterpreterBridge(new_quick_code)); } } else { - new_portable_code = class_linker->GetPortableResolutionTrampoline(); - new_quick_code = class_linker->GetQuickResolutionTrampoline(); + new_portable_code = GetPortableResolutionStub(); + new_quick_code = GetQuickResolutionStub(); } } } @@ -287,7 +287,7 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg); std::unique_ptr<Context> context(Context::Create()); - uintptr_t instrumentation_exit_pc = GetQuickInstrumentationExitPc(); + uintptr_t instrumentation_exit_pc = reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()); InstallStackVisitor visitor(thread, context.get(), instrumentation_exit_pc); visitor.WalkStack(true); CHECK_EQ(visitor.dex_pcs_.size(), thread->GetInstrumentationStack()->size()); @@ -388,7 +388,8 @@ static void InstrumentationRestoreStack(Thread* thread, void* arg) std::deque<instrumentation::InstrumentationStackFrame>* stack = thread->GetInstrumentationStack(); if (stack->size() > 0) { Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg); - uintptr_t instrumentation_exit_pc = GetQuickInstrumentationExitPc(); + uintptr_t instrumentation_exit_pc = + reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()); RestoreStackVisitor visitor(thread, instrumentation_exit_pc, instrumentation); visitor.WalkStack(true); CHECK_EQ(visitor.frames_removed_, stack->size()); @@ -632,7 +633,6 @@ void Instrumentation::InstrumentQuickAllocEntryPointsLocked() { SetEntrypointsInstrumented(true); } ++quick_alloc_entry_points_instrumentation_counter_; - LOG(INFO) << "Counter: " << quick_alloc_entry_points_instrumentation_counter_; } void Instrumentation::UninstrumentQuickAllocEntryPointsLocked() { @@ -642,7 +642,6 @@ void Instrumentation::UninstrumentQuickAllocEntryPointsLocked() { if (quick_alloc_entry_points_instrumentation_counter_ == 0) { SetEntrypointsInstrumented(false); } - LOG(INFO) << "Counter: " << quick_alloc_entry_points_instrumentation_counter_; } void Instrumentation::ResetQuickAllocEntryPoints() { @@ -669,11 +668,10 @@ void Instrumentation::UpdateMethodsCode(mirror::ArtMethod* method, const void* q new_have_portable_code = false; } else { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - if (quick_code == class_linker->GetQuickResolutionTrampoline() || - quick_code == class_linker->GetQuickToInterpreterBridgeTrampoline() || - quick_code == GetQuickToInterpreterBridge()) { - DCHECK((portable_code == class_linker->GetPortableResolutionTrampoline()) || - (portable_code == GetPortableToInterpreterBridge())); + if (class_linker->IsQuickResolutionStub(quick_code) || + class_linker->IsQuickToInterpreterBridge(quick_code)) { + DCHECK(class_linker->IsPortableResolutionStub(portable_code) || + class_linker->IsPortableToInterpreterBridge(portable_code)); new_portable_code = portable_code; new_quick_code = quick_code; new_have_portable_code = have_portable_code; @@ -793,9 +791,7 @@ void Instrumentation::Undeoptimize(mirror::ArtMethod* method) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); if (method->IsStatic() && !method->IsConstructor() && !method->GetDeclaringClass()->IsInitialized()) { - // TODO: we're updating to entrypoints in the image here, we can avoid the trampoline. - UpdateEntrypoints(method, class_linker->GetQuickResolutionTrampoline(), - class_linker->GetPortableResolutionTrampoline(), false); + UpdateEntrypoints(method, GetQuickResolutionStub(), GetPortableResolutionStub(), false); } else { bool have_portable_code = false; const void* quick_code = class_linker->GetQuickOatCodeFor(method); @@ -877,9 +873,10 @@ const void* Instrumentation::GetQuickCodeFor(mirror::ArtMethod* method) const { const void* code = method->GetEntryPointFromQuickCompiledCode(); DCHECK(code != nullptr); ClassLinker* class_linker = runtime->GetClassLinker(); - if (LIKELY(code != class_linker->GetQuickResolutionTrampoline()) && - LIKELY(code != class_linker->GetQuickToInterpreterBridgeTrampoline()) && - LIKELY(code != GetQuickToInterpreterBridge())) { + if (LIKELY(!class_linker->IsQuickResolutionStub(code) && + !class_linker->IsQuickToInterpreterBridge(code)) && + !class_linker->IsQuickResolutionStub(code) && + !class_linker->IsQuickToInterpreterBridge(code)) { return code; } } @@ -1017,6 +1014,7 @@ TwoWordReturn Instrumentation::PopInstrumentationStackFrame(Thread* self, uintpt // Set return PC and check the sanity of the stack. *return_pc = instrumentation_frame.return_pc_; CheckStackDepth(self, instrumentation_frame, 0); + self->VerifyStack(); mirror::ArtMethod* method = instrumentation_frame.method_; uint32_t length; diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 8fb1712eb9..dfb03cdeb8 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -507,8 +507,9 @@ void EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame* shadow_frame, JVa ret_val->SetJ(value.GetJ()); } -JValue EnterInterpreterFromStub(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, - ShadowFrame& shadow_frame) { +JValue EnterInterpreterFromEntryPoint(Thread* self, MethodHelper* mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame) { DCHECK_EQ(self, Thread::Current()); bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks(); if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEndForInterpreter(implicit_check))) { @@ -516,10 +517,10 @@ JValue EnterInterpreterFromStub(Thread* self, MethodHelper& mh, const DexFile::C return JValue(); } - return Execute(self, mh, code_item, shadow_frame, JValue()); + return Execute(self, *mh, code_item, *shadow_frame, JValue()); } -extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh, +extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper* mh, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result) { bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks(); @@ -529,10 +530,10 @@ extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh } self->PushShadowFrame(shadow_frame); - DCHECK_EQ(shadow_frame->GetMethod(), mh.Get()); + DCHECK_EQ(shadow_frame->GetMethod(), mh->Get()); // Ensure static methods are initialized. - if (mh.Get()->IsStatic()) { - mirror::Class* declaring_class = mh.Get()->GetDeclaringClass(); + if (mh->Get()->IsStatic()) { + mirror::Class* declaring_class = mh->Get()->GetDeclaringClass(); if (UNLIKELY(!declaring_class->IsInitialized())) { StackHandleScope<1> hs(self); HandleWrapper<Class> h_declaring_class(hs.NewHandleWrapper(&declaring_class)); @@ -546,15 +547,15 @@ extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh } } - if (LIKELY(!mh.Get()->IsNative())) { - result->SetJ(Execute(self, mh, code_item, *shadow_frame, JValue()).GetJ()); + if (LIKELY(!mh->Get()->IsNative())) { + result->SetJ(Execute(self, *mh, code_item, *shadow_frame, JValue()).GetJ()); } else { // We don't expect to be asked to interpret native code (which is entered via a JNI compiler // generated stub) except during testing and image writing. CHECK(!Runtime::Current()->IsStarted()); - Object* receiver = mh.Get()->IsStatic() ? nullptr : shadow_frame->GetVRegReference(0); - uint32_t* args = shadow_frame->GetVRegArgs(mh.Get()->IsStatic() ? 0 : 1); - UnstartedRuntimeJni(self, mh.Get(), receiver, args, result); + Object* receiver = mh->Get()->IsStatic() ? nullptr : shadow_frame->GetVRegReference(0); + uint32_t* args = shadow_frame->GetVRegArgs(mh->Get()->IsStatic() ? 0 : 1); + UnstartedRuntimeJni(self, mh->Get(), receiver, args, result); } self->PopShadowFrame(); diff --git a/runtime/interpreter/interpreter.h b/runtime/interpreter/interpreter.h index 0750eb5c49..d327a71a4f 100644 --- a/runtime/interpreter/interpreter.h +++ b/runtime/interpreter/interpreter.h @@ -42,19 +42,20 @@ extern void EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame* shadow_fra JValue* ret_val) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -extern JValue EnterInterpreterFromStub(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame& shadow_frame) +extern JValue EnterInterpreterFromEntryPoint(Thread* self, MethodHelper* mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh, + +} // namespace interpreter + +extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper* mh, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -} // namespace interpreter - -extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& mh, +extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper* mh, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 52583ae2b8..3ccdd03136 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -506,7 +506,7 @@ void UnexpectedOpcode(const Instruction* inst, MethodHelper& mh) { exit(0); // Unreachable, keep GCC happy. } -static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, +static void UnstartedRuntimeInvoke(Thread* self, MethodHelper* mh, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -666,9 +666,9 @@ bool DoCall(ArtMethod* method, Thread* self, ShadowFrame& shadow_frame, mh.Get()->GetEntryPointFromInterpreter() == artInterpreterToCompiledCodeBridge) { LOG(FATAL) << "Attempt to call compiled code when -Xint: " << PrettyMethod(mh.Get()); } - (mh.Get()->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result); + (mh.Get()->GetEntryPointFromInterpreter())(self, &mh, code_item, new_shadow_frame, result); } else { - UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, first_dest_reg); + UnstartedRuntimeInvoke(self, &mh, code_item, new_shadow_frame, result, first_dest_reg); } return !self->IsExceptionPending(); } @@ -809,7 +809,7 @@ static void UnstartedRuntimeFindClass(Thread* self, Handle<mirror::String> class result->SetL(found); } -static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, +static void UnstartedRuntimeInvoke(Thread* self, MethodHelper* mh, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { // In a runtime that's not started we intercept certain methods to avoid complicated dependency diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index a8345ad579..fa03fc7fa9 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -21,6 +21,9 @@ #include <math.h> +#include <iostream> +#include <sstream> + #include "base/logging.h" #include "class_linker-inl.h" #include "common_throws.h" diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc index 1444d9762a..19e03d8f82 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/java_vm_ext.cc @@ -18,9 +18,11 @@ #include <dlfcn.h> +#include "base/dumpable.h" #include "base/mutex.h" #include "base/stl_util.h" #include "check_jni.h" +#include "fault_handler.h" #include "indirect_reference_table-inl.h" #include "mirror/art_method.h" #include "mirror/class-inl.h" @@ -688,6 +690,10 @@ bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym); int version = (*jni_on_load)(this, nullptr); + if (runtime_->GetTargetSdkVersion() != 0 && runtime_->GetTargetSdkVersion() <= 21) { + fault_manager.EnsureArtActionInFrontOfSignalChain(); + } + self->SetClassLoaderOverride(old_class_loader.get()); if (version == JNI_ERR) { diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc index 16a774fdf0..7fdc18e8a0 100644 --- a/runtime/jdwp/jdwp_handler.cc +++ b/runtime/jdwp/jdwp_handler.cc @@ -329,14 +329,14 @@ static JdwpError VM_ClassPaths(JdwpState*, Request*, ExpandBuf* pReply) expandBufAddUtf8String(pReply, "/"); std::vector<std::string> class_path; - Split(Runtime::Current()->GetClassPathString(), ':', class_path); + Split(Runtime::Current()->GetClassPathString(), ':', &class_path); expandBufAdd4BE(pReply, class_path.size()); for (size_t i = 0; i < class_path.size(); ++i) { expandBufAddUtf8String(pReply, class_path[i]); } std::vector<std::string> boot_class_path; - Split(Runtime::Current()->GetBootClassPathString(), ':', boot_class_path); + Split(Runtime::Current()->GetBootClassPathString(), ':', &boot_class_path); expandBufAdd4BE(pReply, boot_class_path.size()); for (size_t i = 0; i < boot_class_path.size(); ++i) { expandBufAddUtf8String(pReply, boot_class_path[i]); diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index dea30145eb..ad06b85f36 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -30,6 +30,7 @@ #include "base/stl_util.h" #include "class_linker-inl.h" #include "dex_file-inl.h" +#include "fault_handler.h" #include "gc_root.h" #include "gc/accounting/card_table-inl.h" #include "indirect_reference_table-inl.h" @@ -2144,7 +2145,7 @@ class JNI { VLOG(jni) << "[Registering JNI native method " << PrettyMethod(m) << "]"; - m->RegisterNative(soa.Self(), fnPtr, is_fast); + m->RegisterNative(fnPtr, is_fast); } return JNI_OK; } @@ -2160,14 +2161,14 @@ class JNI { for (size_t i = 0; i < c->NumDirectMethods(); ++i) { mirror::ArtMethod* m = c->GetDirectMethod(i); if (m->IsNative()) { - m->UnregisterNative(soa.Self()); + m->UnregisterNative(); unregistered_count++; } } for (size_t i = 0; i < c->NumVirtualMethods(); ++i) { mirror::ArtMethod* m = c->GetVirtualMethod(i); if (m->IsNative()) { - m->UnregisterNative(soa.Self()); + m->UnregisterNative(); unregistered_count++; } } @@ -2704,7 +2705,7 @@ std::ostream& operator<<(std::ostream& os, const jobjectRefType& rhs) { os << "JNIWeakGlobalRefType"; return os; default: - LOG(FATAL) << "jobjectRefType[" << static_cast<int>(rhs) << "]"; - return os; + LOG(::art::FATAL) << "jobjectRefType[" << static_cast<int>(rhs) << "]"; + UNREACHABLE(); } } diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 231e9e56b0..c1184716b1 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -15,11 +15,12 @@ */ #include "mem_map.h" -#include "thread-inl.h" -#include <inttypes.h> #include <backtrace/BacktraceMap.h> +#include <inttypes.h> + #include <memory> +#include <sstream> // See CreateStartPos below. #ifdef __BIONIC__ @@ -28,6 +29,7 @@ #include "base/stringprintf.h" #include "ScopedFd.h" +#include "thread-inl.h" #include "utils.h" #define USE_ASHMEM 1 @@ -70,7 +72,7 @@ std::ostream& operator<<(std::ostream& os, const MemMap::Maps& mem_maps) { return os; } -MemMap::Maps MemMap::maps_; +MemMap::Maps* MemMap::maps_ = nullptr; #if USE_ART_LOW_4G_ALLOCATOR // Handling mem_map in 32b address range for 64b architectures that do not support MAP_32BIT. @@ -457,11 +459,12 @@ MemMap::~MemMap() { // Remove it from maps_. MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_); bool found = false; - for (auto it = maps_.lower_bound(base_begin_), end = maps_.end(); + DCHECK(maps_ != nullptr); + for (auto it = maps_->lower_bound(base_begin_), end = maps_->end(); it != end && it->first == base_begin_; ++it) { if (it->second == this) { found = true; - maps_.erase(it); + maps_->erase(it); break; } } @@ -483,7 +486,8 @@ MemMap::MemMap(const std::string& name, uint8_t* begin, size_t size, void* base_ // Add it to maps_. MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_); - maps_.insert(std::pair<void*, MemMap*>(base_begin_, this)); + DCHECK(maps_ != nullptr); + maps_->insert(std::make_pair(base_begin_, this)); } } @@ -614,7 +618,7 @@ void MemMap::DumpMapsLocked(std::ostream& os) { bool MemMap::HasMemMap(MemMap* map) { void* base_begin = map->BaseBegin(); - for (auto it = maps_.lower_bound(base_begin), end = maps_.end(); + for (auto it = maps_->lower_bound(base_begin), end = maps_->end(); it != end && it->first == base_begin; ++it) { if (it->second == map) { return true; @@ -626,7 +630,8 @@ bool MemMap::HasMemMap(MemMap* map) { MemMap* MemMap::GetLargestMemMapAt(void* address) { size_t largest_size = 0; MemMap* largest_map = nullptr; - for (auto it = maps_.lower_bound(address), end = maps_.end(); + DCHECK(maps_ != nullptr); + for (auto it = maps_->lower_bound(address), end = maps_->end(); it != end && it->first == address; ++it) { MemMap* map = it->second; CHECK(map != nullptr); @@ -638,6 +643,20 @@ MemMap* MemMap::GetLargestMemMapAt(void* address) { return largest_map; } +void MemMap::Init() { + MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_); + if (maps_ == nullptr) { + // dex2oat calls MemMap::Init twice since its needed before the runtime is created. + maps_ = new Maps; + } +} + +void MemMap::Shutdown() { + MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_); + delete maps_; + maps_ = nullptr; +} + std::ostream& operator<<(std::ostream& os, const MemMap& mem_map) { os << StringPrintf("[MemMap: %p-%p prot=0x%x %s]", mem_map.BaseBegin(), mem_map.BaseEnd(), mem_map.GetProtect(), diff --git a/runtime/mem_map.h b/runtime/mem_map.h index 314bf8d800..df1222c39d 100644 --- a/runtime/mem_map.h +++ b/runtime/mem_map.h @@ -138,6 +138,9 @@ class MemMap { typedef AllocationTrackingMultiMap<void*, MemMap*, kAllocatorTagMaps> Maps; + static void Init() LOCKS_EXCLUDED(Locks::mem_maps_lock_); + static void Shutdown() LOCKS_EXCLUDED(Locks::mem_maps_lock_); + private: MemMap(const std::string& name, uint8_t* begin, size_t size, void* base_begin, size_t base_size, int prot, bool reuse) LOCKS_EXCLUDED(Locks::mem_maps_lock_); @@ -167,7 +170,7 @@ class MemMap { #endif // All the non-empty MemMaps. Use a multimap as we do a reserve-and-divide (eg ElfMap::Load()). - static Maps maps_ GUARDED_BY(Locks::mem_maps_lock_); + static Maps* maps_ GUARDED_BY(Locks::mem_maps_lock_); friend class MemMapTest; // To allow access to base_begin_ and base_size_. }; diff --git a/runtime/mem_map_test.cc b/runtime/mem_map_test.cc index a78f4631f7..14a72b9b1b 100644 --- a/runtime/mem_map_test.cc +++ b/runtime/mem_map_test.cc @@ -87,6 +87,10 @@ class MemMapTest : public testing::Test { delete m1; } + void CommonInit() { + MemMap::Init(); + } + #if defined(__LP64__) && !defined(__x86_64__) static uintptr_t GetLinearScanPos() { return MemMap::next_mem_pos_; @@ -101,10 +105,10 @@ extern uintptr_t CreateStartPos(uint64_t input); #endif TEST_F(MemMapTest, Start) { + CommonInit(); uintptr_t start = GetLinearScanPos(); EXPECT_LE(64 * KB, start); EXPECT_LT(start, static_cast<uintptr_t>(ART_BASE_ADDRESS)); - #ifdef __BIONIC__ // Test a couple of values. Make sure they are different. uintptr_t last = 0; @@ -122,6 +126,7 @@ TEST_F(MemMapTest, Start) { #endif TEST_F(MemMapTest, MapAnonymousEmpty) { + CommonInit(); std::string error_msg; std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousEmpty", nullptr, @@ -143,6 +148,7 @@ TEST_F(MemMapTest, MapAnonymousEmpty) { #ifdef __LP64__ TEST_F(MemMapTest, MapAnonymousEmpty32bit) { + CommonInit(); std::string error_msg; std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousEmpty", nullptr, @@ -157,6 +163,7 @@ TEST_F(MemMapTest, MapAnonymousEmpty32bit) { #endif TEST_F(MemMapTest, MapAnonymousExactAddr) { + CommonInit(); std::string error_msg; // Map at an address that should work, which should succeed. std::unique_ptr<MemMap> map0(MemMap::MapAnonymous("MapAnonymous0", @@ -200,6 +207,7 @@ TEST_F(MemMapTest, RemapAtEnd32bit) { #endif TEST_F(MemMapTest, MapAnonymousExactAddr32bitHighAddr) { + CommonInit(); // This test may not work under valgrind. if (RUNNING_ON_VALGRIND == 0) { uintptr_t start_addr = ART_BASE_ADDRESS + 0x1000000; @@ -217,6 +225,7 @@ TEST_F(MemMapTest, MapAnonymousExactAddr32bitHighAddr) { } TEST_F(MemMapTest, MapAnonymousOverflow) { + CommonInit(); std::string error_msg; uintptr_t ptr = 0; ptr -= kPageSize; // Now it's close to the top. @@ -232,6 +241,7 @@ TEST_F(MemMapTest, MapAnonymousOverflow) { #ifdef __LP64__ TEST_F(MemMapTest, MapAnonymousLow4GBExpectedTooHigh) { + CommonInit(); std::string error_msg; std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousLow4GBExpectedTooHigh", reinterpret_cast<uint8_t*>(UINT64_C(0x100000000)), @@ -244,6 +254,7 @@ TEST_F(MemMapTest, MapAnonymousLow4GBExpectedTooHigh) { } TEST_F(MemMapTest, MapAnonymousLow4GBRangeTooHigh) { + CommonInit(); std::string error_msg; std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousLow4GBRangeTooHigh", reinterpret_cast<uint8_t*>(0xF0000000), @@ -257,6 +268,7 @@ TEST_F(MemMapTest, MapAnonymousLow4GBRangeTooHigh) { #endif TEST_F(MemMapTest, CheckNoGaps) { + CommonInit(); std::string error_msg; constexpr size_t kNumPages = 3; // Map a 3-page mem map. diff --git a/runtime/memory_region.h b/runtime/memory_region.h index 645996366b..4eb6d47537 100644 --- a/runtime/memory_region.h +++ b/runtime/memory_region.h @@ -21,6 +21,7 @@ #include "base/logging.h" #include "base/macros.h" +#include "base/value_object.h" #include "globals.h" namespace art { @@ -28,9 +29,9 @@ namespace art { // Memory regions are useful for accessing memory with bounds check in // debug mode. They can be safely passed by value and do not assume ownership // of the region. -class MemoryRegion { +class MemoryRegion FINAL : public ValueObject { public: - MemoryRegion() : pointer_(NULL), size_(0) {} + MemoryRegion() : pointer_(nullptr), size_(0) {} MemoryRegion(void* pointer, uintptr_t size) : pointer_(pointer), size_(size) {} void* pointer() const { return pointer_; } diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index 1a65d99023..664a412292 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -24,7 +24,6 @@ #include "class_linker.h" #include "dex_cache.h" #include "dex_file.h" -#include "entrypoints/entrypoint_utils.h" #include "method_helper.h" #include "object-inl.h" #include "object_array.h" @@ -176,32 +175,6 @@ inline bool ArtMethod::CheckIncompatibleClassChange(InvokeType type) { } } -inline void ArtMethod::AssertPcIsWithinQuickCode(uintptr_t pc) { - if (!kIsDebugBuild) { - return; - } - if (IsNative() || IsRuntimeMethod() || IsProxyMethod()) { - return; - } - if (pc == GetQuickInstrumentationExitPc()) { - return; - } - const void* code = GetEntryPointFromQuickCompiledCode(); - if (code == GetQuickToInterpreterBridge() || code == GetQuickInstrumentationEntryPoint()) { - return; - } - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - if (code == class_linker->GetQuickResolutionTrampoline() || - code == class_linker->GetQuickToInterpreterBridgeTrampoline()) { - return; - } - DCHECK(IsWithinQuickCode(pc)) - << PrettyMethod(this) - << " pc=" << std::hex << pc - << " code=" << code - << " size=" << GetCodeSize(); -} - inline uint32_t ArtMethod::GetQuickOatCodeOffset() { DCHECK(!Runtime::Current()->IsStarted()); return PointerToLowMemUInt32(GetEntryPointFromQuickCompiledCode()); @@ -222,27 +195,6 @@ inline void ArtMethod::SetPortableOatCodeOffset(uint32_t code_offset) { SetEntryPointFromPortableCompiledCode(reinterpret_cast<void*>(code_offset)); } -inline const void* ArtMethod::GetQuickOatEntryPoint() { - if (IsPortableCompiled() || IsAbstract() || IsRuntimeMethod() || IsProxyMethod()) { - return nullptr; - } - Runtime* runtime = Runtime::Current(); - const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this); - // On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method - // indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline) - // for non-native methods. - DCHECK(entry_point != runtime->GetClassLinker()->GetQuickToInterpreterBridgeTrampoline()); - if (UNLIKELY(entry_point == GetQuickToInterpreterBridge()) || - UNLIKELY(entry_point == runtime->GetClassLinker()->GetQuickGenericJniTrampoline())) { - return nullptr; - } - return entry_point; -} - -inline const void* ArtMethod::GetQuickOatCodePointer() { - return EntryPointToCodePointer(GetQuickOatEntryPoint()); -} - inline const uint8_t* ArtMethod::GetMappingTable() { const void* code_pointer = GetQuickOatCodePointer(); if (code_pointer == nullptr) { @@ -341,69 +293,17 @@ inline bool ArtMethod::IsImtConflictMethod() { return result; } -inline uintptr_t ArtMethod::NativePcOffset(const uintptr_t pc) { +inline uintptr_t ArtMethod::NativeQuickPcOffset(const uintptr_t pc) { const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this); return pc - reinterpret_cast<uintptr_t>(code); } -inline uintptr_t ArtMethod::NativePcOffset(const uintptr_t pc, const void* quick_entry_point) { - DCHECK(quick_entry_point != GetQuickToInterpreterBridge()); - DCHECK(quick_entry_point == Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this)); - return pc - reinterpret_cast<uintptr_t>(quick_entry_point); -} - template<VerifyObjectFlags kVerifyFlags> inline void ArtMethod::SetNativeMethod(const void* native_method) { SetFieldPtr<false, true, kVerifyFlags>( OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_), native_method); } -inline QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo() { - if (UNLIKELY(IsPortableCompiled())) { - // Portable compiled dex bytecode or jni stub. - return QuickMethodFrameInfo(kStackAlignment, 0u, 0u); - } - Runtime* runtime = Runtime::Current(); - // For Proxy method we exclude direct method (there is only one direct method - constructor). - // Direct method is cloned from original java.lang.reflect.Proxy class together with code - // and as a result it is executed as usual quick compiled method without any stubs. - // So the frame info should be returned as it is a quick method not a stub. - if (UNLIKELY(IsAbstract()) || UNLIKELY(IsProxyMethod() && !IsDirect())) { - return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs); - } - if (UNLIKELY(IsRuntimeMethod())) { - return runtime->GetRuntimeMethodFrameInfo(this); - } - - const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this); - // On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method - // indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline) - // for non-native methods. And we really shouldn't see a failure for non-native methods here. - DCHECK(entry_point != runtime->GetClassLinker()->GetQuickToInterpreterBridgeTrampoline()); - CHECK(entry_point != GetQuickToInterpreterBridge()); - - if (UNLIKELY(entry_point == runtime->GetClassLinker()->GetQuickGenericJniTrampoline())) { - // Generic JNI frame. - DCHECK(IsNative()); - StackHandleScope<1> hs(Thread::Current()); - uint32_t handle_refs = - MethodHelper(hs.NewHandle(this)).GetNumberOfReferenceArgsWithoutReceiver() + 1; - size_t scope_size = HandleScope::SizeOf(handle_refs); - QuickMethodFrameInfo callee_info = runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs); - - // Callee saves + handle scope + method ref + alignment - size_t frame_size = RoundUp(callee_info.FrameSizeInBytes() + scope_size - - sizeof(void*) // callee-save frame stores a whole method pointer - + sizeof(StackReference<mirror::ArtMethod>), - kStackAlignment); - - return QuickMethodFrameInfo(frame_size, callee_info.CoreSpillMask(), callee_info.FpSpillMask()); - } - - const void* code_pointer = EntryPointToCodePointer(entry_point); - return GetQuickFrameInfo(code_pointer); -} - inline QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo(const void* code_pointer) { DCHECK(code_pointer != nullptr); DCHECK_EQ(code_pointer, GetQuickOatCodePointer()); diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index 787c76715a..9584d155ce 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -23,6 +23,8 @@ #include "class-inl.h" #include "dex_file-inl.h" #include "dex_instruction.h" +#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" #include "interpreter/interpreter.h" #include "jni_internal.h" @@ -203,7 +205,7 @@ uint32_t ArtMethod::ToDexPc(const uintptr_t pc, bool abort_on_failure) { return DexFile::kDexNoIndex; } -uintptr_t ArtMethod::ToNativePc(const uint32_t dex_pc) { +uintptr_t ArtMethod::ToNativeQuickPc(const uint32_t dex_pc) { const void* entry_point = GetQuickOatEntryPoint(); MappingTable table( entry_point != nullptr ? GetMappingTable(EntryPointToCodePointer(entry_point)) : nullptr); @@ -281,19 +283,74 @@ uint32_t ArtMethod::FindCatchBlock(Handle<ArtMethod> h_this, Handle<Class> excep return found_dex_pc; } +void ArtMethod::AssertPcIsWithinQuickCode(uintptr_t pc) { + if (IsNative() || IsRuntimeMethod() || IsProxyMethod()) { + return; + } + if (pc == reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc())) { + return; + } + const void* code = GetEntryPointFromQuickCompiledCode(); + if (code == GetQuickInstrumentationEntryPoint()) { + return; + } + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + if (class_linker->IsQuickToInterpreterBridge(code) || + class_linker->IsQuickResolutionStub(code)) { + return; + } + /* + * During a stack walk, a return PC may point past-the-end of the code + * in the case that the last instruction is a call that isn't expected to + * return. Thus, we check <= code + GetCodeSize(). + * + * NOTE: For Thumb both pc and code are offset by 1 indicating the Thumb state. + */ + CHECK(PcIsWithinQuickCode(pc)) + << PrettyMethod(this) + << " pc=" << std::hex << pc + << " code=" << code + << " size=" << GetCodeSize(); +} + bool ArtMethod::IsEntrypointInterpreter() { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - const void* oat_quick_code = class_linker->GetOatMethodQuickCodeFor(this); - const void* oat_portable_code = class_linker->GetOatMethodPortableCodeFor(this); if (!IsPortableCompiled()) { // Quick. + const void* oat_quick_code = class_linker->GetOatMethodQuickCodeFor(this); return oat_quick_code == nullptr || oat_quick_code != GetEntryPointFromQuickCompiledCode(); } else { // Portable. + const void* oat_portable_code = class_linker->GetOatMethodPortableCodeFor(this); return oat_portable_code == nullptr || oat_portable_code != GetEntryPointFromPortableCompiledCode(); } } +const void* ArtMethod::GetQuickOatEntryPoint() { + if (IsPortableCompiled() || IsAbstract() || IsRuntimeMethod() || IsProxyMethod()) { + return nullptr; + } + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + const void* code = runtime->GetInstrumentation()->GetQuickCodeFor(this); + // On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method + // indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline) + // for non-native methods. + if (class_linker->IsQuickToInterpreterBridge(code) || + class_linker->IsQuickGenericJniStub(code)) { + return nullptr; + } + return code; +} + +#ifndef NDEBUG +uintptr_t ArtMethod::NativeQuickPcOffset(const uintptr_t pc, const void* quick_entry_point) { + CHECK_NE(quick_entry_point, GetQuickToInterpreterBridge()); + CHECK_EQ(quick_entry_point, Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this)); + return pc - reinterpret_cast<uintptr_t>(quick_entry_point); +} +#endif + void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, const char* shorty) { if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) { @@ -357,7 +414,7 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* // stack. Continue execution in the interpreter. self->ClearException(); ShadowFrame* shadow_frame = self->GetAndClearDeoptimizationShadowFrame(result); - self->SetTopOfStack(nullptr, 0); + self->SetTopOfStack(nullptr); self->SetTopOfShadowStack(shadow_frame); interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, result); } @@ -379,8 +436,53 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* self->PopManagedStackFragment(fragment); } -void ArtMethod::RegisterNative(Thread* self, const void* native_method, bool is_fast) { - DCHECK(Thread::Current() == self); +QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo() { + if (UNLIKELY(IsPortableCompiled())) { + // Portable compiled dex bytecode or jni stub. + return QuickMethodFrameInfo(kStackAlignment, 0u, 0u); + } + Runtime* runtime = Runtime::Current(); + // For Proxy method we exclude direct method (there is only one direct method - constructor). + // Direct method is cloned from original java.lang.reflect.Proxy class together with code + // and as a result it is executed as usual quick compiled method without any stubs. + // So the frame info should be returned as it is a quick method not a stub. + if (UNLIKELY(IsAbstract()) || UNLIKELY(IsProxyMethod() && !IsDirect())) { + return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs); + } + if (UNLIKELY(IsRuntimeMethod())) { + return runtime->GetRuntimeMethodFrameInfo(this); + } + + const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this); + ClassLinker* class_linker = runtime->GetClassLinker(); + // On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method + // indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline) + // for non-native methods. And we really shouldn't see a failure for non-native methods here. + DCHECK(!class_linker->IsQuickToInterpreterBridge(entry_point)); + + if (class_linker->IsQuickGenericJniStub(entry_point)) { + // Generic JNI frame. + DCHECK(IsNative()); + StackHandleScope<1> hs(Thread::Current()); + uint32_t handle_refs = + MethodHelper(hs.NewHandle(this)).GetNumberOfReferenceArgsWithoutReceiver() + 1; + size_t scope_size = HandleScope::SizeOf(handle_refs); + QuickMethodFrameInfo callee_info = runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs); + + // Callee saves + handle scope + method ref + alignment + size_t frame_size = RoundUp(callee_info.FrameSizeInBytes() + scope_size + - sizeof(void*) // callee-save frame stores a whole method pointer + + sizeof(StackReference<mirror::ArtMethod>), + kStackAlignment); + + return QuickMethodFrameInfo(frame_size, callee_info.CoreSpillMask(), callee_info.FpSpillMask()); + } + + const void* code_pointer = EntryPointToCodePointer(entry_point); + return GetQuickFrameInfo(code_pointer); +} + +void ArtMethod::RegisterNative(const void* native_method, bool is_fast) { CHECK(IsNative()) << PrettyMethod(this); CHECK(!IsFastNative()) << PrettyMethod(this); CHECK(native_method != NULL) << PrettyMethod(this); @@ -390,10 +492,10 @@ void ArtMethod::RegisterNative(Thread* self, const void* native_method, bool is_ SetNativeMethod(native_method); } -void ArtMethod::UnregisterNative(Thread* self) { +void ArtMethod::UnregisterNative() { CHECK(IsNative() && !IsFastNative()) << PrettyMethod(this); // restore stub to lookup native pointer via dlsym - RegisterNative(self, GetJniDlsymLookupStub(), false); + RegisterNative(GetJniDlsymLookupStub(), false); } } // namespace mirror diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index 939d856eb7..3b9201266c 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -39,7 +39,7 @@ class ShadowFrame; namespace mirror { -typedef void (EntryPointFromInterpreter)(Thread* self, MethodHelper& mh, +typedef void (EntryPointFromInterpreter)(Thread* self, MethodHelper* mh, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result); // C++ mirror of java.lang.reflect.ArtMethod. @@ -302,7 +302,10 @@ class MANAGED ArtMethod FINAL : public Object { uint32_t GetCodeSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool IsWithinQuickCode(uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // Check whether the given PC is within the quick compiled code associated with this method's + // quick entrypoint. This code isn't robust for instrumentation, etc. and is only used for + // debug purposes. + bool PcIsWithinQuickCode(uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { uintptr_t code = reinterpret_cast<uintptr_t>(GetEntryPointFromQuickCompiledCode()); if (code == 0) { return pc == 0; @@ -329,16 +332,21 @@ class MANAGED ArtMethod FINAL : public Object { void SetQuickOatCodeOffset(uint32_t code_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void SetPortableOatCodeOffset(uint32_t code_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static const void* EntryPointToCodePointer(const void* entry_point) ALWAYS_INLINE { + ALWAYS_INLINE static const void* EntryPointToCodePointer(const void* entry_point) { uintptr_t code = reinterpret_cast<uintptr_t>(entry_point); - code &= ~0x1; // TODO: Make this Thumb2 specific. + // TODO: Make this Thumb2 specific. It is benign on other architectures as code is always at + // least 2 byte aligned. + code &= ~0x1; return reinterpret_cast<const void*>(code); } - // Actual entry point pointer to compiled oat code or nullptr. + // Actual entry point pointer to compiled oat code or nullptr if method has no compiled code. const void* GetQuickOatEntryPoint() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Actual pointer to compiled oat code or nullptr. - const void* GetQuickOatCodePointer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const void* GetQuickOatCodePointer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return EntryPointToCodePointer(GetQuickOatEntryPoint()); + } // Callers should wrap the uint8_t* in a MappingTable instance for convenient access. const uint8_t* GetMappingTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -378,24 +386,25 @@ class MANAGED ArtMethod FINAL : public Object { QuickMethodFrameInfo GetQuickFrameInfo(const void* code_pointer) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - size_t GetReturnPcOffsetInBytes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return GetReturnPcOffsetInBytes(GetFrameSizeInBytes()); + FrameOffset GetReturnPcOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetReturnPcOffset(GetFrameSizeInBytes()); } - size_t GetReturnPcOffsetInBytes(uint32_t frame_size_in_bytes) + FrameOffset GetReturnPcOffset(uint32_t frame_size_in_bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK_EQ(frame_size_in_bytes, GetFrameSizeInBytes()); - return frame_size_in_bytes - sizeof(void*); + return FrameOffset(frame_size_in_bytes - sizeof(void*)); } - size_t GetHandleScopeOffsetInBytes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return sizeof(void*); + FrameOffset GetHandleScopeOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK_LT(sizeof(void*), GetFrameSizeInBytes()); + return FrameOffset(sizeof(void*)); } - void RegisterNative(Thread* self, const void* native_method, bool is_fast) + void RegisterNative(const void* native_method, bool is_fast) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void UnregisterNative(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void UnregisterNative() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static MemberOffset NativeMethodOffset() { return OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_); @@ -423,16 +432,23 @@ class MANAGED ArtMethod FINAL : public Object { bool IsImtConflictMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - uintptr_t NativePcOffset(const uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - uintptr_t NativePcOffset(const uintptr_t pc, const void* quick_entry_point) + uintptr_t NativeQuickPcOffset(const uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +#ifdef NDEBUG + uintptr_t NativeQuickPcOffset(const uintptr_t pc, const void* quick_entry_point) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return pc - reinterpret_cast<uintptr_t>(quick_entry_point); + } +#else + uintptr_t NativeQuickPcOffset(const uintptr_t pc, const void* quick_entry_point) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +#endif // Converts a native PC to a dex PC. uint32_t ToDexPc(const uintptr_t pc, bool abort_on_failure = true) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Converts a dex PC to a native PC. - uintptr_t ToNativePc(const uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + uintptr_t ToNativeQuickPc(const uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Find the catch block for the given exception type and dex_pc. When a catch block is found, // indicates whether the found catch block is responsible for clearing the exception or whether diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 3d3ae166a1..e3295efad0 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -112,7 +112,8 @@ inline uint32_t Class::NumVirtualMethods() { template<VerifyObjectFlags kVerifyFlags> inline ArtMethod* Class::GetVirtualMethod(uint32_t i) { - DCHECK(IsResolved<kVerifyFlags>() || IsErroneous<kVerifyFlags>()); + DCHECK(IsResolved<kVerifyFlags>() || IsErroneous<kVerifyFlags>()) + << PrettyClass(this) << " status=" << GetStatus(); return GetVirtualMethods()->Get(i); } @@ -615,12 +616,11 @@ inline uint32_t Class::ComputeClassSize(bool has_embedded_tables, template <bool kVisitClass, typename Visitor> inline void Class::VisitReferences(mirror::Class* klass, const Visitor& visitor) { - // Visit the static fields first so that we don't overwrite the SFields / IFields instance - // fields. VisitInstanceFieldsReferences<kVisitClass>(klass, visitor); - if (!IsTemp()) { + if (!IsTemp() && IsResolved()) { // Temp classes don't ever populate imt/vtable or static fields and they are not even - // allocated with the right size for those. + // allocated with the right size for those. Also, unresolved classes don't have fields + // linked yet. VisitStaticFieldsReferences<kVisitClass>(this, visitor); if (ShouldHaveEmbeddedImtAndVTable()) { VisitEmbeddedImtAndVTable(visitor); diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 2d4912191b..c06071be11 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -27,6 +27,7 @@ #include "object_callbacks.h" #include "primitive.h" #include "read_barrier_option.h" +#include "utils.h" namespace art { @@ -507,7 +508,7 @@ class MANAGED Class FINAL : public Object { // The size of java.lang.Class.class. static uint32_t ClassClassSize() { // The number of vtable entries in java.lang.Class. - uint32_t vtable_entries = Object::kVTableLength + 64; + uint32_t vtable_entries = Object::kVTableLength + 66; return ComputeClassSize(true, vtable_entries, 0, 0, 0, 1, 0); } @@ -840,7 +841,7 @@ class MANAGED Class FINAL : public Object { // Returns the number of static fields containing reference types. uint32_t NumReferenceStaticFields() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(IsResolved() || IsErroneous()); + DCHECK(IsResolved() || IsErroneous()) << PrettyClass(this) << " status=" << GetStatus(); return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_)); } diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 1bbcf8ef1c..b2b24209c7 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -478,6 +478,7 @@ class MANAGED LOCKABLE Object { friend struct art::ObjectOffsets; // for verifying offset information friend class CopyObjectVisitor; // for CopyObject(). friend class CopyClassVisitor; // for CopyObject(). + DISALLOW_ALLOCATION(); DISALLOW_IMPLICIT_CONSTRUCTORS(Object); }; diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index a2a062617a..a0aaa9e8c7 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -80,26 +80,6 @@ TEST_F(ObjectTest, Constants) { EXPECT_EQ(kObjectHeaderSize, sizeof(Object)); } -// Keep the assembly code constats in sync. -TEST_F(ObjectTest, AsmConstants) { - EXPECT_EQ(CLASS_OFFSET, Object::ClassOffset().Int32Value()); - EXPECT_EQ(LOCK_WORD_OFFSET, Object::MonitorOffset().Int32Value()); - - EXPECT_EQ(CLASS_COMPONENT_TYPE_OFFSET, Class::ComponentTypeOffset().Int32Value()); - - EXPECT_EQ(ARRAY_LENGTH_OFFSET, Array::LengthOffset().Int32Value()); - EXPECT_EQ(OBJECT_ARRAY_DATA_OFFSET, Array::DataOffset(sizeof(HeapReference<Object>)).Int32Value()); - - EXPECT_EQ(STRING_VALUE_OFFSET, String::ValueOffset().Int32Value()); - EXPECT_EQ(STRING_COUNT_OFFSET, String::CountOffset().Int32Value()); - EXPECT_EQ(STRING_OFFSET_OFFSET, String::OffsetOffset().Int32Value()); - EXPECT_EQ(STRING_DATA_OFFSET, Array::DataOffset(sizeof(uint16_t)).Int32Value()); - - EXPECT_EQ(METHOD_DEX_CACHE_METHODS_OFFSET, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()); - EXPECT_EQ(METHOD_PORTABLE_CODE_OFFSET, ArtMethod::EntryPointFromPortableCompiledCodeOffset().Int32Value()); - EXPECT_EQ(METHOD_QUICK_CODE_OFFSET, ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()); -} - TEST_F(ObjectTest, IsInSamePackage) { // Matches EXPECT_TRUE(Class::IsInSamePackage("Ljava/lang/Object;", "Ljava/lang/Class;")); diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 1320ab71a3..64408a6604 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -17,8 +17,6 @@ #ifndef ART_RUNTIME_MIRROR_STRING_H_ #define ART_RUNTIME_MIRROR_STRING_H_ -#include <gtest/gtest.h> - #include "gc_root.h" #include "object.h" #include "object_callbacks.h" @@ -163,7 +161,8 @@ class MANAGED String FINAL : public Object { static GcRoot<Class> java_lang_String_; friend struct art::StringOffsets; // for verifying offset information - FRIEND_TEST(ObjectTest, StringLength); // for SetOffset and SetCount + ART_FRIEND_TEST(ObjectTest, StringLength); // for SetOffset and SetCount + DISALLOW_IMPLICIT_CONSTRUCTORS(String); }; diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 65a79198a4..11e9efcfb4 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -192,12 +192,13 @@ static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, j mirror::Class* result = class_linker->DefineClass(soa.Self(), descriptor.c_str(), class_loader, *dex_file, *dex_class_def); if (result != nullptr) { - VLOG(class_linker) << "DexFile_defineClassNative returning " << result; + VLOG(class_linker) << "DexFile_defineClassNative returning " << result + << " for " << class_name.c_str(); return soa.AddLocalReference<jclass>(result); } } } - VLOG(class_linker) << "Failed to find dex_class_def"; + VLOG(class_linker) << "Failed to find dex_class_def " << class_name.c_str(); return nullptr; } diff --git a/runtime/native/java_lang_Runtime.cc b/runtime/native/java_lang_Runtime.cc index 62ca14de17..f9a1cee2d8 100644 --- a/runtime/native/java_lang_Runtime.cc +++ b/runtime/native/java_lang_Runtime.cc @@ -37,8 +37,7 @@ static void Runtime_gc(JNIEnv*, jclass) { Runtime::Current()->GetHeap()->CollectGarbage(false); } -static void Runtime_nativeExit(JNIEnv*, jclass, jint status) NO_RETURN; -static void Runtime_nativeExit(JNIEnv*, jclass, jint status) { +[[noreturn]] static void Runtime_nativeExit(JNIEnv*, jclass, jint status) { LOG(INFO) << "System.exit called, status: " << status; Runtime::Current()->CallExitHook(status); exit(status); diff --git a/runtime/native/scoped_fast_native_object_access.h b/runtime/native/scoped_fast_native_object_access.h index 606d62d8ce..dfabff5f6a 100644 --- a/runtime/native/scoped_fast_native_object_access.h +++ b/runtime/native/scoped_fast_native_object_access.h @@ -17,7 +17,7 @@ #ifndef ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_H_ #define ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_H_ -#include "mirror/art_method.h" +#include "mirror/art_method-inl.h" #include "scoped_thread_state_change.h" namespace art { diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc index bc191b4289..b0d8e87a90 100644 --- a/runtime/native_bridge_art_interface.cc +++ b/runtime/native_bridge_art_interface.cc @@ -107,10 +107,11 @@ static android::NativeBridgeRuntimeCallbacks native_bridge_art_callbacks_ { GetMethodShorty, GetNativeMethodCount, GetNativeMethods }; -void LoadNativeBridge(std::string& native_bridge_library_filename) { - android::LoadNativeBridge(native_bridge_library_filename.c_str(), &native_bridge_art_callbacks_); +bool LoadNativeBridge(std::string& native_bridge_library_filename) { VLOG(startup) << "Runtime::Setup native bridge library: " << (native_bridge_library_filename.empty() ? "(empty)" : native_bridge_library_filename); + return android::LoadNativeBridge(native_bridge_library_filename.c_str(), + &native_bridge_art_callbacks_); } void PreInitializeNativeBridge(std::string dir) { diff --git a/runtime/native_bridge_art_interface.h b/runtime/native_bridge_art_interface.h index 026cd82c15..090cddb9b6 100644 --- a/runtime/native_bridge_art_interface.h +++ b/runtime/native_bridge_art_interface.h @@ -26,7 +26,7 @@ namespace art { // Mirror libnativebridge interface. Done to have the ART callbacks out of line, and not require // the system/core header file in other files. -void LoadNativeBridge(std::string& native_bridge_library_filename); +bool LoadNativeBridge(std::string& native_bridge_library_filename); // This is mostly for testing purposes, as in a full system this is called by Zygote code. void PreInitializeNativeBridge(std::string dir); diff --git a/runtime/oat.cc b/runtime/oat.cc index 6810d73cbb..0d7fb0120f 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -23,7 +23,7 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '4', '2', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '4', '3', '\0' }; static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) { size_t estimate = 0U; @@ -39,7 +39,7 @@ static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* vari } OatHeader* OatHeader::Create(InstructionSet instruction_set, - const InstructionSetFeatures& instruction_set_features, + const InstructionSetFeatures* instruction_set_features, const std::vector<const DexFile*>* dex_files, uint32_t image_file_location_oat_checksum, uint32_t image_file_location_oat_data_begin, @@ -60,7 +60,7 @@ OatHeader* OatHeader::Create(InstructionSet instruction_set, } OatHeader::OatHeader(InstructionSet instruction_set, - const InstructionSetFeatures& instruction_set_features, + const InstructionSetFeatures* instruction_set_features, const std::vector<const DexFile*>* dex_files, uint32_t image_file_location_oat_checksum, uint32_t image_file_location_oat_data_begin, @@ -76,8 +76,8 @@ OatHeader::OatHeader(InstructionSet instruction_set, instruction_set_ = instruction_set; UpdateChecksum(&instruction_set_, sizeof(instruction_set_)); - instruction_set_features_ = instruction_set_features; - UpdateChecksum(&instruction_set_features_, sizeof(instruction_set_features_)); + instruction_set_features_bitmap_ = instruction_set_features->AsBitmap(); + UpdateChecksum(&instruction_set_features_bitmap_, sizeof(instruction_set_features_bitmap_)); dex_file_count_ = dex_files->size(); UpdateChecksum(&dex_file_count_, sizeof(dex_file_count_)); @@ -149,9 +149,9 @@ InstructionSet OatHeader::GetInstructionSet() const { return instruction_set_; } -const InstructionSetFeatures& OatHeader::GetInstructionSetFeatures() const { +uint32_t OatHeader::GetInstructionSetFeaturesBitmap() const { CHECK(IsValid()); - return instruction_set_features_; + return instruction_set_features_bitmap_; } uint32_t OatHeader::GetExecutableOffset() const { diff --git a/runtime/oat.h b/runtime/oat.h index 6a32e3e56e..92b98b1be5 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -37,7 +37,7 @@ class PACKED(4) OatHeader { static constexpr const char* kDex2OatHostKey = "dex2oat-host"; static OatHeader* Create(InstructionSet instruction_set, - const InstructionSetFeatures& instruction_set_features, + const InstructionSetFeatures* instruction_set_features, const std::vector<const DexFile*>* dex_files, uint32_t image_file_location_oat_checksum, uint32_t image_file_location_oat_data_begin, @@ -93,7 +93,7 @@ class PACKED(4) OatHeader { void SetImagePatchDelta(int32_t off); InstructionSet GetInstructionSet() const; - const InstructionSetFeatures& GetInstructionSetFeatures() const; + uint32_t GetInstructionSetFeaturesBitmap() const; uint32_t GetImageFileLocationOatChecksum() const; uint32_t GetImageFileLocationOatDataBegin() const; @@ -106,7 +106,7 @@ class PACKED(4) OatHeader { private: OatHeader(InstructionSet instruction_set, - const InstructionSetFeatures& instruction_set_features, + const InstructionSetFeatures* instruction_set_features, const std::vector<const DexFile*>* dex_files, uint32_t image_file_location_oat_checksum, uint32_t image_file_location_oat_data_begin, @@ -119,7 +119,7 @@ class PACKED(4) OatHeader { uint32_t adler32_checksum_; InstructionSet instruction_set_; - InstructionSetFeatures instruction_set_features_; + uint32_t instruction_set_features_bitmap_; uint32_t dex_file_count_; uint32_t executable_offset_; uint32_t interpreter_to_interpreter_bridge_offset_; diff --git a/runtime/offsets.cc b/runtime/offsets.cc index 369140176e..f59ed881af 100644 --- a/runtime/offsets.cc +++ b/runtime/offsets.cc @@ -16,7 +16,7 @@ #include "offsets.h" -#include <iostream> // NOLINT +#include <ostream> namespace art { diff --git a/runtime/offsets.h b/runtime/offsets.h index 72a6b0f31f..9d5063f3ee 100644 --- a/runtime/offsets.h +++ b/runtime/offsets.h @@ -17,7 +17,8 @@ #ifndef ART_RUNTIME_OFFSETS_H_ #define ART_RUNTIME_OFFSETS_H_ -#include <iostream> // NOLINT +#include <ostream> + #include "globals.h" namespace art { diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index d82002683a..6b64c257df 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -16,6 +16,8 @@ #include "parsed_options.h" +#include <sstream> + #ifdef HAVE_ANDROID_OS #include "cutils/properties.h" #endif @@ -205,7 +207,7 @@ static gc::CollectorType ParseCollectorType(const std::string& option) { bool ParsedOptions::ParseXGcOption(const std::string& option) { std::vector<std::string> gc_options; - Split(option.substr(strlen("-Xgc:")), ',', gc_options); + Split(option.substr(strlen("-Xgc:")), ',', &gc_options); for (const std::string& gc_option : gc_options) { gc::CollectorType collector_type = ParseCollectorType(gc_option); if (collector_type != gc::kCollectorTypeNone) { @@ -501,7 +503,7 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize is_explicit_gc_disabled_ = true; } else if (StartsWith(option, "-verbose:")) { std::vector<std::string> verbose_options; - Split(option.substr(strlen("-verbose:")), ',', verbose_options); + Split(option.substr(strlen("-verbose:")), ',', &verbose_options); for (size_t i = 0; i < verbose_options.size(); ++i) { if (verbose_options[i] == "class") { gLogVerbosity.class_linker = true; @@ -534,9 +536,6 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize return false; } } - } else if (StartsWith(option, "-verbose-methods:")) { - gLogVerbosity.compiler = false; - Split(option.substr(strlen("-verbose-methods:")), ',', gVerboseMethods); } else if (StartsWith(option, "-Xlockprofthreshold:")) { if (!ParseUnsignedInteger(option, ':', &lock_profiling_threshold_)) { return false; diff --git a/runtime/profiler.cc b/runtime/profiler.cc index cde4177d6d..e399195008 100644 --- a/runtime/profiler.cc +++ b/runtime/profiler.cc @@ -16,9 +16,11 @@ #include "profiler.h" -#include <fstream> -#include <sys/uio.h> #include <sys/file.h> +#include <sys/stat.h> +#include <sys/uio.h> + +#include <fstream> #include "base/stl_util.h" #include "base/unix_file/fd_file.h" @@ -95,7 +97,7 @@ static void GetSample(Thread* thread, void* arg) SHARED_LOCKS_REQUIRED(Locks::mu switch (profile_options.GetProfileType()) { case kProfilerMethod: { mirror::ArtMethod* method = thread->GetCurrentMethod(nullptr); - if (false && method == nullptr) { + if ((false) && method == nullptr) { LOG(INFO) << "No current method available"; std::ostringstream os; thread->Dump(os); @@ -742,7 +744,7 @@ void ProfileSampleResults::ReadPrevious(int fd, ProfileDataType type) { return; } std::vector<std::string> summary_info; - Split(line, '/', summary_info); + Split(line, '/', &summary_info); if (summary_info.size() != 3) { // Bad summary info. It should be count/nullcount/bootcount return; @@ -757,7 +759,7 @@ void ProfileSampleResults::ReadPrevious(int fd, ProfileDataType type) { break; } std::vector<std::string> info; - Split(line, '/', info); + Split(line, '/', &info); if (info.size() != 3 && info.size() != 4) { // Malformed. break; @@ -770,10 +772,10 @@ void ProfileSampleResults::ReadPrevious(int fd, ProfileDataType type) { context_map = new PreviousContextMap(); std::string context_counts_str = info[3].substr(1, info[3].size() - 2); std::vector<std::string> context_count_pairs; - Split(context_counts_str, '#', context_count_pairs); + Split(context_counts_str, '#', &context_count_pairs); for (uint32_t i = 0; i < context_count_pairs.size(); ++i) { std::vector<std::string> context_count; - Split(context_count_pairs[i], ':', context_count); + Split(context_count_pairs[i], ':', &context_count); if (context_count.size() == 2) { // Handles the situtation when the profile file doesn't contain context information. uint32_t dexpc = strtoul(context_count[0].c_str(), nullptr, 10); @@ -819,7 +821,7 @@ bool ProfileFile::LoadFile(const std::string& fileName) { return false; } std::vector<std::string> summary_info; - Split(line, '/', summary_info); + Split(line, '/', &summary_info); if (summary_info.size() != 3) { // Bad summary info. It should be total/null/boot. return false; @@ -837,7 +839,7 @@ bool ProfileFile::LoadFile(const std::string& fileName) { break; } std::vector<std::string> info; - Split(line, '/', info); + Split(line, '/', &info); if (info.size() != 3 && info.size() != 4) { // Malformed. return false; diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 2c158ba963..c58735a94e 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -19,6 +19,7 @@ #include "arch/context.h" #include "dex_instruction.h" #include "entrypoints/entrypoint_utils.h" +#include "entrypoints/runtime_asm_entrypoints.h" #include "handle_scope-inl.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" @@ -96,7 +97,7 @@ class CatchBlockStackVisitor FINAL : public StackVisitor { if (found_dex_pc != DexFile::kDexNoIndex) { exception_handler_->SetHandlerMethod(method.Get()); exception_handler_->SetHandlerDexPc(found_dex_pc); - exception_handler_->SetHandlerQuickFramePc(method->ToNativePc(found_dex_pc)); + exception_handler_->SetHandlerQuickFramePc(method->ToNativeQuickPc(found_dex_pc)); exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame()); return false; // End stack walk. } @@ -299,7 +300,7 @@ class InstrumentationStackVisitor : public StackVisitor { InstrumentationStackVisitor(Thread* self, bool is_deoptimization, size_t frame_depth) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : StackVisitor(self, nullptr), - self_(self), frame_depth_(frame_depth), + frame_depth_(frame_depth), instrumentation_frames_to_pop_(0) { CHECK_NE(frame_depth_, kInvalidFrameDepth); } @@ -308,7 +309,7 @@ class InstrumentationStackVisitor : public StackVisitor { size_t current_frame_depth = GetFrameDepth(); if (current_frame_depth < frame_depth_) { CHECK(GetMethod() != nullptr); - if (UNLIKELY(GetQuickInstrumentationExitPc() == GetReturnPc())) { + if (UNLIKELY(reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) == GetReturnPc())) { ++instrumentation_frames_to_pop_; } return true; @@ -323,7 +324,6 @@ class InstrumentationStackVisitor : public StackVisitor { } private: - Thread* const self_; const size_t frame_depth_; size_t instrumentation_frames_to_pop_; diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 23f8076e0f..b57e48fdd5 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -19,6 +19,7 @@ #include "class_linker.h" #include "common_throws.h" #include "dex_file-inl.h" +#include "entrypoints/entrypoint_utils.h" #include "jni_internal.h" #include "method_helper-inl.h" #include "mirror/art_field-inl.h" @@ -528,7 +529,7 @@ JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnab } void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg_offset, - MethodHelper& mh, JValue* result) { + MethodHelper* mh, JValue* result) { // We want to make sure that the stack is not within a small distance from the // protected region in case we are calling into a leaf function whose stack // check has been elided. @@ -537,10 +538,10 @@ void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg return; } - ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); + ArgArray arg_array(mh->GetShorty(), mh->GetShortyLength()); arg_array.BuildArgArrayFromFrame(shadow_frame, arg_offset); shadow_frame->GetMethod()->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result, - mh.GetShorty()); + mh->GetShorty()); } jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaMethod, diff --git a/runtime/reflection.h b/runtime/reflection.h index 23d8e05452..f9a795194d 100644 --- a/runtime/reflection.h +++ b/runtime/reflection.h @@ -65,7 +65,7 @@ JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnab SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg_offset, - MethodHelper& mh, JValue* result) + MethodHelper* mh, JValue* result) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject method, jobject receiver, diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 42d05a9307..8ba098f5d9 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -43,10 +43,14 @@ #include "arch/x86/registers_x86.h" #include "arch/x86_64/quick_method_frame_info_x86_64.h" #include "arch/x86_64/registers_x86_64.h" +#include "asm_support.h" #include "atomic.h" +#include "base/dumpable.h" +#include "base/unix_file/fd_file.h" #include "class_linker.h" #include "debugger.h" #include "elf_file.h" +#include "entrypoints/runtime_asm_entrypoints.h" #include "fault_handler.h" #include "gc/accounting/card_table-inl.h" #include "gc/heap.h" @@ -92,10 +96,9 @@ namespace art { +// If a signal isn't handled properly, enable a handler that attempts to dump the Java stack. static constexpr bool kEnableJavaStackTraceHandler = false; -const char* Runtime::kDefaultInstructionSetFeatures = - STRINGIFY(ART_DEFAULT_INSTRUCTION_SET_FEATURES); -Runtime* Runtime::instance_ = NULL; +Runtime* Runtime::instance_ = nullptr; Runtime::Runtime() : instruction_set_(kNone), @@ -144,10 +147,15 @@ Runtime::Runtime() target_sdk_version_(0), implicit_null_checks_(false), implicit_so_checks_(false), - implicit_suspend_checks_(false) { + implicit_suspend_checks_(false), + is_native_bridge_loaded_(false) { + CheckAsmSupportOffsetsAndSizes(); } Runtime::~Runtime() { + if (is_native_bridge_loaded_) { + UnloadNativeBridge(); + } if (dump_gc_performance_on_shutdown_) { // This can't be called from the Heap destructor below because it // could call RosAlloc::InspectAll() which needs the thread_list @@ -169,9 +177,6 @@ Runtime::~Runtime() { BackgroundMethodSamplingProfiler::Shutdown(); } - // Shutdown the fault manager if it was initialized. - fault_manager.Shutdown(); - Trace::Shutdown(); // Make sure to let the GC complete if it is running. @@ -184,6 +189,10 @@ Runtime::~Runtime() { // Make sure all other non-daemon threads have terminated, and all daemon threads are suspended. delete thread_list_; + + // Shutdown the fault manager if it was initialized. + fault_manager.Shutdown(); + delete monitor_list_; delete monitor_pool_; delete class_linker_; @@ -193,13 +202,14 @@ Runtime::~Runtime() { Thread::Shutdown(); QuasiAtomic::Shutdown(); verifier::MethodVerifier::Shutdown(); + MemMap::Shutdown(); // TODO: acquire a static mutex on Runtime to avoid racing. CHECK(instance_ == nullptr || instance_ == this); instance_ = nullptr; } struct AbortState { - void Dump(std::ostream& os) { + void Dump(std::ostream& os) const { if (gAborting > 1) { os << "Runtime aborting --- recursively, so no thread-specific detail!\n"; return; @@ -230,7 +240,7 @@ struct AbortState { } // No thread-safety analysis as we do explicitly test for holding the mutator lock. - void DumpThread(std::ostream& os, Thread* self) NO_THREAD_SAFETY_ANALYSIS { + void DumpThread(std::ostream& os, Thread* self) const NO_THREAD_SAFETY_ANALYSIS { DCHECK(Locks::mutator_lock_->IsExclusiveHeld(self) || Locks::mutator_lock_->IsSharedHeld(self)); self->Dump(os); if (self->IsExceptionPending()) { @@ -242,7 +252,7 @@ struct AbortState { } } - void DumpAllThreads(std::ostream& os, Thread* self) { + void DumpAllThreads(std::ostream& os, Thread* self) const { Runtime* runtime = Runtime::Current(); if (runtime != nullptr) { ThreadList* thread_list = runtime->GetThreadList(); @@ -425,12 +435,11 @@ bool Runtime::Start() { return false; } } else { - bool have_native_bridge = !native_bridge_library_filename_.empty(); - if (have_native_bridge) { + if (is_native_bridge_loaded_) { PreInitializeNativeBridge("."); } - DidForkFromZygote(self->GetJniEnv(), have_native_bridge ? NativeBridgeAction::kInitialize : - NativeBridgeAction::kUnload, GetInstructionSetString(kRuntimeISA)); + DidForkFromZygote(self->GetJniEnv(), NativeBridgeAction::kInitialize, + GetInstructionSetString(kRuntimeISA)); } StartDaemonThreads(); @@ -509,14 +518,17 @@ bool Runtime::InitZygote() { void Runtime::DidForkFromZygote(JNIEnv* env, NativeBridgeAction action, const char* isa) { is_zygote_ = false; - switch (action) { - case NativeBridgeAction::kUnload: - UnloadNativeBridge(); - break; + if (is_native_bridge_loaded_) { + switch (action) { + case NativeBridgeAction::kUnload: + UnloadNativeBridge(); + is_native_bridge_loaded_ = false; + break; - case NativeBridgeAction::kInitialize: - InitializeNativeBridge(env, isa); - break; + case NativeBridgeAction::kInitialize: + InitializeNativeBridge(env, isa); + break; + } } // Create the thread pool. @@ -645,6 +657,8 @@ static size_t OpenDexFiles(const std::vector<std::string>& dex_filenames, bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) { CHECK_EQ(sysconf(_SC_PAGE_SIZE), kPageSize); + MemMap::Init(); + std::unique_ptr<ParsedOptions> options(ParsedOptions::Create(raw_options, ignore_unrecognized)); if (options.get() == nullptr) { LOG(ERROR) << "Failed to parse options"; @@ -800,7 +814,7 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) } } else if (!IsCompiler() || !image_dex2oat_enabled_) { std::vector<std::string> dex_filenames; - Split(boot_class_path_string_, ':', dex_filenames); + Split(boot_class_path_string_, ':', &dex_filenames); std::vector<const DexFile*> boot_class_path; OpenDexFiles(dex_filenames, options->image_, boot_class_path); class_linker_->InitWithoutImage(boot_class_path); @@ -844,14 +858,14 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) // Pre-allocate an OutOfMemoryError for the double-OOME case. self->ThrowNewException(ThrowLocation(), "Ljava/lang/OutOfMemoryError;", "OutOfMemoryError thrown while trying to throw OutOfMemoryError; " - "no stack available"); + "no stack trace available"); pre_allocated_OutOfMemoryError_ = GcRoot<mirror::Throwable>(self->GetException(NULL)); self->ClearException(); // Pre-allocate a NoClassDefFoundError for the common case of failing to find a system class // ahead of checking the application's class loader. self->ThrowNewException(ThrowLocation(), "Ljava/lang/NoClassDefFoundError;", - "Class not found using the boot class loader; no stack available"); + "Class not found using the boot class loader; no stack trace available"); pre_allocated_NoClassDefFoundError_ = GcRoot<mirror::Throwable>(self->GetException(NULL)); self->ClearException(); @@ -882,8 +896,7 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) // Runtime::Start(): // DidForkFromZygote(kInitialize) -> try to initialize any native bridge given. // No-op wrt native bridge. - native_bridge_library_filename_ = options->native_bridge_library_filename_; - LoadNativeBridge(native_bridge_library_filename_); + is_native_bridge_loaded_ = LoadNativeBridge(options->native_bridge_library_filename_); VLOG(startup) << "Runtime::Init exiting"; return true; @@ -911,7 +924,7 @@ void Runtime::InitNativeMethods() { { std::string mapped_name(StringPrintf(OS_SHARED_LIB_FORMAT_STR, "javacore")); std::string reason; - if (!instance_->java_vm_->LoadNativeLibrary(env, mapped_name, nullptr, &reason)) { + if (!java_vm_->LoadNativeLibrary(env, mapped_name, nullptr, &reason)) { LOG(FATAL) << "LoadNativeLibrary failed for \"" << mapped_name << "\": " << reason; } } @@ -1213,8 +1226,8 @@ mirror::ArtMethod* Runtime::CreateImtConflictMethod() { method->SetEntryPointFromPortableCompiledCode(nullptr); method->SetEntryPointFromQuickCompiledCode(nullptr); } else { - method->SetEntryPointFromPortableCompiledCode(class_linker->GetPortableImtConflictTrampoline()); - method->SetEntryPointFromQuickCompiledCode(class_linker->GetQuickImtConflictTrampoline()); + method->SetEntryPointFromPortableCompiledCode(GetPortableImtConflictStub()); + method->SetEntryPointFromQuickCompiledCode(GetQuickImtConflictStub()); } return method.Get(); } @@ -1233,8 +1246,8 @@ mirror::ArtMethod* Runtime::CreateResolutionMethod() { method->SetEntryPointFromPortableCompiledCode(nullptr); method->SetEntryPointFromQuickCompiledCode(nullptr); } else { - method->SetEntryPointFromPortableCompiledCode(class_linker->GetPortableResolutionTrampoline()); - method->SetEntryPointFromQuickCompiledCode(class_linker->GetQuickResolutionTrampoline()); + method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionStub()); + method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub()); } return method.Get(); } @@ -1451,9 +1464,10 @@ void Runtime::AddCurrentRuntimeFeaturesAsDex2OatArguments(std::vector<std::strin instruction_set += GetInstructionSetString(kRuntimeISA); argv->push_back(instruction_set); - std::string features("--instruction-set-features="); - features += GetDefaultInstructionSetFeatures(); - argv->push_back(features); + std::unique_ptr<const InstructionSetFeatures> features(InstructionSetFeatures::FromCppDefines()); + std::string feature_string("--instruction-set-features="); + feature_string += features->GetFeatureString(); + argv->push_back(feature_string); } void Runtime::UpdateProfilerState(int state) { diff --git a/runtime/runtime.h b/runtime/runtime.h index 30dabe7af4..bfa7d720cb 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -177,10 +177,7 @@ class Runtime { // Aborts semi-cleanly. Used in the implementation of LOG(FATAL), which most // callers should prefer. - // This isn't marked ((noreturn)) because then gcc will merge multiple calls - // in a single function together. This reduces code size slightly, but means - // that the native stack trace we get may point at the wrong call site. - static void Abort() NO_RETURN LOCKS_EXCLUDED(Locks::abort_lock_); + [[noreturn]] static void Abort() LOCKS_EXCLUDED(Locks::abort_lock_); // Returns the "main" ThreadGroup, used when attaching user threads. jobject GetMainThreadGroup() const; @@ -486,10 +483,6 @@ class Runtime { return target_sdk_version_; } - static const char* GetDefaultInstructionSetFeatures() { - return kDefaultInstructionSetFeatures; - } - private: static void InitPlatformSignalHandlers(); @@ -509,8 +502,6 @@ class Runtime { // A pointer to the active runtime or NULL. static Runtime* instance_; - static const char* kDefaultInstructionSetFeatures; - // NOTE: these must match the gc::ProcessState values as they come directly from the framework. static constexpr int kProfileForground = 0; static constexpr int kProfileBackgrouud = 1; @@ -644,14 +635,16 @@ class Runtime { bool implicit_so_checks_; // StackOverflow checks are implicit. bool implicit_suspend_checks_; // Thread suspension checks are implicit. - // The filename to the native bridge library. If this is not empty the native bridge will be - // initialized and loaded from the given file (initialized and available). An empty value means - // that there's no native bridge (initialized but not available). + // Whether or not a native bridge has been loaded. // // The native bridge allows running native code compiled for a foreign ISA. The way it works is, // if standard dlopen fails to load native library associated with native activity, it calls to // the native bridge to load it and then gets the trampoline for the entry to native activity. - std::string native_bridge_library_filename_; + // + // The option 'native_bridge_library_filename' specifies the name of the native bridge. + // When non-empty the native bridge will be loaded from the given file. An empty value means + // that there's no native bridge. + bool is_native_bridge_loaded_; DISALLOW_COPY_AND_ASSIGN(Runtime); }; diff --git a/runtime/runtime_android.cc b/runtime/runtime_android.cc index 079d7e5900..33600ddba5 100644 --- a/runtime/runtime_android.cc +++ b/runtime/runtime_android.cc @@ -32,13 +32,12 @@ static constexpr bool kUseSignalHandler = false; struct sigaction old_action; void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context) { - static bool handlingUnexpectedSignal = false; - if (handlingUnexpectedSignal) { - LogMessageData data(__FILE__, __LINE__, INTERNAL_FATAL, -1); - LogMessage::LogLine(data, "HandleUnexpectedSignal reentered\n"); + static bool handling_unexpected_signal = false; + if (handling_unexpected_signal) { + LogMessage::LogLine(__FILE__, __LINE__, INTERNAL_FATAL, "HandleUnexpectedSignal reentered\n"); _exit(1); } - handlingUnexpectedSignal = true; + handling_unexpected_signal = true; gAborting++; // set before taking any locks MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_); diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc index 46ee27405f..1de035c0d5 100644 --- a/runtime/runtime_linux.cc +++ b/runtime/runtime_linux.cc @@ -21,6 +21,9 @@ #include <sys/utsname.h> #include <inttypes.h> +#include <sstream> + +#include "base/dumpable.h" #include "base/logging.h" #include "base/mutex.h" #include "base/stringprintf.h" @@ -32,13 +35,13 @@ namespace art { static constexpr bool kDumpHeapObjectOnSigsevg = false; struct Backtrace { - void Dump(std::ostream& os) { + void Dump(std::ostream& os) const { DumpNativeStack(os, GetTid(), "\t"); } }; struct OsInfo { - void Dump(std::ostream& os) { + void Dump(std::ostream& os) const { utsname info; uname(&info); // Linux 2.6.38.8-gg784 (x86_64) @@ -132,9 +135,11 @@ static const char* GetSignalCodeName(int signal_number, int signal_code) { } struct UContext { - explicit UContext(void* raw_context) : context(reinterpret_cast<ucontext_t*>(raw_context)->uc_mcontext) {} + explicit UContext(void* raw_context) : + context(reinterpret_cast<ucontext_t*>(raw_context)->uc_mcontext) { + } - void Dump(std::ostream& os) { + void Dump(std::ostream& os) const { // TODO: support non-x86 hosts (not urgent because this code doesn't run on targets). #if defined(__APPLE__) && defined(__i386__) DumpRegister32(os, "eax", context->__ss.__eax); @@ -228,15 +233,15 @@ struct UContext { #endif } - void DumpRegister32(std::ostream& os, const char* name, uint32_t value) { + void DumpRegister32(std::ostream& os, const char* name, uint32_t value) const { os << StringPrintf(" %6s: 0x%08x", name, value); } - void DumpRegister64(std::ostream& os, const char* name, uint64_t value) { + void DumpRegister64(std::ostream& os, const char* name, uint64_t value) const { os << StringPrintf(" %6s: 0x%016" PRIx64, name, value); } - void DumpX86Flags(std::ostream& os, uint32_t flags) { + void DumpX86Flags(std::ostream& os, uint32_t flags) const { os << " ["; if ((flags & (1 << 0)) != 0) { os << " CF"; @@ -274,8 +279,7 @@ struct UContext { void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context) { static bool handlingUnexpectedSignal = false; if (handlingUnexpectedSignal) { - LogMessageData data(__FILE__, __LINE__, INTERNAL_FATAL, -1); - LogMessage::LogLine(data, "HandleUnexpectedSignal reentered\n"); + LogMessage::LogLine(__FILE__, __LINE__, INTERNAL_FATAL, "HandleUnexpectedSignal reentered\n"); _exit(1); } handlingUnexpectedSignal = true; diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc index 336340eca9..d4ec80372d 100644 --- a/runtime/signal_catcher.cc +++ b/runtime/signal_catcher.cc @@ -25,6 +25,8 @@ #include <sys/types.h> #include <unistd.h> +#include <sstream> + #include "base/unix_file/fd_file.h" #include "class_linker.h" #include "gc/heap.h" @@ -131,7 +133,7 @@ void SignalCatcher::HandleSigQuit() { runtime->DumpForSigQuit(os); - if (false) { + if ((false)) { std::string maps; if (ReadFileToString("/proc/self/maps", &maps)) { os << "/proc/self/maps:\n" << maps; diff --git a/runtime/stack.cc b/runtime/stack.cc index 008941fcee..0cdc984e87 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -18,6 +18,7 @@ #include "arch/context.h" #include "base/hex_dump.h" +#include "entrypoints/runtime_asm_entrypoints.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/object.h" @@ -119,7 +120,7 @@ mirror::Object* StackVisitor::GetThisObject() const { } else if (m->IsNative()) { if (cur_quick_frame_ != nullptr) { HandleScope* hs = reinterpret_cast<HandleScope*>( - reinterpret_cast<char*>(cur_quick_frame_) + m->GetHandleScopeOffsetInBytes()); + reinterpret_cast<char*>(cur_quick_frame_) + m->GetHandleScopeOffset().SizeValue()); return hs->GetReference(0); } else { return cur_shadow_frame_->GetVRegReference(0); @@ -143,7 +144,7 @@ mirror::Object* StackVisitor::GetThisObject() const { size_t StackVisitor::GetNativePcOffset() const { DCHECK(!IsShadowFrame()); - return GetMethod()->NativePcOffset(cur_quick_frame_pc_); + return GetMethod()->NativeQuickPcOffset(cur_quick_frame_pc_); } bool StackVisitor::GetVReg(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind, @@ -394,14 +395,14 @@ bool StackVisitor::SetFPR(uint32_t reg, uintptr_t value) { uintptr_t StackVisitor::GetReturnPc() const { uint8_t* sp = reinterpret_cast<uint8_t*>(GetCurrentQuickFrame()); DCHECK(sp != NULL); - uint8_t* pc_addr = sp + GetMethod()->GetReturnPcOffsetInBytes(); + uint8_t* pc_addr = sp + GetMethod()->GetReturnPcOffset().SizeValue(); return *reinterpret_cast<uintptr_t*>(pc_addr); } void StackVisitor::SetReturnPc(uintptr_t new_ret_pc) { uint8_t* sp = reinterpret_cast<uint8_t*>(GetCurrentQuickFrame()); CHECK(sp != NULL); - uint8_t* pc_addr = sp + GetMethod()->GetReturnPcOffsetInBytes(); + uint8_t* pc_addr = sp + GetMethod()->GetReturnPcOffset().SizeValue(); *reinterpret_cast<uintptr_t*>(pc_addr) = new_ret_pc; } @@ -509,7 +510,7 @@ void StackVisitor::SanityCheckFrame() const { // const size_t kMaxExpectedFrameSize = (256 + 2 + 3 + 3) * sizeof(word); const size_t kMaxExpectedFrameSize = 2 * KB; CHECK_LE(frame_size, kMaxExpectedFrameSize); - size_t return_pc_offset = method->GetReturnPcOffsetInBytes(); + size_t return_pc_offset = method->GetReturnPcOffset().SizeValue(); CHECK_LT(return_pc_offset, frame_size); } } @@ -525,7 +526,7 @@ void StackVisitor::WalkStack(bool include_transitions) { current_fragment = current_fragment->GetLink()) { cur_shadow_frame_ = current_fragment->GetTopShadowFrame(); cur_quick_frame_ = current_fragment->GetTopQuickFrame(); - cur_quick_frame_pc_ = current_fragment->GetTopQuickFramePc(); + cur_quick_frame_pc_ = 0; if (cur_quick_frame_ != NULL) { // Handle quick stack frames. // Can't be both a shadow and a quick fragment. @@ -543,13 +544,13 @@ void StackVisitor::WalkStack(bool include_transitions) { } size_t frame_size = method->GetFrameSizeInBytes(); // Compute PC for next stack frame from return PC. - size_t return_pc_offset = method->GetReturnPcOffsetInBytes(frame_size); + size_t return_pc_offset = method->GetReturnPcOffset(frame_size).SizeValue(); uint8_t* return_pc_addr = reinterpret_cast<uint8_t*>(cur_quick_frame_) + return_pc_offset; uintptr_t return_pc = *reinterpret_cast<uintptr_t*>(return_pc_addr); if (UNLIKELY(exit_stubs_installed)) { // While profiling, the return pc is restored from the side stack, except when walking // the stack for an exception where the side stack will be unwound in VisitFrame. - if (GetQuickInstrumentationExitPc() == return_pc) { + if (reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) == return_pc) { const instrumentation::InstrumentationStackFrame& instrumentation_frame = GetInstrumentationStackFrame(thread_, instrumentation_stack_depth); instrumentation_stack_depth++; diff --git a/runtime/stack.h b/runtime/stack.h index 25e50a1a1f..2f8df61099 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -360,7 +360,7 @@ class ShadowFrame { class PACKED(4) ManagedStack { public: ManagedStack() - : link_(NULL), top_shadow_frame_(NULL), top_quick_frame_(NULL), top_quick_frame_pc_(0) {} + : top_quick_frame_(nullptr), link_(nullptr), top_shadow_frame_(nullptr) {} void PushManagedStackFragment(ManagedStack* fragment) { // Copy this top fragment into given fragment. @@ -386,29 +386,16 @@ class PACKED(4) ManagedStack { } void SetTopQuickFrame(StackReference<mirror::ArtMethod>* top) { - DCHECK(top_shadow_frame_ == NULL); + DCHECK(top_shadow_frame_ == nullptr); top_quick_frame_ = top; } - uintptr_t GetTopQuickFramePc() const { - return top_quick_frame_pc_; - } - - void SetTopQuickFramePc(uintptr_t pc) { - DCHECK(top_shadow_frame_ == NULL); - top_quick_frame_pc_ = pc; - } - static size_t TopQuickFrameOffset() { return OFFSETOF_MEMBER(ManagedStack, top_quick_frame_); } - static size_t TopQuickFramePcOffset() { - return OFFSETOF_MEMBER(ManagedStack, top_quick_frame_pc_); - } - ShadowFrame* PushShadowFrame(ShadowFrame* new_top_frame) { - DCHECK(top_quick_frame_ == NULL); + DCHECK(top_quick_frame_ == nullptr); ShadowFrame* old_frame = top_shadow_frame_; top_shadow_frame_ = new_top_frame; new_top_frame->SetLink(old_frame); @@ -416,8 +403,8 @@ class PACKED(4) ManagedStack { } ShadowFrame* PopShadowFrame() { - DCHECK(top_quick_frame_ == NULL); - CHECK(top_shadow_frame_ != NULL); + DCHECK(top_quick_frame_ == nullptr); + CHECK(top_shadow_frame_ != nullptr); ShadowFrame* frame = top_shadow_frame_; top_shadow_frame_ = frame->GetLink(); return frame; @@ -428,7 +415,7 @@ class PACKED(4) ManagedStack { } void SetTopShadowFrame(ShadowFrame* top) { - DCHECK(top_quick_frame_ == NULL); + DCHECK(top_quick_frame_ == nullptr); top_shadow_frame_ = top; } @@ -441,10 +428,9 @@ class PACKED(4) ManagedStack { bool ShadowFramesContain(StackReference<mirror::Object>* shadow_frame_entry) const; private: + StackReference<mirror::ArtMethod>* top_quick_frame_; ManagedStack* link_; ShadowFrame* top_shadow_frame_; - StackReference<mirror::ArtMethod>* top_quick_frame_; - uintptr_t top_quick_frame_pc_; }; class StackVisitor { diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 9b49d31ed9..b1c46a9771 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -88,6 +88,7 @@ class DexRegisterMap { kNone, kInStack, kInRegister, + kInFpuRegister, kConstant }; diff --git a/runtime/thread.cc b/runtime/thread.cc index b0c8fe1b3a..9c04133e3a 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -29,9 +29,11 @@ #include <cerrno> #include <iostream> #include <list> +#include <sstream> #include "arch/context.h" #include "base/mutex.h" +#include "base/to_str.h" #include "class_linker-inl.h" #include "class_linker.h" #include "debugger.h" @@ -409,6 +411,11 @@ Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_g } } + { + ScopedObjectAccess soa(self); + Dbg::PostThreadStart(self); + } + return self; } @@ -947,10 +954,36 @@ static bool ShouldShowNativeStack(const Thread* thread) } void Thread::DumpJavaStack(std::ostream& os) const { + // Dumping the Java stack involves the verifier for locks. The verifier operates under the + // assumption that there is no exception pending on entry. Thus, stash any pending exception. + // TODO: Find a way to avoid const_cast. + StackHandleScope<3> scope(const_cast<Thread*>(this)); + Handle<mirror::Throwable> exc; + Handle<mirror::Object> throw_location_this_object; + Handle<mirror::ArtMethod> throw_location_method; + uint32_t throw_location_dex_pc; + bool have_exception = false; + if (IsExceptionPending()) { + ThrowLocation exc_location; + exc = scope.NewHandle(GetException(&exc_location)); + throw_location_this_object = scope.NewHandle(exc_location.GetThis()); + throw_location_method = scope.NewHandle(exc_location.GetMethod()); + throw_location_dex_pc = exc_location.GetDexPc(); + const_cast<Thread*>(this)->ClearException(); + have_exception = true; + } + std::unique_ptr<Context> context(Context::Create()); StackDumpVisitor dumper(os, const_cast<Thread*>(this), context.get(), !tls32_.throwing_OutOfMemoryError); dumper.WalkStack(); + + if (have_exception) { + ThrowLocation exc_location(throw_location_this_object.Get(), + throw_location_method.Get(), + throw_location_dex_pc); + const_cast<Thread*>(this)->SetException(exc_location, exc.Get()); + } } void Thread::DumpStack(std::ostream& os) const { @@ -1786,7 +1819,6 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) { DO_THREAD_OFFSET(StackEndOffset<ptr_size>(), "stack_end") DO_THREAD_OFFSET(ThinLockIdOffset<ptr_size>(), "thin_lock_thread_id") DO_THREAD_OFFSET(TopOfManagedStackOffset<ptr_size>(), "top_quick_frame_method") - DO_THREAD_OFFSET(TopOfManagedStackPcOffset<ptr_size>(), "top_quick_frame_pc") DO_THREAD_OFFSET(TopShadowFrameOffset<ptr_size>(), "top_shadow_frame") DO_THREAD_OFFSET(TopHandleScopeOffset<ptr_size>(), "top_handle_scope") DO_THREAD_OFFSET(ThreadSuspendTriggerOffset<ptr_size>(), "suspend_trigger") @@ -2082,7 +2114,7 @@ class ReferenceMapVisitor : public StackVisitor { if (m->IsOptimized()) { Runtime* runtime = Runtime::Current(); const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m); - uintptr_t native_pc_offset = m->NativePcOffset(GetCurrentQuickFramePc(), entry_point); + uintptr_t native_pc_offset = m->NativeQuickPcOffset(GetCurrentQuickFramePc(), entry_point); StackMap map = m->GetStackMap(native_pc_offset); MemoryRegion mask = map.GetStackMask(); for (size_t i = 0; i < mask.size_in_bits(); ++i) { @@ -2111,7 +2143,7 @@ class ReferenceMapVisitor : public StackVisitor { if (num_regs > 0) { Runtime* runtime = Runtime::Current(); const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m); - uintptr_t native_pc_offset = m->NativePcOffset(GetCurrentQuickFramePc(), entry_point); + uintptr_t native_pc_offset = m->NativeQuickPcOffset(GetCurrentQuickFramePc(), entry_point); const uint8_t* reg_bitmap = map.FindBitMap(native_pc_offset); DCHECK(reg_bitmap != nullptr); const void* code_pointer = mirror::ArtMethod::EntryPointToCodePointer(entry_point); diff --git a/runtime/thread.h b/runtime/thread.h index b0be841730..694dbda978 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -144,6 +144,9 @@ class Thread { // Reset internal state of child thread after fork. void InitAfterFork(); + // Get the currently executing thread, frequently referred to as 'self'. This call has reasonably + // high cost and so we favor passing self around when possible. + // TODO: mark as PURE so the compiler may coalesce and remove? static Thread* Current(); // On a runnable thread, check for pending thread suspension request and handle if pending. @@ -362,9 +365,8 @@ class Thread { ThrowLocation GetCurrentLocationForThrow() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void SetTopOfStack(StackReference<mirror::ArtMethod>* top_method, uintptr_t pc) { + void SetTopOfStack(StackReference<mirror::ArtMethod>* top_method) { tlsPtr_.managed_stack.SetTopQuickFrame(top_method); - tlsPtr_.managed_stack.SetTopQuickFramePc(pc); } void SetTopOfShadowStack(ShadowFrame* top) { @@ -634,13 +636,6 @@ class Thread { ManagedStack::TopQuickFrameOffset()); } - template<size_t pointer_size> - static ThreadOffset<pointer_size> TopOfManagedStackPcOffset() { - return ThreadOffsetFromTlsPtr<pointer_size>( - OFFSETOF_MEMBER(tls_ptr_sized_values, managed_stack) + - ManagedStack::TopQuickFramePcOffset()); - } - const ManagedStack* GetManagedStack() const { return &tlsPtr_.managed_stack; } diff --git a/runtime/thread_linux.cc b/runtime/thread_linux.cc index 125405625b..0284364de7 100644 --- a/runtime/thread_linux.cc +++ b/runtime/thread_linux.cc @@ -16,6 +16,8 @@ #include "thread.h" +#include <signal.h> + namespace art { void Thread::SetNativePriority(int) { diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index f714c1391c..f8c8fdbfee 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -25,6 +25,8 @@ #include <sys/types.h> #include <unistd.h> +#include <sstream> + #include "base/mutex.h" #include "base/mutex-inl.h" #include "base/timing_logger.h" @@ -34,6 +36,7 @@ #include "monitor.h" #include "scoped_thread_state_change.h" #include "thread.h" +#include "trace.h" #include "utils.h" #include "well_known_classes.h" @@ -479,17 +482,18 @@ void ThreadList::Resume(Thread* thread, bool for_debugger) { VLOG(threads) << "Resume(" << reinterpret_cast<void*>(thread) << ") complete"; } -static void ThreadSuspendByPeerWarning(Thread* self, int level, const char* message, jobject peer) { +static void ThreadSuspendByPeerWarning(Thread* self, LogSeverity severity, const char* message, + jobject peer) { JNIEnvExt* env = self->GetJniEnv(); ScopedLocalRef<jstring> scoped_name_string(env, (jstring)env->GetObjectField(peer, WellKnownClasses::java_lang_Thread_name)); ScopedUtfChars scoped_name_chars(env, scoped_name_string.get()); if (scoped_name_chars.c_str() == NULL) { - LOG(level) << message << ": " << peer; + LOG(severity) << message << ": " << peer; env->ExceptionClear(); } else { - LOG(level) << message << ": " << peer << ":" << scoped_name_chars.c_str(); + LOG(severity) << message << ": " << peer << ":" << scoped_name_chars.c_str(); } } @@ -561,8 +565,9 @@ Thread* ThreadList::SuspendThreadByPeer(jobject peer, bool request_suspension, } } -static void ThreadSuspendByThreadIdWarning(int level, const char* message, uint32_t thread_id) { - LOG(level) << StringPrintf("%s: %d", message, thread_id); +static void ThreadSuspendByThreadIdWarning(LogSeverity severity, const char* message, + uint32_t thread_id) { + LOG(severity) << StringPrintf("%s: %d", message, thread_id); } Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspension, @@ -927,6 +932,9 @@ void ThreadList::Unregister(Thread* self) { // suspend and so on, must happen at this point, and not in ~Thread. self->Destroy(); + // If tracing, remember thread id and name before thread exits. + Trace::StoreExitingThreadInfo(self); + uint32_t thin_lock_id = self->GetThreadId(); while (self != nullptr) { // Remove and delete the Thread* while holding the thread_list_lock_ and diff --git a/runtime/trace.cc b/runtime/trace.cc index 027f62d880..b3158a48a6 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -18,6 +18,9 @@ #include <sys/uio.h> +#define ATRACE_TAG ATRACE_TAG_DALVIK +#include "cutils/trace.h" + #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" @@ -706,9 +709,21 @@ static void DumpThread(Thread* t, void* arg) { void Trace::DumpThreadList(std::ostream& os) { Thread* self = Thread::Current(); + for (auto it : exited_threads_) { + os << it.first << "\t" << it.second << "\n"; + } Locks::thread_list_lock_->AssertNotHeld(self); MutexLock mu(self, *Locks::thread_list_lock_); Runtime::Current()->GetThreadList()->ForEach(DumpThread, &os); } +void Trace::StoreExitingThreadInfo(Thread* thread) { + MutexLock mu(thread, *Locks::trace_lock_); + if (the_trace_ != nullptr) { + std::string name; + thread->GetThreadName(name); + the_trace_->exited_threads_.Put(thread->GetTid(), name); + } +} + } // namespace art diff --git a/runtime/trace.h b/runtime/trace.h index 45a02dab3c..ead1c29c72 100644 --- a/runtime/trace.h +++ b/runtime/trace.h @@ -104,6 +104,8 @@ class Trace FINAL : public instrumentation::InstrumentationListener { static std::vector<mirror::ArtMethod*>* AllocStackTrace(); // Clear and store an old stack trace for later use. static void FreeStackTrace(std::vector<mirror::ArtMethod*>* stack_trace); + // Save id and name of a thread before it exits. + static void StoreExitingThreadInfo(Thread* thread); private: explicit Trace(File* trace_file, int buffer_size, int flags, bool sampling_enabled); @@ -166,6 +168,9 @@ class Trace FINAL : public instrumentation::InstrumentationListener { // Did we overflow the buffer recording traces? bool overflow_; + // Map of thread ids and names that have already exited. + SafeMap<pid_t, std::string> exited_threads_; + DISALLOW_COPY_AND_ASSIGN(Trace); }; diff --git a/runtime/utils.cc b/runtime/utils.cc index 0688c1a784..9c94f6cb01 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -963,7 +963,7 @@ bool IsValidDescriptor(const char* s) { return IsValidClassName<kDescriptor, '/'>(s); } -void Split(const std::string& s, char separator, std::vector<std::string>& result) { +void Split(const std::string& s, char separator, std::vector<std::string>* result) { const char* p = s.data(); const char* end = p + s.size(); while (p != end) { @@ -974,12 +974,12 @@ void Split(const std::string& s, char separator, std::vector<std::string>& resul while (++p != end && *p != separator) { // Skip to the next occurrence of the separator. } - result.push_back(std::string(start, p - start)); + result->push_back(std::string(start, p - start)); } } } -std::string Trim(std::string s) { +std::string Trim(const std::string& s) { std::string result; unsigned int start_index = 0; unsigned int end_index = s.size() - 1; @@ -1009,7 +1009,7 @@ std::string Trim(std::string s) { } template <typename StringT> -std::string Join(std::vector<StringT>& strings, char separator) { +std::string Join(const std::vector<StringT>& strings, char separator) { if (strings.empty()) { return ""; } @@ -1023,9 +1023,8 @@ std::string Join(std::vector<StringT>& strings, char separator) { } // Explicit instantiations. -template std::string Join<std::string>(std::vector<std::string>& strings, char separator); -template std::string Join<const char*>(std::vector<const char*>& strings, char separator); -template std::string Join<char*>(std::vector<char*>& strings, char separator); +template std::string Join<std::string>(const std::vector<std::string>& strings, char separator); +template std::string Join<const char*>(const std::vector<const char*>& strings, char separator); bool StartsWith(const std::string& s, const char* prefix) { return s.compare(0, strlen(prefix), prefix) == 0; @@ -1087,7 +1086,7 @@ void GetTaskStats(pid_t tid, char* state, int* utime, int* stime, int* task_cpu) stats = stats.substr(stats.find(')') + 2); // Extract the three fields we care about. std::vector<std::string> fields; - Split(stats, ' ', fields); + Split(stats, ' ', &fields); *state = fields[0][0]; *utime = strtoull(fields[11].c_str(), NULL, 10); *stime = strtoull(fields[12].c_str(), NULL, 10); @@ -1104,12 +1103,12 @@ std::string GetSchedulerGroupName(pid_t tid) { return ""; } std::vector<std::string> cgroup_lines; - Split(cgroup_file, '\n', cgroup_lines); + Split(cgroup_file, '\n', &cgroup_lines); for (size_t i = 0; i < cgroup_lines.size(); ++i) { std::vector<std::string> cgroup_fields; - Split(cgroup_lines[i], ':', cgroup_fields); + Split(cgroup_lines[i], ':', &cgroup_fields); std::vector<std::string> cgroups; - Split(cgroup_fields[1], ',', cgroups); + Split(cgroup_fields[1], ',', &cgroups); for (size_t i = 0; i < cgroups.size(); ++i) { if (cgroups[i] == "cpu") { return cgroup_fields[2].substr(1); // Skip the leading slash. @@ -1154,7 +1153,7 @@ void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix, } } else if (current_method != nullptr && Locks::mutator_lock_->IsSharedHeld(Thread::Current()) && - current_method->IsWithinQuickCode(it->pc)) { + current_method->PcIsWithinQuickCode(it->pc)) { const void* start_of_code = current_method->GetEntryPointFromQuickCompiledCode(); os << JniLongName(current_method) << "+" << (it->pc - reinterpret_cast<uintptr_t>(start_of_code)); @@ -1189,7 +1188,7 @@ void DumpKernelStack(std::ostream& os, pid_t tid, const char* prefix, bool inclu } std::vector<std::string> kernel_stack_frames; - Split(kernel_stack, '\n', kernel_stack_frames); + Split(kernel_stack, '\n', &kernel_stack_frames); // We skip the last stack frame because it's always equivalent to "[<ffffffff>] 0xffffffff", // which looking at the source appears to be the kernel's way of saying "that's all, folks!". kernel_stack_frames.pop_back(); diff --git a/runtime/utils.h b/runtime/utils.h index 53b49c8fd4..b7daa64d90 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -380,13 +380,13 @@ void InitTimeSpec(bool absolute, int clock, int64_t ms, int32_t ns, timespec* ts // Splits a string using the given separator character into a vector of // strings. Empty strings will be omitted. -void Split(const std::string& s, char separator, std::vector<std::string>& result); +void Split(const std::string& s, char separator, std::vector<std::string>* result); // Trims whitespace off both ends of the given string. -std::string Trim(std::string s); +std::string Trim(const std::string& s); // Joins a vector of strings into a single string, using the given separator. -template <typename StringT> std::string Join(std::vector<StringT>& strings, char separator); +template <typename StringT> std::string Join(const std::vector<StringT>& strings, char separator); // Returns the calling thread's tid. (The C libraries don't expose this.) pid_t GetTid(); diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc index 1b2c3eec05..92323da554 100644 --- a/runtime/utils_test.cc +++ b/runtime/utils_test.cc @@ -241,62 +241,62 @@ TEST_F(UtilsTest, Split) { expected.clear(); actual.clear(); - Split("", ':', actual); + Split("", ':', &actual); EXPECT_EQ(expected, actual); actual.clear(); - Split(":", ':', actual); + Split(":", ':', &actual); EXPECT_EQ(expected, actual); expected.clear(); expected.push_back("foo"); actual.clear(); - Split(":foo", ':', actual); + Split(":foo", ':', &actual); EXPECT_EQ(expected, actual); actual.clear(); - Split("foo:", ':', actual); + Split("foo:", ':', &actual); EXPECT_EQ(expected, actual); actual.clear(); - Split(":foo:", ':', actual); + Split(":foo:", ':', &actual); EXPECT_EQ(expected, actual); expected.push_back("bar"); actual.clear(); - Split("foo:bar", ':', actual); + Split("foo:bar", ':', &actual); EXPECT_EQ(expected, actual); actual.clear(); - Split(":foo:bar", ':', actual); + Split(":foo:bar", ':', &actual); EXPECT_EQ(expected, actual); actual.clear(); - Split("foo:bar:", ':', actual); + Split("foo:bar:", ':', &actual); EXPECT_EQ(expected, actual); actual.clear(); - Split(":foo:bar:", ':', actual); + Split(":foo:bar:", ':', &actual); EXPECT_EQ(expected, actual); expected.push_back("baz"); actual.clear(); - Split("foo:bar:baz", ':', actual); + Split("foo:bar:baz", ':', &actual); EXPECT_EQ(expected, actual); actual.clear(); - Split(":foo:bar:baz", ':', actual); + Split(":foo:bar:baz", ':', &actual); EXPECT_EQ(expected, actual); actual.clear(); - Split("foo:bar:baz:", ':', actual); + Split("foo:bar:baz:", ':', &actual); EXPECT_EQ(expected, actual); actual.clear(); - Split(":foo:bar:baz:", ':', actual); + Split(":foo:bar:baz:", ':', &actual); EXPECT_EQ(expected, actual); } diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 4a3c3ec549..16338c476d 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -18,6 +18,8 @@ #include <stdlib.h> +#include <sstream> + #include "base/logging.h" #include "mirror/class.h" #include "ScopedLocalRef.h" @@ -52,6 +54,7 @@ jclass WellKnownClasses::java_lang_Thread__UncaughtExceptionHandler; jclass WellKnownClasses::java_lang_ThreadGroup; jclass WellKnownClasses::java_lang_Throwable; jclass WellKnownClasses::java_nio_DirectByteBuffer; +jclass WellKnownClasses::java_util_ArrayList; jclass WellKnownClasses::java_util_Collections; jclass WellKnownClasses::libcore_util_EmptyArray; jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk; @@ -95,8 +98,10 @@ jfieldID WellKnownClasses::java_lang_Thread_name; jfieldID WellKnownClasses::java_lang_Thread_priority; jfieldID WellKnownClasses::java_lang_Thread_uncaughtHandler; jfieldID WellKnownClasses::java_lang_Thread_nativePeer; +jfieldID WellKnownClasses::java_lang_ThreadGroup_groups; jfieldID WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup; jfieldID WellKnownClasses::java_lang_ThreadGroup_name; +jfieldID WellKnownClasses::java_lang_ThreadGroup_parent; jfieldID WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup; jfieldID WellKnownClasses::java_lang_Throwable_cause; jfieldID WellKnownClasses::java_lang_Throwable_detailMessage; @@ -108,6 +113,8 @@ jfieldID WellKnownClasses::java_lang_reflect_Field_artField; jfieldID WellKnownClasses::java_lang_reflect_Proxy_h; jfieldID WellKnownClasses::java_nio_DirectByteBuffer_capacity; jfieldID WellKnownClasses::java_nio_DirectByteBuffer_effectiveDirectAddress; +jfieldID WellKnownClasses::java_util_ArrayList_array; +jfieldID WellKnownClasses::java_util_ArrayList_size; jfieldID WellKnownClasses::java_util_Collections_EMPTY_LIST; jfieldID WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT; jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data; @@ -187,6 +194,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_ThreadGroup = CacheClass(env, "java/lang/ThreadGroup"); java_lang_Throwable = CacheClass(env, "java/lang/Throwable"); java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer"); + java_util_ArrayList = CacheClass(env, "java/util/ArrayList"); java_util_Collections = CacheClass(env, "java/util/Collections"); libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray"); org_apache_harmony_dalvik_ddmc_Chunk = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk"); @@ -225,8 +233,10 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Thread_priority = CacheField(env, java_lang_Thread, false, "priority", "I"); java_lang_Thread_uncaughtHandler = CacheField(env, java_lang_Thread, false, "uncaughtHandler", "Ljava/lang/Thread$UncaughtExceptionHandler;"); java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "J"); + java_lang_ThreadGroup_groups = CacheField(env, java_lang_ThreadGroup, false, "groups", "Ljava/util/List;"); java_lang_ThreadGroup_mainThreadGroup = CacheField(env, java_lang_ThreadGroup, true, "mainThreadGroup", "Ljava/lang/ThreadGroup;"); java_lang_ThreadGroup_name = CacheField(env, java_lang_ThreadGroup, false, "name", "Ljava/lang/String;"); + java_lang_ThreadGroup_parent = CacheField(env, java_lang_ThreadGroup, false, "parent", "Ljava/lang/ThreadGroup;"); java_lang_ThreadGroup_systemThreadGroup = CacheField(env, java_lang_ThreadGroup, true, "systemThreadGroup", "Ljava/lang/ThreadGroup;"); java_lang_Throwable_cause = CacheField(env, java_lang_Throwable, false, "cause", "Ljava/lang/Throwable;"); java_lang_Throwable_detailMessage = CacheField(env, java_lang_Throwable, false, "detailMessage", "Ljava/lang/String;"); @@ -238,6 +248,8 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_reflect_Proxy_h = CacheField(env, java_lang_reflect_Proxy, false, "h", "Ljava/lang/reflect/InvocationHandler;"); java_nio_DirectByteBuffer_capacity = CacheField(env, java_nio_DirectByteBuffer, false, "capacity", "I"); java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "effectiveDirectAddress", "J"); + java_util_ArrayList_array = CacheField(env, java_util_ArrayList, false, "array", "[Ljava/lang/Object;"); + java_util_ArrayList_size = CacheField(env, java_util_ArrayList, false, "size", "I"); java_util_Collections_EMPTY_LIST = CacheField(env, java_util_Collections, true, "EMPTY_LIST", "Ljava/util/List;"); libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(env, libcore_util_EmptyArray, true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;"); org_apache_harmony_dalvik_ddmc_Chunk_data = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "data", "[B"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 790d7f7ab6..d651b90c07 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -64,6 +64,7 @@ struct WellKnownClasses { static jclass java_lang_ThreadGroup; static jclass java_lang_Thread__UncaughtExceptionHandler; static jclass java_lang_Throwable; + static jclass java_util_ArrayList; static jclass java_util_Collections; static jclass java_nio_DirectByteBuffer; static jclass libcore_util_EmptyArray; @@ -111,8 +112,10 @@ struct WellKnownClasses { static jfieldID java_lang_Thread_priority; static jfieldID java_lang_Thread_uncaughtHandler; static jfieldID java_lang_Thread_nativePeer; + static jfieldID java_lang_ThreadGroup_groups; static jfieldID java_lang_ThreadGroup_mainThreadGroup; static jfieldID java_lang_ThreadGroup_name; + static jfieldID java_lang_ThreadGroup_parent; static jfieldID java_lang_ThreadGroup_systemThreadGroup; static jfieldID java_lang_Throwable_cause; static jfieldID java_lang_Throwable_detailMessage; @@ -121,6 +124,8 @@ struct WellKnownClasses { static jfieldID java_lang_Throwable_suppressedExceptions; static jfieldID java_nio_DirectByteBuffer_capacity; static jfieldID java_nio_DirectByteBuffer_effectiveDirectAddress; + static jfieldID java_util_ArrayList_array; + static jfieldID java_util_ArrayList_size; static jfieldID java_util_Collections_EMPTY_LIST; static jfieldID libcore_util_EmptyArray_STACK_TRACE_ELEMENT; static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_data; diff --git a/sigchainlib/Android.mk b/sigchainlib/Android.mk index d86735d120..b7ff3606fd 100644 --- a/sigchainlib/Android.mk +++ b/sigchainlib/Android.mk @@ -22,14 +22,26 @@ include $(CLEAR_VARS) LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) LOCAL_MODULE_TAGS := optional LOCAL_CFLAGS += $(ART_TARGET_CFLAGS) -LOCAL_SRC_FILES := sigchain.cc +LOCAL_SRC_FILES := sigchain_dummy.cc LOCAL_CLANG = $(ART_TARGET_CLANG) LOCAL_MODULE:= libsigchain -LOCAL_SHARED_LIBRARIES := liblog libdl +LOCAL_SHARED_LIBRARIES := liblog LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_build.mk include $(BUILD_SHARED_LIBRARY) +include $(CLEAR_VARS) +LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS += $(ART_TARGET_CFLAGS) +LOCAL_SRC_FILES := sigchain.cc +LOCAL_CLANG = $(ART_TARGET_CLANG) +LOCAL_MODULE:= libsigchain +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_build.mk +include $(BUILD_STATIC_LIBRARY) + # Build host library. include $(CLEAR_VARS) LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) @@ -37,9 +49,23 @@ LOCAL_MODULE_TAGS := optional LOCAL_IS_HOST_MODULE := true LOCAL_CFLAGS += $(ART_HOST_CFLAGS) LOCAL_CLANG = $(ART_HOST_CLANG) -LOCAL_SRC_FILES := sigchain.cc +LOCAL_SRC_FILES := sigchain_dummy.cc LOCAL_MODULE:= libsigchain LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk LOCAL_LDLIBS = -ldl LOCAL_MULTILIB := both include $(BUILD_HOST_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) +LOCAL_MODULE_TAGS := optional +LOCAL_IS_HOST_MODULE := true +LOCAL_CFLAGS += $(ART_HOST_CFLAGS) +LOCAL_CLANG = $(ART_HOST_CLANG) +LOCAL_SRC_FILES := sigchain.cc +LOCAL_MODULE:= libsigchain +LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk +LOCAL_LDLIBS = -ldl +LOCAL_MULTILIB := both +include external/libcxx/libcxx.mk +include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc index 74bfb7e9c6..601e3210a9 100644 --- a/sigchainlib/sigchain.cc +++ b/sigchainlib/sigchain.cc @@ -35,6 +35,8 @@ namespace art { +typedef int (*SigActionFnPtr)(int, const struct sigaction*, struct sigaction*); + class SignalAction { public: SignalAction() : claimed_(false), uses_old_style_(false) { @@ -107,21 +109,20 @@ static void CheckSignalValid(int signal) { } } - // Claim a signal chain for a particular signal. -void ClaimSignalChain(int signal, struct sigaction* oldaction) { +extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction) { CheckSignalValid(signal); user_sigactions[signal].Claim(*oldaction); } -void UnclaimSignalChain(int signal) { +extern "C" void UnclaimSignalChain(int signal) { CheckSignalValid(signal); user_sigactions[signal].Unclaim(signal); } // Invoke the user's signal handler. -void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) { +extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) { // Check the arguments. CheckSignalValid(sig); @@ -148,10 +149,22 @@ void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) { } } -extern "C" { -// These functions are C linkage since they replace the functions in libc. +extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action) { + CheckSignalValid(signal); + // Read the current action without looking at the chain, it should be the expected action. + SigActionFnPtr linked_sigaction = reinterpret_cast<SigActionFnPtr>(linked_sigaction_sym); + struct sigaction current_action; + linked_sigaction(signal, nullptr, ¤t_action); + // If the sigactions don't match then we put the current action on the chain and make ourself as + // the main action. + if (current_action.sa_sigaction != expected_action->sa_sigaction) { + log("Warning: Unexpected sigaction action found %p\n", current_action.sa_sigaction); + user_sigactions[signal].Claim(current_action); + linked_sigaction(signal, expected_action, nullptr); + } +} -int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) { +extern "C" int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) { // If this signal has been claimed as a signal chain, record the user's // action but don't pass it on to the kernel. // Note that we check that the signal number is in range here. An out of range signal @@ -181,13 +194,11 @@ int sigaction(int signal, const struct sigaction* new_action, struct sigaction* log("Unable to find next sigaction in signal chain"); abort(); } - - typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*); - SigAction linked_sigaction = reinterpret_cast<SigAction>(linked_sigaction_sym); + SigActionFnPtr linked_sigaction = reinterpret_cast<SigActionFnPtr>(linked_sigaction_sym); return linked_sigaction(signal, new_action, old_action); } -sighandler_t signal(int signal, sighandler_t handler) { +extern "C" sighandler_t signal(int signal, sighandler_t handler) { struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_handler = handler; @@ -226,7 +237,7 @@ sighandler_t signal(int signal, sighandler_t handler) { return reinterpret_cast<sighandler_t>(sa.sa_handler); } -int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) { +extern "C" int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) { const sigset_t* new_set_ptr = bionic_new_set; sigset_t tmpset; if (bionic_new_set != NULL) { @@ -258,9 +269,8 @@ int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_se SigProcMask linked_sigprocmask= reinterpret_cast<SigProcMask>(linked_sigprocmask_sym); return linked_sigprocmask(how, new_set_ptr, bionic_old_set); } -} // extern "C" -void InitializeSignalChain() { +extern "C" void InitializeSignalChain() { // Warning. // Don't call this from within a signal context as it makes calls to // dlsym. Calling into the dynamic linker will result in locks being @@ -290,5 +300,6 @@ void InitializeSignalChain() { } initialized = true; } + } // namespace art diff --git a/sigchainlib/sigchain.h b/sigchainlib/sigchain.h index 5bc4026850..79b76a74da 100644 --- a/sigchainlib/sigchain.h +++ b/sigchainlib/sigchain.h @@ -21,14 +21,16 @@ namespace art { -void InitializeSignalChain(); +extern "C" void InitializeSignalChain(); -void ClaimSignalChain(int signal, struct sigaction* oldaction); +extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction); -void UnclaimSignalChain(int signal); +extern "C" void UnclaimSignalChain(int signal); -void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context); +extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context); -} // namespace art +extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action); + +} // namespace art #endif // ART_SIGCHAINLIB_SIGCHAIN_H_ diff --git a/sigchainlib/sigchain_dummy.cc b/sigchainlib/sigchain_dummy.cc new file mode 100644 index 0000000000..17bfe8f729 --- /dev/null +++ b/sigchainlib/sigchain_dummy.cc @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> + +#ifdef HAVE_ANDROID_OS +#include <android/log.h> +#else +#include <stdarg.h> +#include <iostream> +#endif + +#include "sigchain.h" + +static void log(const char* format, ...) { + char buf[256]; + va_list ap; + va_start(ap, format); + vsnprintf(buf, sizeof(buf), format, ap); +#ifdef HAVE_ANDROID_OS + __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf); +#else + std::cout << buf << "\n"; +#endif + va_end(ap); +} + +extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction) { + log("ClaimSignalChain is not exported by the main executable."); + abort(); +} + +extern "C" void UnclaimSignalChain(int signal) { + log("UnclaimSignalChain is not exported by the main executable."); + abort(); +} + +extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) { + log("InvokeUserSignalHandler is not exported by the main executable."); + abort(); +} + +extern "C" void InitializeSignalChain() { + log("InitializeSignalChain is not exported by the main executable."); + abort(); +} + +extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action) { + log("EnsureFrontOfChain is not exported by the main executable."); + abort(); +} diff --git a/sigchainlib/version-script.txt b/sigchainlib/version-script.txt new file mode 100644 index 0000000000..ce1505490b --- /dev/null +++ b/sigchainlib/version-script.txt @@ -0,0 +1,13 @@ +{ +global: + ClaimSignalChain; + UnclaimSignalChain; + InvokeUserSignalHandler; + InitializeSignalChain; + EnsureFrontOfChain; + sigaction; + signal; + sigprocmask; +local: + *; +}; diff --git a/test/004-ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc index e914bd9d09..291b45f358 100644 --- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc +++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc @@ -22,7 +22,7 @@ namespace art { #define CHECK_REGS_CONTAIN_REFS(native_pc_offset, ...) do { \ int t[] = {__VA_ARGS__}; \ int t_size = sizeof(t) / sizeof(*t); \ - CheckReferences(t, t_size, m->NativePcOffset(m->ToNativePc(native_pc_offset))); \ + CheckReferences(t, t_size, m->NativeQuickPcOffset(m->ToNativeQuickPc(native_pc_offset))); \ } while (false); struct ReferenceMap2Visitor : public CheckReferenceMapVisitor { diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java index 8c8ad163c5..743d62ca69 100644 --- a/test/004-UnsafeTest/src/Main.java +++ b/test/004-UnsafeTest/src/Main.java @@ -25,14 +25,14 @@ public class Main { private static void check(int actual, int expected, String msg) { if (actual != expected) { System.out.println(msg + " : " + actual + " != " + expected); - System.exit(-1); + System.exit(1); } } private static void check(long actual, long expected, String msg) { if (actual != expected) { System.out.println(msg + " : " + actual + " != " + expected); - System.exit(-1); + System.exit(1); } } diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java index 390472df4f..e3cf4eedbc 100644 --- a/test/036-finalizer/src/Main.java +++ b/test/036-finalizer/src/Main.java @@ -125,34 +125,55 @@ public class Main { } static class FinalizeCounter { + public static final int maxCount = 1024; + public static boolean finalized[] = new boolean[maxCount]; private static Object finalizeLock = new Object(); private static volatile int finalizeCount = 0; private int index; static int getCount() { return finalizeCount; } + static void printNonFinalized() { + for (int i = 0; i < maxCount; ++i) { + if (!FinalizeCounter.finalized[i]) { + System.err.println("Element " + i + " was not finalized"); + } + } + } FinalizeCounter(int index) { this.index = index; } protected void finalize() { synchronized(finalizeLock) { ++finalizeCount; + finalized[index] = true; } } } - private static void runFinalizationTest() { - int count = 1024; + private static void allocFinalizableObjects(int count) { Object[] objs = new Object[count]; for (int i = 0; i < count; ++i) { objs[i] = new FinalizeCounter(i); } - for (int i = 0; i < count; ++i) { - objs[i] = null; - } - System.gc(); + } + + private static void runFinalizationTest() { + allocFinalizableObjects(FinalizeCounter.maxCount); + Runtime.getRuntime().gc(); System.runFinalization(); - System.out.println("Finalized " + FinalizeCounter.getCount() + " / " + count); + System.out.println("Finalized " + FinalizeCounter.getCount() + " / " + FinalizeCounter.maxCount); + if (FinalizeCounter.getCount() != FinalizeCounter.maxCount) { + // Print out all the finalized elements. + FinalizeCounter.printNonFinalized(); + // Try to sleep for a couple seconds to see if the objects became finalized after. + try { + java.lang.Thread.sleep(2000); + } catch (InterruptedException e) { + } + System.out.println("After sleep finalized " + FinalizeCounter.getCount() + " / " + FinalizeCounter.maxCount); + FinalizeCounter.printNonFinalized(); + } } public static class FinalizerTest { diff --git a/test/074-gc-thrash/src/Main.java b/test/074-gc-thrash/src/Main.java index 78413f3676..32fbf2d103 100644 --- a/test/074-gc-thrash/src/Main.java +++ b/test/074-gc-thrash/src/Main.java @@ -183,7 +183,11 @@ class Robin extends Thread { } private String makeString(int val) { - return new String("Robin" + val); + try { + return new String("Robin" + val); + } catch (OutOfMemoryError e) { + return null; + } } } @@ -251,58 +255,63 @@ class Deep extends Thread { * valid and invalid references on the stack. */ private String dive(int depth, int iteration) { - String str0; - String str1; - String str2; - String str3; - String str4; - String str5; - String str6; - String str7; - String funStr; - - funStr = ""; - - switch (iteration % 8) { - case 0: - funStr = str0 = makeString(iteration); - break; - case 1: - funStr = str1 = makeString(iteration); - break; - case 2: - funStr = str2 = makeString(iteration); - break; - case 3: - funStr = str3 = makeString(iteration); - break; - case 4: - funStr = str4 = makeString(iteration); - break; - case 5: - funStr = str5 = makeString(iteration); - break; - case 6: - funStr = str6 = makeString(iteration); - break; - case 7: - funStr = str7 = makeString(iteration); - break; - } - - strong[depth] = funStr; - weak[depth] = new WeakReference(funStr); - - if (depth+1 < MAX_DEPTH) - dive(depth+1, iteration+1); - else - Main.sleep(100); + try { + String str0; + String str1; + String str2; + String str3; + String str4; + String str5; + String str6; + String str7; + String funStr = ""; + switch (iteration % 8) { + case 0: + funStr = str0 = makeString(iteration); + break; + case 1: + funStr = str1 = makeString(iteration); + break; + case 2: + funStr = str2 = makeString(iteration); + break; + case 3: + funStr = str3 = makeString(iteration); + break; + case 4: + funStr = str4 = makeString(iteration); + break; + case 5: + funStr = str5 = makeString(iteration); + break; + case 6: + funStr = str6 = makeString(iteration); + break; + case 7: + funStr = str7 = makeString(iteration); + break; + } - return funStr; + strong[depth] = funStr; + weak[depth] = new WeakReference(funStr); + if (depth+1 < MAX_DEPTH) + dive(depth+1, iteration+1); + else + Main.sleep(100); + return funStr; + } catch (OutOfMemoryError e) { + // Silently ignore OOME since gc stress mode causes them to occur but shouldn't be a + // test failure. + } + return ""; } private String makeString(int val) { - return new String("Deep" + val); + try { + return new String("Deep" + val); + } catch (OutOfMemoryError e) { + return null; + } } } @@ -319,13 +328,16 @@ class Large extends Thread { Main.startupDelay(); while (!Main.quit) { - chunk = new byte[100000]; - pretendToUse(chunk); - - count++; - if ((count % 500) == 0) { - Main.sleep(400); - sleepCount++; + try { + chunk = new byte[100000]; + pretendToUse(chunk); + + count++; + if ((count % 500) == 0) { + Main.sleep(400); + sleepCount++; + } + } catch (OutOfMemoryError e) { } } diff --git a/test/083-compiler-regressions/expected.txt b/test/083-compiler-regressions/expected.txt index e907fd1d58..c43d1f78cb 100644 --- a/test/083-compiler-regressions/expected.txt +++ b/test/083-compiler-regressions/expected.txt @@ -16,6 +16,7 @@ false b13679511Test finishing b16177324TestWrapper caught NPE as expected. b16230771TestWrapper caught NPE as expected. +b17969907TestWrapper caught NPE as expected. largeFrame passes largeFrameFloat passes mulBy1Test passes diff --git a/test/083-compiler-regressions/src/Main.java b/test/083-compiler-regressions/src/Main.java index 8d7bf01192..9c772b9fb0 100644 --- a/test/083-compiler-regressions/src/Main.java +++ b/test/083-compiler-regressions/src/Main.java @@ -38,6 +38,7 @@ public class Main { b13679511Test(); b16177324TestWrapper(); b16230771TestWrapper(); + b17969907TestWrapper(); largeFrameTest(); largeFrameTestFloat(); mulBy1Test(); @@ -977,6 +978,24 @@ public class Main { } } + static void b17969907TestWrapper() { + try { + b17969907Test(); + System.out.println("b17969907Test unexpectedly didn't throw NPE."); + } catch (NullPointerException expected) { + System.out.println("b17969907TestWrapper caught NPE as expected."); + } + } + + public static void b17969907Test() { + Integer i = new Integer(1); + int sum = 0; + while (sum < 100) { + sum += i; + i = null; + } + } + static double TooManyArgs( long l00, long l01, diff --git a/test/089-many-methods/check b/test/089-many-methods/check new file mode 100755 index 0000000000..594626a35a --- /dev/null +++ b/test/089-many-methods/check @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Strip build error debug messages, as they are environment-specific. +sed -e '/^Failed to build/d' -e '/^Non-canonical tmpdir/d' -e '/^Args:/d' "$2" > "$2.tmp" + +diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null
\ No newline at end of file diff --git a/test/115-native-bridge/run b/test/115-native-bridge/run index e475cd6b89..32a9975c4b 100644 --- a/test/115-native-bridge/run +++ b/test/115-native-bridge/run @@ -18,9 +18,9 @@ ARGS=${@} # Use libnativebridgetest as a native bridge, start NativeBridgeMain (Main is JniTest main file). LIBPATH=$(echo ${ARGS} | sed -r 's/.*Djava.library.path=([^ ]*) .*/\1/') -cp ${LIBPATH}/libnativebridgetest.so . +ln -s ${LIBPATH}/libnativebridgetest.so . touch libarttest.so -cp ${LIBPATH}/libarttest.so libarttest2.so +ln -s ${LIBPATH}/libarttest.so libarttest2.so # pwd likely has /, so it's a pain to put that into a sed rule. LEFT=$(echo ${ARGS} | sed -r 's/-Djava.library.path.*//') diff --git a/test/800-smali/build b/test/124-missing-classes/build index 1b5a4e3381..62e57c86da 100644 --- a/test/800-smali/build +++ b/test/124-missing-classes/build @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (C) 2014 The Android Open Source Project +# 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. @@ -17,16 +17,12 @@ # Stop if something fails. set -e -# Compile Java classes mkdir classes -${JAVAC} -d classes `find src -name '*.java'` -${DX} -JXmx256m --debug --dex --output=java_classes.dex classes - -# Compile Smali classes -${SMALI} -JXmx256m --output smali_classes.dex `find src -name '*.smali'` -# Combine files. -${DXMERGER} classes.dex java_classes.dex smali_classes.dex +# Some classes are available at compile time... +${JAVAC} -d classes `find src -name '*.java'` -# Zip up output. -zip $TEST_NAME.jar classes.dex +# ...but not at run time. +rm 'classes/MissingClass.class' +rm 'classes/Main$MissingInnerClass.class' +${DX} -JXmx256m --debug --dex --output=$TEST_NAME.jar classes diff --git a/test/124-missing-classes/expected.txt b/test/124-missing-classes/expected.txt new file mode 100644 index 0000000000..ce761c3761 --- /dev/null +++ b/test/124-missing-classes/expected.txt @@ -0,0 +1,6 @@ +Test Started +testMissingFieldType caught NoClassDefFoundError +testMissingMethodReturnType caught NoClassDefFoundError +testMissingMethodParameterType caught NoClassDefFoundError +testMissingInnerClass caught NoClassDefFoundError +Test Finished diff --git a/test/124-missing-classes/info.txt b/test/124-missing-classes/info.txt new file mode 100644 index 0000000000..a734f990d5 --- /dev/null +++ b/test/124-missing-classes/info.txt @@ -0,0 +1 @@ +Tests the effects of missing classes. diff --git a/test/124-missing-classes/src/Main.java b/test/124-missing-classes/src/Main.java new file mode 100644 index 0000000000..1667d2d55b --- /dev/null +++ b/test/124-missing-classes/src/Main.java @@ -0,0 +1,80 @@ +/* + * 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. + */ + +public final class Main { + + public static void main(String[] args) throws Exception { + System.out.println("Test Started"); + testMissingFieldType(); + testMissingMethodReturnType(); + testMissingMethodParameterType(); + testMissingInnerClass(); + System.out.println("Test Finished"); + } + + private static class ClassWithMissingFieldType { + MissingClass field; + } + + private static void testMissingFieldType() throws Exception { + try { + ClassWithMissingFieldType.class.getDeclaredFields(); + throw new AssertionError(); + } catch (NoClassDefFoundError e) { + System.out.println("testMissingFieldType caught NoClassDefFoundError"); + } + } + + private static class ClassWithMissingMethodReturnType { + MissingClass method() { + return null; + } + } + + private static void testMissingMethodReturnType() throws Exception { + try { + ClassWithMissingMethodReturnType.class.getDeclaredMethods(); + throw new AssertionError(); + } catch (NoClassDefFoundError e) { + System.out.println("testMissingMethodReturnType caught NoClassDefFoundError"); + } + } + + private static class ClassWithMissingMethodParameterType { + void method(MissingClass arg) {} + } + + private static void testMissingMethodParameterType() throws Exception { + try { + ClassWithMissingMethodParameterType.class.getDeclaredMethods(); + throw new AssertionError(); + } catch (NoClassDefFoundError e) { + System.out.println("testMissingMethodParameterType caught NoClassDefFoundError"); + } + } + + private static final class MissingInnerClass { + } + + private static void testMissingInnerClass() throws Exception { + try { + Main.class.getDeclaredClasses(); + throw new AssertionError(); + } catch (NoClassDefFoundError e) { + System.out.println("testMissingInnerClass caught NoClassDefFoundError"); + } + } +} diff --git a/runtime/log_severity.h b/test/124-missing-classes/src/MissingClass.java index 31682dfeee..33aaa5625f 100644 --- a/runtime/log_severity.h +++ b/test/124-missing-classes/src/MissingClass.java @@ -14,12 +14,7 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_LOG_SEVERITY_H_ -#define ART_RUNTIME_LOG_SEVERITY_H_ - -typedef int LogSeverity; - -const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5; -const int INTERNAL_FATAL = 6; // For Runtime::Abort. - -#endif // ART_RUNTIME_LOG_SEVERITY_H_ +public final class MissingClass { + public static final class MissingInnerClass { + } +} diff --git a/test/125-gc-and-classloading/expected.txt b/test/125-gc-and-classloading/expected.txt new file mode 100644 index 0000000000..7ef22e9a43 --- /dev/null +++ b/test/125-gc-and-classloading/expected.txt @@ -0,0 +1 @@ +PASS diff --git a/test/125-gc-and-classloading/info.txt b/test/125-gc-and-classloading/info.txt new file mode 100644 index 0000000000..bb6bf123d2 --- /dev/null +++ b/test/125-gc-and-classloading/info.txt @@ -0,0 +1 @@ +Tests class loading and GC running in parallel. diff --git a/test/125-gc-and-classloading/src/Main.java b/test/125-gc-and-classloading/src/Main.java new file mode 100644 index 0000000000..61e123d329 --- /dev/null +++ b/test/125-gc-and-classloading/src/Main.java @@ -0,0 +1,3072 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.concurrent.CountDownLatch; + +public class Main { + + public static void main(String[] args) throws Exception { + // Try to cause a class loading/linking while GC is running. + CountDownLatch cdl = new CountDownLatch(1); + GcThread gcThread = new GcThread(cdl); + ClassLoadingThread classLoadingThread = new ClassLoadingThread(cdl); + gcThread.start(); + classLoadingThread.start(); + gcThread.join(); + classLoadingThread.join(); + System.out.println("PASS"); + } + + static class GcThread extends Thread { + CountDownLatch cdl; + + GcThread(CountDownLatch cdl) { + this.cdl = cdl; + } + + public void run() { + for (int i = 0; i < 10; ++i) { + Runtime.getRuntime().gc(); + if (i == 0) { + cdl.countDown(); + } + } + } + } + + static class ClassLoadingThread extends Thread { + CountDownLatch cdl; + + ClassLoadingThread(CountDownLatch cdl) { + this.cdl = cdl; + } + + public void run() { + try { + cdl.await(); + Class c0 = Class.forName("Main$BigClass"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + // A class with lots of fields so that the class loading/linking takes a long time. + // It is an abstract class to exercise the non-embedded imt/vtable case. + static abstract class BigClass { + static Object sf1; + static Object sf2; + static Object sf3; + static Object sf4; + static Object sf5; + static Object sf6; + static Object sf7; + static Object sf8; + static Object sf9; + static Object sf10; + static Object sf11; + static Object sf12; + static Object sf13; + static Object sf14; + static Object sf15; + static Object sf16; + static Object sf17; + static Object sf18; + static Object sf19; + static Object sf20; + static Object sf21; + static Object sf22; + static Object sf23; + static Object sf24; + static Object sf25; + static Object sf26; + static Object sf27; + static Object sf28; + static Object sf29; + static Object sf30; + static Object sf31; + static Object sf32; + static Object sf33; + static Object sf34; + static Object sf35; + static Object sf36; + static Object sf37; + static Object sf38; + static Object sf39; + static Object sf40; + static Object sf41; + static Object sf42; + static Object sf43; + static Object sf44; + static Object sf45; + static Object sf46; + static Object sf47; + static Object sf48; + static Object sf49; + static Object sf50; + static Object sf51; + static Object sf52; + static Object sf53; + static Object sf54; + static Object sf55; + static Object sf56; + static Object sf57; + static Object sf58; + static Object sf59; + static Object sf60; + static Object sf61; + static Object sf62; + static Object sf63; + static Object sf64; + static Object sf65; + static Object sf66; + static Object sf67; + static Object sf68; + static Object sf69; + static Object sf70; + static Object sf71; + static Object sf72; + static Object sf73; + static Object sf74; + static Object sf75; + static Object sf76; + static Object sf77; + static Object sf78; + static Object sf79; + static Object sf80; + static Object sf81; + static Object sf82; + static Object sf83; + static Object sf84; + static Object sf85; + static Object sf86; + static Object sf87; + static Object sf88; + static Object sf89; + static Object sf90; + static Object sf91; + static Object sf92; + static Object sf93; + static Object sf94; + static Object sf95; + static Object sf96; + static Object sf97; + static Object sf98; + static Object sf99; + static Object sf100; + static Object sf101; + static Object sf102; + static Object sf103; + static Object sf104; + static Object sf105; + static Object sf106; + static Object sf107; + static Object sf108; + static Object sf109; + static Object sf110; + static Object sf111; + static Object sf112; + static Object sf113; + static Object sf114; + static Object sf115; + static Object sf116; + static Object sf117; + static Object sf118; + static Object sf119; + static Object sf120; + static Object sf121; + static Object sf122; + static Object sf123; + static Object sf124; + static Object sf125; + static Object sf126; + static Object sf127; + static Object sf128; + static Object sf129; + static Object sf130; + static Object sf131; + static Object sf132; + static Object sf133; + static Object sf134; + static Object sf135; + static Object sf136; + static Object sf137; + static Object sf138; + static Object sf139; + static Object sf140; + static Object sf141; + static Object sf142; + static Object sf143; + static Object sf144; + static Object sf145; + static Object sf146; + static Object sf147; + static Object sf148; + static Object sf149; + static Object sf150; + static Object sf151; + static Object sf152; + static Object sf153; + static Object sf154; + static Object sf155; + static Object sf156; + static Object sf157; + static Object sf158; + static Object sf159; + static Object sf160; + static Object sf161; + static Object sf162; + static Object sf163; + static Object sf164; + static Object sf165; + static Object sf166; + static Object sf167; + static Object sf168; + static Object sf169; + static Object sf170; + static Object sf171; + static Object sf172; + static Object sf173; + static Object sf174; + static Object sf175; + static Object sf176; + static Object sf177; + static Object sf178; + static Object sf179; + static Object sf180; + static Object sf181; + static Object sf182; + static Object sf183; + static Object sf184; + static Object sf185; + static Object sf186; + static Object sf187; + static Object sf188; + static Object sf189; + static Object sf190; + static Object sf191; + static Object sf192; + static Object sf193; + static Object sf194; + static Object sf195; + static Object sf196; + static Object sf197; + static Object sf198; + static Object sf199; + static Object sf200; + static Object sf201; + static Object sf202; + static Object sf203; + static Object sf204; + static Object sf205; + static Object sf206; + static Object sf207; + static Object sf208; + static Object sf209; + static Object sf210; + static Object sf211; + static Object sf212; + static Object sf213; + static Object sf214; + static Object sf215; + static Object sf216; + static Object sf217; + static Object sf218; + static Object sf219; + static Object sf220; + static Object sf221; + static Object sf222; + static Object sf223; + static Object sf224; + static Object sf225; + static Object sf226; + static Object sf227; + static Object sf228; + static Object sf229; + static Object sf230; + static Object sf231; + static Object sf232; + static Object sf233; + static Object sf234; + static Object sf235; + static Object sf236; + static Object sf237; + static Object sf238; + static Object sf239; + static Object sf240; + static Object sf241; + static Object sf242; + static Object sf243; + static Object sf244; + static Object sf245; + static Object sf246; + static Object sf247; + static Object sf248; + static Object sf249; + static Object sf250; + static Object sf251; + static Object sf252; + static Object sf253; + static Object sf254; + static Object sf255; + static Object sf256; + static Object sf257; + static Object sf258; + static Object sf259; + static Object sf260; + static Object sf261; + static Object sf262; + static Object sf263; + static Object sf264; + static Object sf265; + static Object sf266; + static Object sf267; + static Object sf268; + static Object sf269; + static Object sf270; + static Object sf271; + static Object sf272; + static Object sf273; + static Object sf274; + static Object sf275; + static Object sf276; + static Object sf277; + static Object sf278; + static Object sf279; + static Object sf280; + static Object sf281; + static Object sf282; + static Object sf283; + static Object sf284; + static Object sf285; + static Object sf286; + static Object sf287; + static Object sf288; + static Object sf289; + static Object sf290; + static Object sf291; + static Object sf292; + static Object sf293; + static Object sf294; + static Object sf295; + static Object sf296; + static Object sf297; + static Object sf298; + static Object sf299; + static Object sf300; + static Object sf301; + static Object sf302; + static Object sf303; + static Object sf304; + static Object sf305; + static Object sf306; + static Object sf307; + static Object sf308; + static Object sf309; + static Object sf310; + static Object sf311; + static Object sf312; + static Object sf313; + static Object sf314; + static Object sf315; + static Object sf316; + static Object sf317; + static Object sf318; + static Object sf319; + static Object sf320; + static Object sf321; + static Object sf322; + static Object sf323; + static Object sf324; + static Object sf325; + static Object sf326; + static Object sf327; + static Object sf328; + static Object sf329; + static Object sf330; + static Object sf331; + static Object sf332; + static Object sf333; + static Object sf334; + static Object sf335; + static Object sf336; + static Object sf337; + static Object sf338; + static Object sf339; + static Object sf340; + static Object sf341; + static Object sf342; + static Object sf343; + static Object sf344; + static Object sf345; + static Object sf346; + static Object sf347; + static Object sf348; + static Object sf349; + static Object sf350; + static Object sf351; + static Object sf352; + static Object sf353; + static Object sf354; + static Object sf355; + static Object sf356; + static Object sf357; + static Object sf358; + static Object sf359; + static Object sf360; + static Object sf361; + static Object sf362; + static Object sf363; + static Object sf364; + static Object sf365; + static Object sf366; + static Object sf367; + static Object sf368; + static Object sf369; + static Object sf370; + static Object sf371; + static Object sf372; + static Object sf373; + static Object sf374; + static Object sf375; + static Object sf376; + static Object sf377; + static Object sf378; + static Object sf379; + static Object sf380; + static Object sf381; + static Object sf382; + static Object sf383; + static Object sf384; + static Object sf385; + static Object sf386; + static Object sf387; + static Object sf388; + static Object sf389; + static Object sf390; + static Object sf391; + static Object sf392; + static Object sf393; + static Object sf394; + static Object sf395; + static Object sf396; + static Object sf397; + static Object sf398; + static Object sf399; + static Object sf400; + static Object sf401; + static Object sf402; + static Object sf403; + static Object sf404; + static Object sf405; + static Object sf406; + static Object sf407; + static Object sf408; + static Object sf409; + static Object sf410; + static Object sf411; + static Object sf412; + static Object sf413; + static Object sf414; + static Object sf415; + static Object sf416; + static Object sf417; + static Object sf418; + static Object sf419; + static Object sf420; + static Object sf421; + static Object sf422; + static Object sf423; + static Object sf424; + static Object sf425; + static Object sf426; + static Object sf427; + static Object sf428; + static Object sf429; + static Object sf430; + static Object sf431; + static Object sf432; + static Object sf433; + static Object sf434; + static Object sf435; + static Object sf436; + static Object sf437; + static Object sf438; + static Object sf439; + static Object sf440; + static Object sf441; + static Object sf442; + static Object sf443; + static Object sf444; + static Object sf445; + static Object sf446; + static Object sf447; + static Object sf448; + static Object sf449; + static Object sf450; + static Object sf451; + static Object sf452; + static Object sf453; + static Object sf454; + static Object sf455; + static Object sf456; + static Object sf457; + static Object sf458; + static Object sf459; + static Object sf460; + static Object sf461; + static Object sf462; + static Object sf463; + static Object sf464; + static Object sf465; + static Object sf466; + static Object sf467; + static Object sf468; + static Object sf469; + static Object sf470; + static Object sf471; + static Object sf472; + static Object sf473; + static Object sf474; + static Object sf475; + static Object sf476; + static Object sf477; + static Object sf478; + static Object sf479; + static Object sf480; + static Object sf481; + static Object sf482; + static Object sf483; + static Object sf484; + static Object sf485; + static Object sf486; + static Object sf487; + static Object sf488; + static Object sf489; + static Object sf490; + static Object sf491; + static Object sf492; + static Object sf493; + static Object sf494; + static Object sf495; + static Object sf496; + static Object sf497; + static Object sf498; + static Object sf499; + static Object sf500; + static Object sf501; + static Object sf502; + static Object sf503; + static Object sf504; + static Object sf505; + static Object sf506; + static Object sf507; + static Object sf508; + static Object sf509; + static Object sf510; + static Object sf511; + static Object sf512; + static Object sf513; + static Object sf514; + static Object sf515; + static Object sf516; + static Object sf517; + static Object sf518; + static Object sf519; + static Object sf520; + static Object sf521; + static Object sf522; + static Object sf523; + static Object sf524; + static Object sf525; + static Object sf526; + static Object sf527; + static Object sf528; + static Object sf529; + static Object sf530; + static Object sf531; + static Object sf532; + static Object sf533; + static Object sf534; + static Object sf535; + static Object sf536; + static Object sf537; + static Object sf538; + static Object sf539; + static Object sf540; + static Object sf541; + static Object sf542; + static Object sf543; + static Object sf544; + static Object sf545; + static Object sf546; + static Object sf547; + static Object sf548; + static Object sf549; + static Object sf550; + static Object sf551; + static Object sf552; + static Object sf553; + static Object sf554; + static Object sf555; + static Object sf556; + static Object sf557; + static Object sf558; + static Object sf559; + static Object sf560; + static Object sf561; + static Object sf562; + static Object sf563; + static Object sf564; + static Object sf565; + static Object sf566; + static Object sf567; + static Object sf568; + static Object sf569; + static Object sf570; + static Object sf571; + static Object sf572; + static Object sf573; + static Object sf574; + static Object sf575; + static Object sf576; + static Object sf577; + static Object sf578; + static Object sf579; + static Object sf580; + static Object sf581; + static Object sf582; + static Object sf583; + static Object sf584; + static Object sf585; + static Object sf586; + static Object sf587; + static Object sf588; + static Object sf589; + static Object sf590; + static Object sf591; + static Object sf592; + static Object sf593; + static Object sf594; + static Object sf595; + static Object sf596; + static Object sf597; + static Object sf598; + static Object sf599; + static Object sf600; + static Object sf601; + static Object sf602; + static Object sf603; + static Object sf604; + static Object sf605; + static Object sf606; + static Object sf607; + static Object sf608; + static Object sf609; + static Object sf610; + static Object sf611; + static Object sf612; + static Object sf613; + static Object sf614; + static Object sf615; + static Object sf616; + static Object sf617; + static Object sf618; + static Object sf619; + static Object sf620; + static Object sf621; + static Object sf622; + static Object sf623; + static Object sf624; + static Object sf625; + static Object sf626; + static Object sf627; + static Object sf628; + static Object sf629; + static Object sf630; + static Object sf631; + static Object sf632; + static Object sf633; + static Object sf634; + static Object sf635; + static Object sf636; + static Object sf637; + static Object sf638; + static Object sf639; + static Object sf640; + static Object sf641; + static Object sf642; + static Object sf643; + static Object sf644; + static Object sf645; + static Object sf646; + static Object sf647; + static Object sf648; + static Object sf649; + static Object sf650; + static Object sf651; + static Object sf652; + static Object sf653; + static Object sf654; + static Object sf655; + static Object sf656; + static Object sf657; + static Object sf658; + static Object sf659; + static Object sf660; + static Object sf661; + static Object sf662; + static Object sf663; + static Object sf664; + static Object sf665; + static Object sf666; + static Object sf667; + static Object sf668; + static Object sf669; + static Object sf670; + static Object sf671; + static Object sf672; + static Object sf673; + static Object sf674; + static Object sf675; + static Object sf676; + static Object sf677; + static Object sf678; + static Object sf679; + static Object sf680; + static Object sf681; + static Object sf682; + static Object sf683; + static Object sf684; + static Object sf685; + static Object sf686; + static Object sf687; + static Object sf688; + static Object sf689; + static Object sf690; + static Object sf691; + static Object sf692; + static Object sf693; + static Object sf694; + static Object sf695; + static Object sf696; + static Object sf697; + static Object sf698; + static Object sf699; + static Object sf700; + static Object sf701; + static Object sf702; + static Object sf703; + static Object sf704; + static Object sf705; + static Object sf706; + static Object sf707; + static Object sf708; + static Object sf709; + static Object sf710; + static Object sf711; + static Object sf712; + static Object sf713; + static Object sf714; + static Object sf715; + static Object sf716; + static Object sf717; + static Object sf718; + static Object sf719; + static Object sf720; + static Object sf721; + static Object sf722; + static Object sf723; + static Object sf724; + static Object sf725; + static Object sf726; + static Object sf727; + static Object sf728; + static Object sf729; + static Object sf730; + static Object sf731; + static Object sf732; + static Object sf733; + static Object sf734; + static Object sf735; + static Object sf736; + static Object sf737; + static Object sf738; + static Object sf739; + static Object sf740; + static Object sf741; + static Object sf742; + static Object sf743; + static Object sf744; + static Object sf745; + static Object sf746; + static Object sf747; + static Object sf748; + static Object sf749; + static Object sf750; + static Object sf751; + static Object sf752; + static Object sf753; + static Object sf754; + static Object sf755; + static Object sf756; + static Object sf757; + static Object sf758; + static Object sf759; + static Object sf760; + static Object sf761; + static Object sf762; + static Object sf763; + static Object sf764; + static Object sf765; + static Object sf766; + static Object sf767; + static Object sf768; + static Object sf769; + static Object sf770; + static Object sf771; + static Object sf772; + static Object sf773; + static Object sf774; + static Object sf775; + static Object sf776; + static Object sf777; + static Object sf778; + static Object sf779; + static Object sf780; + static Object sf781; + static Object sf782; + static Object sf783; + static Object sf784; + static Object sf785; + static Object sf786; + static Object sf787; + static Object sf788; + static Object sf789; + static Object sf790; + static Object sf791; + static Object sf792; + static Object sf793; + static Object sf794; + static Object sf795; + static Object sf796; + static Object sf797; + static Object sf798; + static Object sf799; + static Object sf800; + static Object sf801; + static Object sf802; + static Object sf803; + static Object sf804; + static Object sf805; + static Object sf806; + static Object sf807; + static Object sf808; + static Object sf809; + static Object sf810; + static Object sf811; + static Object sf812; + static Object sf813; + static Object sf814; + static Object sf815; + static Object sf816; + static Object sf817; + static Object sf818; + static Object sf819; + static Object sf820; + static Object sf821; + static Object sf822; + static Object sf823; + static Object sf824; + static Object sf825; + static Object sf826; + static Object sf827; + static Object sf828; + static Object sf829; + static Object sf830; + static Object sf831; + static Object sf832; + static Object sf833; + static Object sf834; + static Object sf835; + static Object sf836; + static Object sf837; + static Object sf838; + static Object sf839; + static Object sf840; + static Object sf841; + static Object sf842; + static Object sf843; + static Object sf844; + static Object sf845; + static Object sf846; + static Object sf847; + static Object sf848; + static Object sf849; + static Object sf850; + static Object sf851; + static Object sf852; + static Object sf853; + static Object sf854; + static Object sf855; + static Object sf856; + static Object sf857; + static Object sf858; + static Object sf859; + static Object sf860; + static Object sf861; + static Object sf862; + static Object sf863; + static Object sf864; + static Object sf865; + static Object sf866; + static Object sf867; + static Object sf868; + static Object sf869; + static Object sf870; + static Object sf871; + static Object sf872; + static Object sf873; + static Object sf874; + static Object sf875; + static Object sf876; + static Object sf877; + static Object sf878; + static Object sf879; + static Object sf880; + static Object sf881; + static Object sf882; + static Object sf883; + static Object sf884; + static Object sf885; + static Object sf886; + static Object sf887; + static Object sf888; + static Object sf889; + static Object sf890; + static Object sf891; + static Object sf892; + static Object sf893; + static Object sf894; + static Object sf895; + static Object sf896; + static Object sf897; + static Object sf898; + static Object sf899; + static Object sf900; + static Object sf901; + static Object sf902; + static Object sf903; + static Object sf904; + static Object sf905; + static Object sf906; + static Object sf907; + static Object sf908; + static Object sf909; + static Object sf910; + static Object sf911; + static Object sf912; + static Object sf913; + static Object sf914; + static Object sf915; + static Object sf916; + static Object sf917; + static Object sf918; + static Object sf919; + static Object sf920; + static Object sf921; + static Object sf922; + static Object sf923; + static Object sf924; + static Object sf925; + static Object sf926; + static Object sf927; + static Object sf928; + static Object sf929; + static Object sf930; + static Object sf931; + static Object sf932; + static Object sf933; + static Object sf934; + static Object sf935; + static Object sf936; + static Object sf937; + static Object sf938; + static Object sf939; + static Object sf940; + static Object sf941; + static Object sf942; + static Object sf943; + static Object sf944; + static Object sf945; + static Object sf946; + static Object sf947; + static Object sf948; + static Object sf949; + static Object sf950; + static Object sf951; + static Object sf952; + static Object sf953; + static Object sf954; + static Object sf955; + static Object sf956; + static Object sf957; + static Object sf958; + static Object sf959; + static Object sf960; + static Object sf961; + static Object sf962; + static Object sf963; + static Object sf964; + static Object sf965; + static Object sf966; + static Object sf967; + static Object sf968; + static Object sf969; + static Object sf970; + static Object sf971; + static Object sf972; + static Object sf973; + static Object sf974; + static Object sf975; + static Object sf976; + static Object sf977; + static Object sf978; + static Object sf979; + static Object sf980; + static Object sf981; + static Object sf982; + static Object sf983; + static Object sf984; + static Object sf985; + static Object sf986; + static Object sf987; + static Object sf988; + static Object sf989; + static Object sf990; + static Object sf991; + static Object sf992; + static Object sf993; + static Object sf994; + static Object sf995; + static Object sf996; + static Object sf997; + static Object sf998; + static Object sf999; + static Object sf1000; + static Object sf1001; + static Object sf1002; + static Object sf1003; + static Object sf1004; + static Object sf1005; + static Object sf1006; + static Object sf1007; + static Object sf1008; + static Object sf1009; + static Object sf1010; + static Object sf1011; + static Object sf1012; + static Object sf1013; + static Object sf1014; + static Object sf1015; + static Object sf1016; + static Object sf1017; + static Object sf1018; + static Object sf1019; + static Object sf1020; + static Object sf1021; + static Object sf1022; + static Object sf1023; + static Object sf1024; + static Object sf1025; + static Object sf1026; + static Object sf1027; + static Object sf1028; + static Object sf1029; + static Object sf1030; + static Object sf1031; + static Object sf1032; + static Object sf1033; + static Object sf1034; + static Object sf1035; + static Object sf1036; + static Object sf1037; + static Object sf1038; + static Object sf1039; + static Object sf1040; + static Object sf1041; + static Object sf1042; + static Object sf1043; + static Object sf1044; + static Object sf1045; + static Object sf1046; + static Object sf1047; + static Object sf1048; + static Object sf1049; + static Object sf1050; + static Object sf1051; + static Object sf1052; + static Object sf1053; + static Object sf1054; + static Object sf1055; + static Object sf1056; + static Object sf1057; + static Object sf1058; + static Object sf1059; + static Object sf1060; + static Object sf1061; + static Object sf1062; + static Object sf1063; + static Object sf1064; + static Object sf1065; + static Object sf1066; + static Object sf1067; + static Object sf1068; + static Object sf1069; + static Object sf1070; + static Object sf1071; + static Object sf1072; + static Object sf1073; + static Object sf1074; + static Object sf1075; + static Object sf1076; + static Object sf1077; + static Object sf1078; + static Object sf1079; + static Object sf1080; + static Object sf1081; + static Object sf1082; + static Object sf1083; + static Object sf1084; + static Object sf1085; + static Object sf1086; + static Object sf1087; + static Object sf1088; + static Object sf1089; + static Object sf1090; + static Object sf1091; + static Object sf1092; + static Object sf1093; + static Object sf1094; + static Object sf1095; + static Object sf1096; + static Object sf1097; + static Object sf1098; + static Object sf1099; + static Object sf1100; + static Object sf1101; + static Object sf1102; + static Object sf1103; + static Object sf1104; + static Object sf1105; + static Object sf1106; + static Object sf1107; + static Object sf1108; + static Object sf1109; + static Object sf1110; + static Object sf1111; + static Object sf1112; + static Object sf1113; + static Object sf1114; + static Object sf1115; + static Object sf1116; + static Object sf1117; + static Object sf1118; + static Object sf1119; + static Object sf1120; + static Object sf1121; + static Object sf1122; + static Object sf1123; + static Object sf1124; + static Object sf1125; + static Object sf1126; + static Object sf1127; + static Object sf1128; + static Object sf1129; + static Object sf1130; + static Object sf1131; + static Object sf1132; + static Object sf1133; + static Object sf1134; + static Object sf1135; + static Object sf1136; + static Object sf1137; + static Object sf1138; + static Object sf1139; + static Object sf1140; + static Object sf1141; + static Object sf1142; + static Object sf1143; + static Object sf1144; + static Object sf1145; + static Object sf1146; + static Object sf1147; + static Object sf1148; + static Object sf1149; + static Object sf1150; + static Object sf1151; + static Object sf1152; + static Object sf1153; + static Object sf1154; + static Object sf1155; + static Object sf1156; + static Object sf1157; + static Object sf1158; + static Object sf1159; + static Object sf1160; + static Object sf1161; + static Object sf1162; + static Object sf1163; + static Object sf1164; + static Object sf1165; + static Object sf1166; + static Object sf1167; + static Object sf1168; + static Object sf1169; + static Object sf1170; + static Object sf1171; + static Object sf1172; + static Object sf1173; + static Object sf1174; + static Object sf1175; + static Object sf1176; + static Object sf1177; + static Object sf1178; + static Object sf1179; + static Object sf1180; + static Object sf1181; + static Object sf1182; + static Object sf1183; + static Object sf1184; + static Object sf1185; + static Object sf1186; + static Object sf1187; + static Object sf1188; + static Object sf1189; + static Object sf1190; + static Object sf1191; + static Object sf1192; + static Object sf1193; + static Object sf1194; + static Object sf1195; + static Object sf1196; + static Object sf1197; + static Object sf1198; + static Object sf1199; + static Object sf1200; + static Object sf1201; + static Object sf1202; + static Object sf1203; + static Object sf1204; + static Object sf1205; + static Object sf1206; + static Object sf1207; + static Object sf1208; + static Object sf1209; + static Object sf1210; + static Object sf1211; + static Object sf1212; + static Object sf1213; + static Object sf1214; + static Object sf1215; + static Object sf1216; + static Object sf1217; + static Object sf1218; + static Object sf1219; + static Object sf1220; + static Object sf1221; + static Object sf1222; + static Object sf1223; + static Object sf1224; + static Object sf1225; + static Object sf1226; + static Object sf1227; + static Object sf1228; + static Object sf1229; + static Object sf1230; + static Object sf1231; + static Object sf1232; + static Object sf1233; + static Object sf1234; + static Object sf1235; + static Object sf1236; + static Object sf1237; + static Object sf1238; + static Object sf1239; + static Object sf1240; + static Object sf1241; + static Object sf1242; + static Object sf1243; + static Object sf1244; + static Object sf1245; + static Object sf1246; + static Object sf1247; + static Object sf1248; + static Object sf1249; + static Object sf1250; + static Object sf1251; + static Object sf1252; + static Object sf1253; + static Object sf1254; + static Object sf1255; + static Object sf1256; + static Object sf1257; + static Object sf1258; + static Object sf1259; + static Object sf1260; + static Object sf1261; + static Object sf1262; + static Object sf1263; + static Object sf1264; + static Object sf1265; + static Object sf1266; + static Object sf1267; + static Object sf1268; + static Object sf1269; + static Object sf1270; + static Object sf1271; + static Object sf1272; + static Object sf1273; + static Object sf1274; + static Object sf1275; + static Object sf1276; + static Object sf1277; + static Object sf1278; + static Object sf1279; + static Object sf1280; + static Object sf1281; + static Object sf1282; + static Object sf1283; + static Object sf1284; + static Object sf1285; + static Object sf1286; + static Object sf1287; + static Object sf1288; + static Object sf1289; + static Object sf1290; + static Object sf1291; + static Object sf1292; + static Object sf1293; + static Object sf1294; + static Object sf1295; + static Object sf1296; + static Object sf1297; + static Object sf1298; + static Object sf1299; + static Object sf1300; + static Object sf1301; + static Object sf1302; + static Object sf1303; + static Object sf1304; + static Object sf1305; + static Object sf1306; + static Object sf1307; + static Object sf1308; + static Object sf1309; + static Object sf1310; + static Object sf1311; + static Object sf1312; + static Object sf1313; + static Object sf1314; + static Object sf1315; + static Object sf1316; + static Object sf1317; + static Object sf1318; + static Object sf1319; + static Object sf1320; + static Object sf1321; + static Object sf1322; + static Object sf1323; + static Object sf1324; + static Object sf1325; + static Object sf1326; + static Object sf1327; + static Object sf1328; + static Object sf1329; + static Object sf1330; + static Object sf1331; + static Object sf1332; + static Object sf1333; + static Object sf1334; + static Object sf1335; + static Object sf1336; + static Object sf1337; + static Object sf1338; + static Object sf1339; + static Object sf1340; + static Object sf1341; + static Object sf1342; + static Object sf1343; + static Object sf1344; + static Object sf1345; + static Object sf1346; + static Object sf1347; + static Object sf1348; + static Object sf1349; + static Object sf1350; + static Object sf1351; + static Object sf1352; + static Object sf1353; + static Object sf1354; + static Object sf1355; + static Object sf1356; + static Object sf1357; + static Object sf1358; + static Object sf1359; + static Object sf1360; + static Object sf1361; + static Object sf1362; + static Object sf1363; + static Object sf1364; + static Object sf1365; + static Object sf1366; + static Object sf1367; + static Object sf1368; + static Object sf1369; + static Object sf1370; + static Object sf1371; + static Object sf1372; + static Object sf1373; + static Object sf1374; + static Object sf1375; + static Object sf1376; + static Object sf1377; + static Object sf1378; + static Object sf1379; + static Object sf1380; + static Object sf1381; + static Object sf1382; + static Object sf1383; + static Object sf1384; + static Object sf1385; + static Object sf1386; + static Object sf1387; + static Object sf1388; + static Object sf1389; + static Object sf1390; + static Object sf1391; + static Object sf1392; + static Object sf1393; + static Object sf1394; + static Object sf1395; + static Object sf1396; + static Object sf1397; + static Object sf1398; + static Object sf1399; + static Object sf1400; + static Object sf1401; + static Object sf1402; + static Object sf1403; + static Object sf1404; + static Object sf1405; + static Object sf1406; + static Object sf1407; + static Object sf1408; + static Object sf1409; + static Object sf1410; + static Object sf1411; + static Object sf1412; + static Object sf1413; + static Object sf1414; + static Object sf1415; + static Object sf1416; + static Object sf1417; + static Object sf1418; + static Object sf1419; + static Object sf1420; + static Object sf1421; + static Object sf1422; + static Object sf1423; + static Object sf1424; + static Object sf1425; + static Object sf1426; + static Object sf1427; + static Object sf1428; + static Object sf1429; + static Object sf1430; + static Object sf1431; + static Object sf1432; + static Object sf1433; + static Object sf1434; + static Object sf1435; + static Object sf1436; + static Object sf1437; + static Object sf1438; + static Object sf1439; + static Object sf1440; + static Object sf1441; + static Object sf1442; + static Object sf1443; + static Object sf1444; + static Object sf1445; + static Object sf1446; + static Object sf1447; + static Object sf1448; + static Object sf1449; + static Object sf1450; + static Object sf1451; + static Object sf1452; + static Object sf1453; + static Object sf1454; + static Object sf1455; + static Object sf1456; + static Object sf1457; + static Object sf1458; + static Object sf1459; + static Object sf1460; + static Object sf1461; + static Object sf1462; + static Object sf1463; + static Object sf1464; + static Object sf1465; + static Object sf1466; + static Object sf1467; + static Object sf1468; + static Object sf1469; + static Object sf1470; + static Object sf1471; + static Object sf1472; + static Object sf1473; + static Object sf1474; + static Object sf1475; + static Object sf1476; + static Object sf1477; + static Object sf1478; + static Object sf1479; + static Object sf1480; + static Object sf1481; + static Object sf1482; + static Object sf1483; + static Object sf1484; + static Object sf1485; + static Object sf1486; + static Object sf1487; + static Object sf1488; + static Object sf1489; + static Object sf1490; + static Object sf1491; + static Object sf1492; + static Object sf1493; + static Object sf1494; + static Object sf1495; + static Object sf1496; + static Object sf1497; + static Object sf1498; + static Object sf1499; + static Object sf1500; + static Object sf1501; + static Object sf1502; + static Object sf1503; + static Object sf1504; + static Object sf1505; + static Object sf1506; + static Object sf1507; + static Object sf1508; + static Object sf1509; + static Object sf1510; + static Object sf1511; + static Object sf1512; + static Object sf1513; + static Object sf1514; + static Object sf1515; + static Object sf1516; + static Object sf1517; + static Object sf1518; + static Object sf1519; + static Object sf1520; + static Object sf1521; + static Object sf1522; + static Object sf1523; + static Object sf1524; + static Object sf1525; + static Object sf1526; + static Object sf1527; + static Object sf1528; + static Object sf1529; + static Object sf1530; + static Object sf1531; + static Object sf1532; + static Object sf1533; + static Object sf1534; + static Object sf1535; + static Object sf1536; + static Object sf1537; + static Object sf1538; + static Object sf1539; + static Object sf1540; + static Object sf1541; + static Object sf1542; + static Object sf1543; + static Object sf1544; + static Object sf1545; + static Object sf1546; + static Object sf1547; + static Object sf1548; + static Object sf1549; + static Object sf1550; + static Object sf1551; + static Object sf1552; + static Object sf1553; + static Object sf1554; + static Object sf1555; + static Object sf1556; + static Object sf1557; + static Object sf1558; + static Object sf1559; + static Object sf1560; + static Object sf1561; + static Object sf1562; + static Object sf1563; + static Object sf1564; + static Object sf1565; + static Object sf1566; + static Object sf1567; + static Object sf1568; + static Object sf1569; + static Object sf1570; + static Object sf1571; + static Object sf1572; + static Object sf1573; + static Object sf1574; + static Object sf1575; + static Object sf1576; + static Object sf1577; + static Object sf1578; + static Object sf1579; + static Object sf1580; + static Object sf1581; + static Object sf1582; + static Object sf1583; + static Object sf1584; + static Object sf1585; + static Object sf1586; + static Object sf1587; + static Object sf1588; + static Object sf1589; + static Object sf1590; + static Object sf1591; + static Object sf1592; + static Object sf1593; + static Object sf1594; + static Object sf1595; + static Object sf1596; + static Object sf1597; + static Object sf1598; + static Object sf1599; + static Object sf1600; + static Object sf1601; + static Object sf1602; + static Object sf1603; + static Object sf1604; + static Object sf1605; + static Object sf1606; + static Object sf1607; + static Object sf1608; + static Object sf1609; + static Object sf1610; + static Object sf1611; + static Object sf1612; + static Object sf1613; + static Object sf1614; + static Object sf1615; + static Object sf1616; + static Object sf1617; + static Object sf1618; + static Object sf1619; + static Object sf1620; + static Object sf1621; + static Object sf1622; + static Object sf1623; + static Object sf1624; + static Object sf1625; + static Object sf1626; + static Object sf1627; + static Object sf1628; + static Object sf1629; + static Object sf1630; + static Object sf1631; + static Object sf1632; + static Object sf1633; + static Object sf1634; + static Object sf1635; + static Object sf1636; + static Object sf1637; + static Object sf1638; + static Object sf1639; + static Object sf1640; + static Object sf1641; + static Object sf1642; + static Object sf1643; + static Object sf1644; + static Object sf1645; + static Object sf1646; + static Object sf1647; + static Object sf1648; + static Object sf1649; + static Object sf1650; + static Object sf1651; + static Object sf1652; + static Object sf1653; + static Object sf1654; + static Object sf1655; + static Object sf1656; + static Object sf1657; + static Object sf1658; + static Object sf1659; + static Object sf1660; + static Object sf1661; + static Object sf1662; + static Object sf1663; + static Object sf1664; + static Object sf1665; + static Object sf1666; + static Object sf1667; + static Object sf1668; + static Object sf1669; + static Object sf1670; + static Object sf1671; + static Object sf1672; + static Object sf1673; + static Object sf1674; + static Object sf1675; + static Object sf1676; + static Object sf1677; + static Object sf1678; + static Object sf1679; + static Object sf1680; + static Object sf1681; + static Object sf1682; + static Object sf1683; + static Object sf1684; + static Object sf1685; + static Object sf1686; + static Object sf1687; + static Object sf1688; + static Object sf1689; + static Object sf1690; + static Object sf1691; + static Object sf1692; + static Object sf1693; + static Object sf1694; + static Object sf1695; + static Object sf1696; + static Object sf1697; + static Object sf1698; + static Object sf1699; + static Object sf1700; + static Object sf1701; + static Object sf1702; + static Object sf1703; + static Object sf1704; + static Object sf1705; + static Object sf1706; + static Object sf1707; + static Object sf1708; + static Object sf1709; + static Object sf1710; + static Object sf1711; + static Object sf1712; + static Object sf1713; + static Object sf1714; + static Object sf1715; + static Object sf1716; + static Object sf1717; + static Object sf1718; + static Object sf1719; + static Object sf1720; + static Object sf1721; + static Object sf1722; + static Object sf1723; + static Object sf1724; + static Object sf1725; + static Object sf1726; + static Object sf1727; + static Object sf1728; + static Object sf1729; + static Object sf1730; + static Object sf1731; + static Object sf1732; + static Object sf1733; + static Object sf1734; + static Object sf1735; + static Object sf1736; + static Object sf1737; + static Object sf1738; + static Object sf1739; + static Object sf1740; + static Object sf1741; + static Object sf1742; + static Object sf1743; + static Object sf1744; + static Object sf1745; + static Object sf1746; + static Object sf1747; + static Object sf1748; + static Object sf1749; + static Object sf1750; + static Object sf1751; + static Object sf1752; + static Object sf1753; + static Object sf1754; + static Object sf1755; + static Object sf1756; + static Object sf1757; + static Object sf1758; + static Object sf1759; + static Object sf1760; + static Object sf1761; + static Object sf1762; + static Object sf1763; + static Object sf1764; + static Object sf1765; + static Object sf1766; + static Object sf1767; + static Object sf1768; + static Object sf1769; + static Object sf1770; + static Object sf1771; + static Object sf1772; + static Object sf1773; + static Object sf1774; + static Object sf1775; + static Object sf1776; + static Object sf1777; + static Object sf1778; + static Object sf1779; + static Object sf1780; + static Object sf1781; + static Object sf1782; + static Object sf1783; + static Object sf1784; + static Object sf1785; + static Object sf1786; + static Object sf1787; + static Object sf1788; + static Object sf1789; + static Object sf1790; + static Object sf1791; + static Object sf1792; + static Object sf1793; + static Object sf1794; + static Object sf1795; + static Object sf1796; + static Object sf1797; + static Object sf1798; + static Object sf1799; + static Object sf1800; + static Object sf1801; + static Object sf1802; + static Object sf1803; + static Object sf1804; + static Object sf1805; + static Object sf1806; + static Object sf1807; + static Object sf1808; + static Object sf1809; + static Object sf1810; + static Object sf1811; + static Object sf1812; + static Object sf1813; + static Object sf1814; + static Object sf1815; + static Object sf1816; + static Object sf1817; + static Object sf1818; + static Object sf1819; + static Object sf1820; + static Object sf1821; + static Object sf1822; + static Object sf1823; + static Object sf1824; + static Object sf1825; + static Object sf1826; + static Object sf1827; + static Object sf1828; + static Object sf1829; + static Object sf1830; + static Object sf1831; + static Object sf1832; + static Object sf1833; + static Object sf1834; + static Object sf1835; + static Object sf1836; + static Object sf1837; + static Object sf1838; + static Object sf1839; + static Object sf1840; + static Object sf1841; + static Object sf1842; + static Object sf1843; + static Object sf1844; + static Object sf1845; + static Object sf1846; + static Object sf1847; + static Object sf1848; + static Object sf1849; + static Object sf1850; + static Object sf1851; + static Object sf1852; + static Object sf1853; + static Object sf1854; + static Object sf1855; + static Object sf1856; + static Object sf1857; + static Object sf1858; + static Object sf1859; + static Object sf1860; + static Object sf1861; + static Object sf1862; + static Object sf1863; + static Object sf1864; + static Object sf1865; + static Object sf1866; + static Object sf1867; + static Object sf1868; + static Object sf1869; + static Object sf1870; + static Object sf1871; + static Object sf1872; + static Object sf1873; + static Object sf1874; + static Object sf1875; + static Object sf1876; + static Object sf1877; + static Object sf1878; + static Object sf1879; + static Object sf1880; + static Object sf1881; + static Object sf1882; + static Object sf1883; + static Object sf1884; + static Object sf1885; + static Object sf1886; + static Object sf1887; + static Object sf1888; + static Object sf1889; + static Object sf1890; + static Object sf1891; + static Object sf1892; + static Object sf1893; + static Object sf1894; + static Object sf1895; + static Object sf1896; + static Object sf1897; + static Object sf1898; + static Object sf1899; + static Object sf1900; + static Object sf1901; + static Object sf1902; + static Object sf1903; + static Object sf1904; + static Object sf1905; + static Object sf1906; + static Object sf1907; + static Object sf1908; + static Object sf1909; + static Object sf1910; + static Object sf1911; + static Object sf1912; + static Object sf1913; + static Object sf1914; + static Object sf1915; + static Object sf1916; + static Object sf1917; + static Object sf1918; + static Object sf1919; + static Object sf1920; + static Object sf1921; + static Object sf1922; + static Object sf1923; + static Object sf1924; + static Object sf1925; + static Object sf1926; + static Object sf1927; + static Object sf1928; + static Object sf1929; + static Object sf1930; + static Object sf1931; + static Object sf1932; + static Object sf1933; + static Object sf1934; + static Object sf1935; + static Object sf1936; + static Object sf1937; + static Object sf1938; + static Object sf1939; + static Object sf1940; + static Object sf1941; + static Object sf1942; + static Object sf1943; + static Object sf1944; + static Object sf1945; + static Object sf1946; + static Object sf1947; + static Object sf1948; + static Object sf1949; + static Object sf1950; + static Object sf1951; + static Object sf1952; + static Object sf1953; + static Object sf1954; + static Object sf1955; + static Object sf1956; + static Object sf1957; + static Object sf1958; + static Object sf1959; + static Object sf1960; + static Object sf1961; + static Object sf1962; + static Object sf1963; + static Object sf1964; + static Object sf1965; + static Object sf1966; + static Object sf1967; + static Object sf1968; + static Object sf1969; + static Object sf1970; + static Object sf1971; + static Object sf1972; + static Object sf1973; + static Object sf1974; + static Object sf1975; + static Object sf1976; + static Object sf1977; + static Object sf1978; + static Object sf1979; + static Object sf1980; + static Object sf1981; + static Object sf1982; + static Object sf1983; + static Object sf1984; + static Object sf1985; + static Object sf1986; + static Object sf1987; + static Object sf1988; + static Object sf1989; + static Object sf1990; + static Object sf1991; + static Object sf1992; + static Object sf1993; + static Object sf1994; + static Object sf1995; + static Object sf1996; + static Object sf1997; + static Object sf1998; + static Object sf1999; + static Object sf2000; + static Object sf2001; + static Object sf2002; + static Object sf2003; + static Object sf2004; + static Object sf2005; + static Object sf2006; + static Object sf2007; + static Object sf2008; + static Object sf2009; + static Object sf2010; + static Object sf2011; + static Object sf2012; + static Object sf2013; + static Object sf2014; + static Object sf2015; + static Object sf2016; + static Object sf2017; + static Object sf2018; + static Object sf2019; + static Object sf2020; + static Object sf2021; + static Object sf2022; + static Object sf2023; + static Object sf2024; + static Object sf2025; + static Object sf2026; + static Object sf2027; + static Object sf2028; + static Object sf2029; + static Object sf2030; + static Object sf2031; + static Object sf2032; + static Object sf2033; + static Object sf2034; + static Object sf2035; + static Object sf2036; + static Object sf2037; + static Object sf2038; + static Object sf2039; + static Object sf2040; + static Object sf2041; + static Object sf2042; + static Object sf2043; + static Object sf2044; + static Object sf2045; + static Object sf2046; + static Object sf2047; + static Object sf2048; + static Object sf2049; + static Object sf2050; + static Object sf2051; + static Object sf2052; + static Object sf2053; + static Object sf2054; + static Object sf2055; + static Object sf2056; + static Object sf2057; + static Object sf2058; + static Object sf2059; + static Object sf2060; + static Object sf2061; + static Object sf2062; + static Object sf2063; + static Object sf2064; + static Object sf2065; + static Object sf2066; + static Object sf2067; + static Object sf2068; + static Object sf2069; + static Object sf2070; + static Object sf2071; + static Object sf2072; + static Object sf2073; + static Object sf2074; + static Object sf2075; + static Object sf2076; + static Object sf2077; + static Object sf2078; + static Object sf2079; + static Object sf2080; + static Object sf2081; + static Object sf2082; + static Object sf2083; + static Object sf2084; + static Object sf2085; + static Object sf2086; + static Object sf2087; + static Object sf2088; + static Object sf2089; + static Object sf2090; + static Object sf2091; + static Object sf2092; + static Object sf2093; + static Object sf2094; + static Object sf2095; + static Object sf2096; + static Object sf2097; + static Object sf2098; + static Object sf2099; + static Object sf2100; + static Object sf2101; + static Object sf2102; + static Object sf2103; + static Object sf2104; + static Object sf2105; + static Object sf2106; + static Object sf2107; + static Object sf2108; + static Object sf2109; + static Object sf2110; + static Object sf2111; + static Object sf2112; + static Object sf2113; + static Object sf2114; + static Object sf2115; + static Object sf2116; + static Object sf2117; + static Object sf2118; + static Object sf2119; + static Object sf2120; + static Object sf2121; + static Object sf2122; + static Object sf2123; + static Object sf2124; + static Object sf2125; + static Object sf2126; + static Object sf2127; + static Object sf2128; + static Object sf2129; + static Object sf2130; + static Object sf2131; + static Object sf2132; + static Object sf2133; + static Object sf2134; + static Object sf2135; + static Object sf2136; + static Object sf2137; + static Object sf2138; + static Object sf2139; + static Object sf2140; + static Object sf2141; + static Object sf2142; + static Object sf2143; + static Object sf2144; + static Object sf2145; + static Object sf2146; + static Object sf2147; + static Object sf2148; + static Object sf2149; + static Object sf2150; + static Object sf2151; + static Object sf2152; + static Object sf2153; + static Object sf2154; + static Object sf2155; + static Object sf2156; + static Object sf2157; + static Object sf2158; + static Object sf2159; + static Object sf2160; + static Object sf2161; + static Object sf2162; + static Object sf2163; + static Object sf2164; + static Object sf2165; + static Object sf2166; + static Object sf2167; + static Object sf2168; + static Object sf2169; + static Object sf2170; + static Object sf2171; + static Object sf2172; + static Object sf2173; + static Object sf2174; + static Object sf2175; + static Object sf2176; + static Object sf2177; + static Object sf2178; + static Object sf2179; + static Object sf2180; + static Object sf2181; + static Object sf2182; + static Object sf2183; + static Object sf2184; + static Object sf2185; + static Object sf2186; + static Object sf2187; + static Object sf2188; + static Object sf2189; + static Object sf2190; + static Object sf2191; + static Object sf2192; + static Object sf2193; + static Object sf2194; + static Object sf2195; + static Object sf2196; + static Object sf2197; + static Object sf2198; + static Object sf2199; + static Object sf2200; + static Object sf2201; + static Object sf2202; + static Object sf2203; + static Object sf2204; + static Object sf2205; + static Object sf2206; + static Object sf2207; + static Object sf2208; + static Object sf2209; + static Object sf2210; + static Object sf2211; + static Object sf2212; + static Object sf2213; + static Object sf2214; + static Object sf2215; + static Object sf2216; + static Object sf2217; + static Object sf2218; + static Object sf2219; + static Object sf2220; + static Object sf2221; + static Object sf2222; + static Object sf2223; + static Object sf2224; + static Object sf2225; + static Object sf2226; + static Object sf2227; + static Object sf2228; + static Object sf2229; + static Object sf2230; + static Object sf2231; + static Object sf2232; + static Object sf2233; + static Object sf2234; + static Object sf2235; + static Object sf2236; + static Object sf2237; + static Object sf2238; + static Object sf2239; + static Object sf2240; + static Object sf2241; + static Object sf2242; + static Object sf2243; + static Object sf2244; + static Object sf2245; + static Object sf2246; + static Object sf2247; + static Object sf2248; + static Object sf2249; + static Object sf2250; + static Object sf2251; + static Object sf2252; + static Object sf2253; + static Object sf2254; + static Object sf2255; + static Object sf2256; + static Object sf2257; + static Object sf2258; + static Object sf2259; + static Object sf2260; + static Object sf2261; + static Object sf2262; + static Object sf2263; + static Object sf2264; + static Object sf2265; + static Object sf2266; + static Object sf2267; + static Object sf2268; + static Object sf2269; + static Object sf2270; + static Object sf2271; + static Object sf2272; + static Object sf2273; + static Object sf2274; + static Object sf2275; + static Object sf2276; + static Object sf2277; + static Object sf2278; + static Object sf2279; + static Object sf2280; + static Object sf2281; + static Object sf2282; + static Object sf2283; + static Object sf2284; + static Object sf2285; + static Object sf2286; + static Object sf2287; + static Object sf2288; + static Object sf2289; + static Object sf2290; + static Object sf2291; + static Object sf2292; + static Object sf2293; + static Object sf2294; + static Object sf2295; + static Object sf2296; + static Object sf2297; + static Object sf2298; + static Object sf2299; + static Object sf2300; + static Object sf2301; + static Object sf2302; + static Object sf2303; + static Object sf2304; + static Object sf2305; + static Object sf2306; + static Object sf2307; + static Object sf2308; + static Object sf2309; + static Object sf2310; + static Object sf2311; + static Object sf2312; + static Object sf2313; + static Object sf2314; + static Object sf2315; + static Object sf2316; + static Object sf2317; + static Object sf2318; + static Object sf2319; + static Object sf2320; + static Object sf2321; + static Object sf2322; + static Object sf2323; + static Object sf2324; + static Object sf2325; + static Object sf2326; + static Object sf2327; + static Object sf2328; + static Object sf2329; + static Object sf2330; + static Object sf2331; + static Object sf2332; + static Object sf2333; + static Object sf2334; + static Object sf2335; + static Object sf2336; + static Object sf2337; + static Object sf2338; + static Object sf2339; + static Object sf2340; + static Object sf2341; + static Object sf2342; + static Object sf2343; + static Object sf2344; + static Object sf2345; + static Object sf2346; + static Object sf2347; + static Object sf2348; + static Object sf2349; + static Object sf2350; + static Object sf2351; + static Object sf2352; + static Object sf2353; + static Object sf2354; + static Object sf2355; + static Object sf2356; + static Object sf2357; + static Object sf2358; + static Object sf2359; + static Object sf2360; + static Object sf2361; + static Object sf2362; + static Object sf2363; + static Object sf2364; + static Object sf2365; + static Object sf2366; + static Object sf2367; + static Object sf2368; + static Object sf2369; + static Object sf2370; + static Object sf2371; + static Object sf2372; + static Object sf2373; + static Object sf2374; + static Object sf2375; + static Object sf2376; + static Object sf2377; + static Object sf2378; + static Object sf2379; + static Object sf2380; + static Object sf2381; + static Object sf2382; + static Object sf2383; + static Object sf2384; + static Object sf2385; + static Object sf2386; + static Object sf2387; + static Object sf2388; + static Object sf2389; + static Object sf2390; + static Object sf2391; + static Object sf2392; + static Object sf2393; + static Object sf2394; + static Object sf2395; + static Object sf2396; + static Object sf2397; + static Object sf2398; + static Object sf2399; + static Object sf2400; + static Object sf2401; + static Object sf2402; + static Object sf2403; + static Object sf2404; + static Object sf2405; + static Object sf2406; + static Object sf2407; + static Object sf2408; + static Object sf2409; + static Object sf2410; + static Object sf2411; + static Object sf2412; + static Object sf2413; + static Object sf2414; + static Object sf2415; + static Object sf2416; + static Object sf2417; + static Object sf2418; + static Object sf2419; + static Object sf2420; + static Object sf2421; + static Object sf2422; + static Object sf2423; + static Object sf2424; + static Object sf2425; + static Object sf2426; + static Object sf2427; + static Object sf2428; + static Object sf2429; + static Object sf2430; + static Object sf2431; + static Object sf2432; + static Object sf2433; + static Object sf2434; + static Object sf2435; + static Object sf2436; + static Object sf2437; + static Object sf2438; + static Object sf2439; + static Object sf2440; + static Object sf2441; + static Object sf2442; + static Object sf2443; + static Object sf2444; + static Object sf2445; + static Object sf2446; + static Object sf2447; + static Object sf2448; + static Object sf2449; + static Object sf2450; + static Object sf2451; + static Object sf2452; + static Object sf2453; + static Object sf2454; + static Object sf2455; + static Object sf2456; + static Object sf2457; + static Object sf2458; + static Object sf2459; + static Object sf2460; + static Object sf2461; + static Object sf2462; + static Object sf2463; + static Object sf2464; + static Object sf2465; + static Object sf2466; + static Object sf2467; + static Object sf2468; + static Object sf2469; + static Object sf2470; + static Object sf2471; + static Object sf2472; + static Object sf2473; + static Object sf2474; + static Object sf2475; + static Object sf2476; + static Object sf2477; + static Object sf2478; + static Object sf2479; + static Object sf2480; + static Object sf2481; + static Object sf2482; + static Object sf2483; + static Object sf2484; + static Object sf2485; + static Object sf2486; + static Object sf2487; + static Object sf2488; + static Object sf2489; + static Object sf2490; + static Object sf2491; + static Object sf2492; + static Object sf2493; + static Object sf2494; + static Object sf2495; + static Object sf2496; + static Object sf2497; + static Object sf2498; + static Object sf2499; + static Object sf2500; + static Object sf2501; + static Object sf2502; + static Object sf2503; + static Object sf2504; + static Object sf2505; + static Object sf2506; + static Object sf2507; + static Object sf2508; + static Object sf2509; + static Object sf2510; + static Object sf2511; + static Object sf2512; + static Object sf2513; + static Object sf2514; + static Object sf2515; + static Object sf2516; + static Object sf2517; + static Object sf2518; + static Object sf2519; + static Object sf2520; + static Object sf2521; + static Object sf2522; + static Object sf2523; + static Object sf2524; + static Object sf2525; + static Object sf2526; + static Object sf2527; + static Object sf2528; + static Object sf2529; + static Object sf2530; + static Object sf2531; + static Object sf2532; + static Object sf2533; + static Object sf2534; + static Object sf2535; + static Object sf2536; + static Object sf2537; + static Object sf2538; + static Object sf2539; + static Object sf2540; + static Object sf2541; + static Object sf2542; + static Object sf2543; + static Object sf2544; + static Object sf2545; + static Object sf2546; + static Object sf2547; + static Object sf2548; + static Object sf2549; + static Object sf2550; + static Object sf2551; + static Object sf2552; + static Object sf2553; + static Object sf2554; + static Object sf2555; + static Object sf2556; + static Object sf2557; + static Object sf2558; + static Object sf2559; + static Object sf2560; + static Object sf2561; + static Object sf2562; + static Object sf2563; + static Object sf2564; + static Object sf2565; + static Object sf2566; + static Object sf2567; + static Object sf2568; + static Object sf2569; + static Object sf2570; + static Object sf2571; + static Object sf2572; + static Object sf2573; + static Object sf2574; + static Object sf2575; + static Object sf2576; + static Object sf2577; + static Object sf2578; + static Object sf2579; + static Object sf2580; + static Object sf2581; + static Object sf2582; + static Object sf2583; + static Object sf2584; + static Object sf2585; + static Object sf2586; + static Object sf2587; + static Object sf2588; + static Object sf2589; + static Object sf2590; + static Object sf2591; + static Object sf2592; + static Object sf2593; + static Object sf2594; + static Object sf2595; + static Object sf2596; + static Object sf2597; + static Object sf2598; + static Object sf2599; + static Object sf2600; + static Object sf2601; + static Object sf2602; + static Object sf2603; + static Object sf2604; + static Object sf2605; + static Object sf2606; + static Object sf2607; + static Object sf2608; + static Object sf2609; + static Object sf2610; + static Object sf2611; + static Object sf2612; + static Object sf2613; + static Object sf2614; + static Object sf2615; + static Object sf2616; + static Object sf2617; + static Object sf2618; + static Object sf2619; + static Object sf2620; + static Object sf2621; + static Object sf2622; + static Object sf2623; + static Object sf2624; + static Object sf2625; + static Object sf2626; + static Object sf2627; + static Object sf2628; + static Object sf2629; + static Object sf2630; + static Object sf2631; + static Object sf2632; + static Object sf2633; + static Object sf2634; + static Object sf2635; + static Object sf2636; + static Object sf2637; + static Object sf2638; + static Object sf2639; + static Object sf2640; + static Object sf2641; + static Object sf2642; + static Object sf2643; + static Object sf2644; + static Object sf2645; + static Object sf2646; + static Object sf2647; + static Object sf2648; + static Object sf2649; + static Object sf2650; + static Object sf2651; + static Object sf2652; + static Object sf2653; + static Object sf2654; + static Object sf2655; + static Object sf2656; + static Object sf2657; + static Object sf2658; + static Object sf2659; + static Object sf2660; + static Object sf2661; + static Object sf2662; + static Object sf2663; + static Object sf2664; + static Object sf2665; + static Object sf2666; + static Object sf2667; + static Object sf2668; + static Object sf2669; + static Object sf2670; + static Object sf2671; + static Object sf2672; + static Object sf2673; + static Object sf2674; + static Object sf2675; + static Object sf2676; + static Object sf2677; + static Object sf2678; + static Object sf2679; + static Object sf2680; + static Object sf2681; + static Object sf2682; + static Object sf2683; + static Object sf2684; + static Object sf2685; + static Object sf2686; + static Object sf2687; + static Object sf2688; + static Object sf2689; + static Object sf2690; + static Object sf2691; + static Object sf2692; + static Object sf2693; + static Object sf2694; + static Object sf2695; + static Object sf2696; + static Object sf2697; + static Object sf2698; + static Object sf2699; + static Object sf2700; + static Object sf2701; + static Object sf2702; + static Object sf2703; + static Object sf2704; + static Object sf2705; + static Object sf2706; + static Object sf2707; + static Object sf2708; + static Object sf2709; + static Object sf2710; + static Object sf2711; + static Object sf2712; + static Object sf2713; + static Object sf2714; + static Object sf2715; + static Object sf2716; + static Object sf2717; + static Object sf2718; + static Object sf2719; + static Object sf2720; + static Object sf2721; + static Object sf2722; + static Object sf2723; + static Object sf2724; + static Object sf2725; + static Object sf2726; + static Object sf2727; + static Object sf2728; + static Object sf2729; + static Object sf2730; + static Object sf2731; + static Object sf2732; + static Object sf2733; + static Object sf2734; + static Object sf2735; + static Object sf2736; + static Object sf2737; + static Object sf2738; + static Object sf2739; + static Object sf2740; + static Object sf2741; + static Object sf2742; + static Object sf2743; + static Object sf2744; + static Object sf2745; + static Object sf2746; + static Object sf2747; + static Object sf2748; + static Object sf2749; + static Object sf2750; + static Object sf2751; + static Object sf2752; + static Object sf2753; + static Object sf2754; + static Object sf2755; + static Object sf2756; + static Object sf2757; + static Object sf2758; + static Object sf2759; + static Object sf2760; + static Object sf2761; + static Object sf2762; + static Object sf2763; + static Object sf2764; + static Object sf2765; + static Object sf2766; + static Object sf2767; + static Object sf2768; + static Object sf2769; + static Object sf2770; + static Object sf2771; + static Object sf2772; + static Object sf2773; + static Object sf2774; + static Object sf2775; + static Object sf2776; + static Object sf2777; + static Object sf2778; + static Object sf2779; + static Object sf2780; + static Object sf2781; + static Object sf2782; + static Object sf2783; + static Object sf2784; + static Object sf2785; + static Object sf2786; + static Object sf2787; + static Object sf2788; + static Object sf2789; + static Object sf2790; + static Object sf2791; + static Object sf2792; + static Object sf2793; + static Object sf2794; + static Object sf2795; + static Object sf2796; + static Object sf2797; + static Object sf2798; + static Object sf2799; + static Object sf2800; + static Object sf2801; + static Object sf2802; + static Object sf2803; + static Object sf2804; + static Object sf2805; + static Object sf2806; + static Object sf2807; + static Object sf2808; + static Object sf2809; + static Object sf2810; + static Object sf2811; + static Object sf2812; + static Object sf2813; + static Object sf2814; + static Object sf2815; + static Object sf2816; + static Object sf2817; + static Object sf2818; + static Object sf2819; + static Object sf2820; + static Object sf2821; + static Object sf2822; + static Object sf2823; + static Object sf2824; + static Object sf2825; + static Object sf2826; + static Object sf2827; + static Object sf2828; + static Object sf2829; + static Object sf2830; + static Object sf2831; + static Object sf2832; + static Object sf2833; + static Object sf2834; + static Object sf2835; + static Object sf2836; + static Object sf2837; + static Object sf2838; + static Object sf2839; + static Object sf2840; + static Object sf2841; + static Object sf2842; + static Object sf2843; + static Object sf2844; + static Object sf2845; + static Object sf2846; + static Object sf2847; + static Object sf2848; + static Object sf2849; + static Object sf2850; + static Object sf2851; + static Object sf2852; + static Object sf2853; + static Object sf2854; + static Object sf2855; + static Object sf2856; + static Object sf2857; + static Object sf2858; + static Object sf2859; + static Object sf2860; + static Object sf2861; + static Object sf2862; + static Object sf2863; + static Object sf2864; + static Object sf2865; + static Object sf2866; + static Object sf2867; + static Object sf2868; + static Object sf2869; + static Object sf2870; + static Object sf2871; + static Object sf2872; + static Object sf2873; + static Object sf2874; + static Object sf2875; + static Object sf2876; + static Object sf2877; + static Object sf2878; + static Object sf2879; + static Object sf2880; + static Object sf2881; + static Object sf2882; + static Object sf2883; + static Object sf2884; + static Object sf2885; + static Object sf2886; + static Object sf2887; + static Object sf2888; + static Object sf2889; + static Object sf2890; + static Object sf2891; + static Object sf2892; + static Object sf2893; + static Object sf2894; + static Object sf2895; + static Object sf2896; + static Object sf2897; + static Object sf2898; + static Object sf2899; + static Object sf2900; + static Object sf2901; + static Object sf2902; + static Object sf2903; + static Object sf2904; + static Object sf2905; + static Object sf2906; + static Object sf2907; + static Object sf2908; + static Object sf2909; + static Object sf2910; + static Object sf2911; + static Object sf2912; + static Object sf2913; + static Object sf2914; + static Object sf2915; + static Object sf2916; + static Object sf2917; + static Object sf2918; + static Object sf2919; + static Object sf2920; + static Object sf2921; + static Object sf2922; + static Object sf2923; + static Object sf2924; + static Object sf2925; + static Object sf2926; + static Object sf2927; + static Object sf2928; + static Object sf2929; + static Object sf2930; + static Object sf2931; + static Object sf2932; + static Object sf2933; + static Object sf2934; + static Object sf2935; + static Object sf2936; + static Object sf2937; + static Object sf2938; + static Object sf2939; + static Object sf2940; + static Object sf2941; + static Object sf2942; + static Object sf2943; + static Object sf2944; + static Object sf2945; + static Object sf2946; + static Object sf2947; + static Object sf2948; + static Object sf2949; + static Object sf2950; + static Object sf2951; + static Object sf2952; + static Object sf2953; + static Object sf2954; + static Object sf2955; + static Object sf2956; + static Object sf2957; + static Object sf2958; + static Object sf2959; + static Object sf2960; + static Object sf2961; + static Object sf2962; + static Object sf2963; + static Object sf2964; + static Object sf2965; + static Object sf2966; + static Object sf2967; + static Object sf2968; + static Object sf2969; + static Object sf2970; + static Object sf2971; + static Object sf2972; + static Object sf2973; + static Object sf2974; + static Object sf2975; + static Object sf2976; + static Object sf2977; + static Object sf2978; + static Object sf2979; + static Object sf2980; + static Object sf2981; + static Object sf2982; + static Object sf2983; + static Object sf2984; + static Object sf2985; + static Object sf2986; + static Object sf2987; + static Object sf2988; + static Object sf2989; + static Object sf2990; + static Object sf2991; + static Object sf2992; + static Object sf2993; + static Object sf2994; + static Object sf2995; + static Object sf2996; + static Object sf2997; + static Object sf2998; + static Object sf2999; + static Object sf3000; + } + +} diff --git a/test/410-floats/src/Main.java b/test/410-floats/src/Main.java index d8d6fac306..2300457840 100644 --- a/test/410-floats/src/Main.java +++ b/test/410-floats/src/Main.java @@ -17,9 +17,10 @@ public class Main { public static void main(String[] args) { assertEquals(4.2f, returnFloat()); - float[] a = new float[1]; + float[] a = new float[2]; a[0] = 42.2f; - assertEquals(42.2f, returnFloat(a)); + a[1] = 3.2f; + assertEquals(45.4f, returnFloat(a)); assertEquals(4.4, returnDouble()); double[] b = new double[1]; @@ -36,6 +37,9 @@ public class Main { assertEquals(3.1, invokeTakeADouble(3.1)); assertEquals(12.7, invokeTakeThreeDouble(3.1, 4.4, 5.2)); assertEquals(12.7f, invokeTakeThreeFloat(3.1f, 4.4f, 5.2f)); + + testArrayOperations(new float[2], 0, 1.2f, 3.4f); + testArrayOperations(new double[2], 0, 4.1, 7.6); } public static float invokeReturnFloat() { @@ -51,7 +55,7 @@ public class Main { } public static float returnFloat(float[] a) { - return a[0]; + return a[0] + a[1]; } public static double returnDouble() { @@ -94,6 +98,34 @@ public class Main { return takeThreeFloat(a, b, c); } + // Test simple operations on a float array to ensure the register allocator works + // properly. + public static void testArrayOperations(float[] a, int index, float value1, float value2) { + a[0] = value1; + a[1] = value2; + assertEquals(value1 + value2, a[0] + a[1]); + a[0] = 0.0f; + a[1] = 0.0f; + assertEquals(0.0f, a[0] + a[1]); + a[index] = value1; + a[index + 1] = value2; + assertEquals(value1 + value2, a[0] + a[1]); + } + + // Test simple operations on a double array to ensure the register allocator works + // properly. + public static void testArrayOperations(double[] a, int index, double value1, double value2) { + a[0] = value1; + a[1] = value2; + assertEquals(value1 + value2, a[0] + a[1]); + a[0] = 0.0; + a[1] = 0.0; + assertEquals(0.0, a[0] + a[1]); + a[index] = value1; + a[index + 1] = value2; + assertEquals(value1 + value2, a[0] + a[1]); + } + public static void assertEquals(float expected, float actual) { if (expected != actual) { throw new AssertionError("Expected " + expected + " got " + actual); diff --git a/test/411-optimizing-arith/expected.txt b/test/411-optimizing-arith/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/411-optimizing-arith/expected.txt diff --git a/test/411-optimizing-arith/info.txt b/test/411-optimizing-arith/info.txt new file mode 100644 index 0000000000..10155512f0 --- /dev/null +++ b/test/411-optimizing-arith/info.txt @@ -0,0 +1 @@ +Tests for basic arithmethic operations. diff --git a/test/411-optimizing-arith/src/Main.java b/test/411-optimizing-arith/src/Main.java new file mode 100644 index 0000000000..4de2271ebd --- /dev/null +++ b/test/411-optimizing-arith/src/Main.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Note that $opt$ is a marker for the optimizing compiler to ensure +// it does compile the method. +public class Main { + + public static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void expectEquals(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void expectEquals(float expected, float result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void expectEquals(double expected, double result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void expectApproxEquals(float a, float b, float maxDelta) { + boolean aproxEquals = (a > b) + ? ((a - b) < maxDelta) + : ((b - a) < maxDelta); + if (!aproxEquals) { + throw new Error("Expected: " + a + ", found: " + b + ", with delta: " + maxDelta); + } + } + + public static void expectApproxEquals(double a, double b, double maxDelta) { + boolean aproxEquals = (a > b) + ? ((a - b) < maxDelta) + : ((b - a) < maxDelta); + if (!aproxEquals) { + throw new Error("Expected: " + a + ", found: " + b + ", with delta: " + maxDelta); + } + } + + public static void expectNaN(float a) { + if (a == a) { + throw new Error("Expected NaN: " + a); + } + } + + public static void expectNaN(double a) { + if (a == a) { + throw new Error("Expected NaN: " + a); + } + } + + public static void main(String[] args) { + mul(); + neg(); + } + + public static void mul() { + mulInt(); + mulLong(); + mulFloat(); + mulDouble(); + } + + private static void mulInt() { + expectEquals(15, $opt$Mul(5, 3)); + expectEquals(0, $opt$Mul(0, 0)); + expectEquals(0, $opt$Mul(0, 3)); + expectEquals(0, $opt$Mul(3, 0)); + expectEquals(-3, $opt$Mul(1, -3)); + expectEquals(36, $opt$Mul(-12, -3)); + expectEquals(33, $opt$Mul(1, 3) * 11); + expectEquals(671088645, $opt$Mul(134217729, 5)); // (2^27 + 1) * 5 + } + + private static void mulLong() { + expectEquals(15L, $opt$Mul(5L, 3L)); + expectEquals(0L, $opt$Mul(0L, 0L)); + expectEquals(0L, $opt$Mul(0L, 3L)); + expectEquals(0L, $opt$Mul(3L, 0L)); + expectEquals(-3L, $opt$Mul(1L, -3L)); + expectEquals(36L, $opt$Mul(-12L, -3L)); + expectEquals(33L, $opt$Mul(1L, 3L) * 11F); + expectEquals(240518168583L, $opt$Mul(34359738369L, 7L)); // (2^35 + 1) * 7 + } + + private static void mulFloat() { + expectApproxEquals(15F, $opt$Mul(5F, 3F), 0.0001F); + expectApproxEquals(0F, $opt$Mul(0F, 0F), 0.0001F); + expectApproxEquals(0F, $opt$Mul(0F, 3F), 0.0001F); + expectApproxEquals(0F, $opt$Mul(3F, 0F), 0.0001F); + expectApproxEquals(-3F, $opt$Mul(1F, -3F), 0.0001F); + expectApproxEquals(36F, $opt$Mul(-12F, -3F), 0.0001F); + expectApproxEquals(33F, $opt$Mul(1F, 3F) * 11F, 0.0001F); + expectApproxEquals(0.02F, 0.1F * 0.2F, 0.0001F); + expectApproxEquals(-0.1F, -0.5F * 0.2F, 0.0001F); + + expectNaN($opt$Mul(0F, Float.POSITIVE_INFINITY)); + expectNaN($opt$Mul(0F, Float.NEGATIVE_INFINITY)); + expectNaN($opt$Mul(Float.NaN, 11F)); + expectNaN($opt$Mul(Float.NaN, -11F)); + expectNaN($opt$Mul(Float.NaN, Float.NEGATIVE_INFINITY)); + expectNaN($opt$Mul(Float.NaN, Float.POSITIVE_INFINITY)); + + expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(2F, 3.40282346638528860e+38F)); + expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(2F, Float.POSITIVE_INFINITY)); + expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(-2F, Float.POSITIVE_INFINITY)); + expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(-2F, 3.40282346638528860e+38F)); + expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(2F, Float.NEGATIVE_INFINITY)); + expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(-2F, Float.NEGATIVE_INFINITY)); + expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY)); + expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)); + expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY)); + } + + private static void mulDouble() { + expectApproxEquals(15D, $opt$Mul(5D, 3D), 0.0001D); + expectApproxEquals(0D, $opt$Mul(0D, 0D), 0.0001D); + expectApproxEquals(0D, $opt$Mul(0D, 3D), 0.0001D); + expectApproxEquals(0D, $opt$Mul(3D, 0D), 0.0001D); + expectApproxEquals(-3D, $opt$Mul(1D, -3D), 0.0001D); + expectApproxEquals(36D, $opt$Mul(-12D, -3D), 0.0001D); + expectApproxEquals(33D, $opt$Mul(1D, 3D) * 11D, 0.0001D); + expectApproxEquals(0.02D, 0.1D * 0.2D, 0.0001D); + expectApproxEquals(-0.1D, -0.5D * 0.2D, 0.0001D); + + expectNaN($opt$Mul(0D, Double.POSITIVE_INFINITY)); + expectNaN($opt$Mul(0D, Double.NEGATIVE_INFINITY)); + expectNaN($opt$Mul(Double.NaN, 11D)); + expectNaN($opt$Mul(Double.NaN, -11D)); + expectNaN($opt$Mul(Double.NaN, Double.NEGATIVE_INFINITY)); + expectNaN($opt$Mul(Double.NaN, Double.POSITIVE_INFINITY)); + + expectEquals(Double.POSITIVE_INFINITY, $opt$Mul(2D, 1.79769313486231570e+308)); + expectEquals(Double.POSITIVE_INFINITY, $opt$Mul(2D, Double.POSITIVE_INFINITY)); + expectEquals(Double.NEGATIVE_INFINITY, $opt$Mul(-2D, Double.POSITIVE_INFINITY)); + expectEquals(Double.NEGATIVE_INFINITY, $opt$Mul(-2D, 1.79769313486231570e+308)); + expectEquals(Double.NEGATIVE_INFINITY, $opt$Mul(2D, Double.NEGATIVE_INFINITY)); + expectEquals(Double.POSITIVE_INFINITY, $opt$Mul(-2D, Double.NEGATIVE_INFINITY)); + expectEquals(Double.NEGATIVE_INFINITY, $opt$Mul(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY)); + expectEquals(Double.POSITIVE_INFINITY, $opt$Mul(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY)); + expectEquals(Double.POSITIVE_INFINITY, $opt$Mul(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY)); + } + + public static void neg() { + expectEquals(-1, $opt$Neg(1)); + expectEquals(1, $opt$Neg(-1)); + expectEquals(0, $opt$Neg(0)); + expectEquals(51, $opt$Neg(-51)); + expectEquals(-51, $opt$Neg(51)); + expectEquals(2147483647, $opt$Neg(-2147483647)); // (2^31 - 1) + expectEquals(-2147483647, $opt$Neg(2147483647)); // -(2^31 - 1) + // From the Java 7 SE Edition specification: + // http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.15.4 + // + // For integer values, negation is the same as subtraction from + // zero. The Java programming language uses two's-complement + // representation for integers, and the range of two's-complement + // values is not symmetric, so negation of the maximum negative + // int or long results in that same maximum negative number. + // Overflow occurs in this case, but no exception is thrown. + // For all integer values x, -x equals (~x)+1.'' + expectEquals(-2147483648, $opt$Neg(-2147483648)); // -(2^31) + + $opt$InplaceNegOne(1); + } + + public static void $opt$InplaceNegOne(int a) { + a = -a; + expectEquals(-1, a); + } + + static int $opt$Mul(int a, int b) { + return a * b; + } + + static long $opt$Mul(long a, long b) { + return a * b; + } + + static float $opt$Mul(float a, float b) { + return a * b; + } + + static double $opt$Mul(double a, double b) { + return a * b; + } + + static int $opt$Neg(int a){ + return -a; + } + +} diff --git a/test/412-new-array/expected.txt b/test/412-new-array/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/412-new-array/expected.txt diff --git a/test/412-new-array/info.txt b/test/412-new-array/info.txt new file mode 100644 index 0000000000..cb388b6023 --- /dev/null +++ b/test/412-new-array/info.txt @@ -0,0 +1 @@ +Simple tests for new-array, filled-new-array and fill-array-data. diff --git a/test/412-new-array/smali/fill_array_data.smali b/test/412-new-array/smali/fill_array_data.smali new file mode 100644 index 0000000000..34776db5de --- /dev/null +++ b/test/412-new-array/smali/fill_array_data.smali @@ -0,0 +1,81 @@ +.class public LFillArrayData; + +.super Ljava/lang/Object; + +.method public static intArray([I)V + .registers 1 + + fill-array-data v0, :ArrayData + return-void + +:ArrayData + .array-data 4 + 1 2 3 4 5 + .end array-data + +.end method + +.method public static shortArray([S)V + .registers 1 + + fill-array-data v0, :ArrayData + return-void + +:ArrayData + .array-data 2 + 1 2 3 4 5 + .end array-data + +.end method + +.method public static charArray([C)V + .registers 1 + + fill-array-data v0, :ArrayData + return-void + +:ArrayData + .array-data 2 + 1 2 3 4 5 + .end array-data + +.end method + +.method public static byteArray([B)V + .registers 1 + + fill-array-data v0, :ArrayData + return-void + +:ArrayData + .array-data 1 + 1 2 3 4 5 + .end array-data + +.end method + +.method public static booleanArray([Z)V + .registers 1 + + fill-array-data v0, :ArrayData + return-void + +:ArrayData + .array-data 1 + 0 1 1 + .end array-data + +.end method + +.method public static longArray([J)V + .registers 1 + + fill-array-data v0, :ArrayData + return-void + +:ArrayData + .array-data 8 + 1 2 3 4 5 + .end array-data + +.end method diff --git a/test/412-new-array/smali/filled_new_array.smali b/test/412-new-array/smali/filled_new_array.smali new file mode 100644 index 0000000000..ed8683a14b --- /dev/null +++ b/test/412-new-array/smali/filled_new_array.smali @@ -0,0 +1,45 @@ +.class public LFilledNewArray; + +.super Ljava/lang/Object; + +.method public static newInt(III)[I + .registers 4 + filled-new-array {v1, v2, v3}, [I + move-result-object v0 + return-object v0 +.end method + +.method public static newRef(Ljava/lang/Object;Ljava/lang/Object;)[Ljava/lang/Object; + .registers 3 + filled-new-array {v1, v2}, [Ljava/lang/Object; + move-result-object v0 + return-object v0 +.end method + +.method public static newArray([I[I)[[I + .registers 3 + filled-new-array {v1, v2}, [[I + move-result-object v0 + return-object v0 +.end method + +.method public static newIntRange(III)[I + .registers 4 + filled-new-array/range {v1 .. v3}, [I + move-result-object v0 + return-object v0 +.end method + +.method public static newRefRange(Ljava/lang/Object;Ljava/lang/Object;)[Ljava/lang/Object; + .registers 3 + filled-new-array/range {v1 .. v2}, [Ljava/lang/Object; + move-result-object v0 + return-object v0 +.end method + +.method public static newArrayRange([I[I)[[I + .registers 3 + filled-new-array/range {v1 .. v2}, [[I + move-result-object v0 + return-object v0 +.end method diff --git a/test/412-new-array/smali/filled_new_array_verify_error.smali b/test/412-new-array/smali/filled_new_array_verify_error.smali new file mode 100644 index 0000000000..b1470ec612 --- /dev/null +++ b/test/412-new-array/smali/filled_new_array_verify_error.smali @@ -0,0 +1,10 @@ +.class public LFilledNewArrayVerifyError; + +.super Ljava/lang/Object; + +.method public static newRef(Ljava/lang/Object;Ljava/lang/Object;)[Ljava/lang/Object; + .registers 3 + filled-new-array {v1, v2}, [Ljava/lang/Integer; + move-result-object v0 + return-object v0 +.end method diff --git a/test/412-new-array/src/Main.java b/test/412-new-array/src/Main.java new file mode 100644 index 0000000000..3c74275831 --- /dev/null +++ b/test/412-new-array/src/Main.java @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +// Note that $opt$ is a marker for the optimizing compiler to ensure +// it does compile the method. + +public class Main extends TestCase { + public static void main(String[] args) throws Exception { + $opt$TestAllocations(); + $opt$TestWithInitializations(); + testSmaliFilledNewArray(); + testSmaliFillArrayData(); + testSmaliVerifyError(); + } + + static void $opt$TestAllocations() { + float[] a = new float[1]; + assertEquals(1, a.length); + + double[] b = new double[2]; + assertEquals(2, b.length); + + long[] c = new long[3]; + assertEquals(3, c.length); + + int[] d = new int[4]; + assertEquals(4, d.length); + + short[] e = new short[5]; + assertEquals(5, e.length); + + char[] f = new char[6]; + assertEquals(6, f.length); + + byte[] g = new byte[7]; + assertEquals(7, g.length); + + boolean[] h = new boolean[8]; + assertEquals(8, h.length); + + Object[] i = new Object[9]; + assertEquals(9, i.length); + } + + static void $opt$TestWithInitializations() { + float[] a = { 1.2f }; + assertEquals(1, a.length); + assertEquals(1.2f, a[0]); + + double[] b = { 4.3, 1.2 }; + assertEquals(2, b.length); + assertEquals(4.3, b[0]); + assertEquals(1.2, b[1]); + + long[] c = { 4L, 5L }; + assertEquals(2, c.length); + assertEquals(4L, c[0]); + assertEquals(5L, c[1]); + + int[] d = {1, 2, 3}; + assertEquals(3, d.length); + assertEquals(1, d[0]); + assertEquals(2, d[1]); + assertEquals(3, d[2]); + + short[] e = {4, 5, 6}; + assertEquals(3, e.length); + assertEquals(4, e[0]); + assertEquals(5, e[1]); + assertEquals(6, e[2]); + + char[] f = {'a', 'b'}; + assertEquals(2, f.length); + assertEquals('a', f[0]); + assertEquals('b', f[1]); + + byte[] g = {7, 8, 9}; + assertEquals(3, g.length); + assertEquals(7, g[0]); + assertEquals(8, g[1]); + assertEquals(9, g[2]); + + boolean[] h = {true, false}; + assertEquals(2, h.length); + assertEquals(true, h[0]); + assertEquals(false, h[1]); + + Object obj1 = new Object(); + Object obj2 = new Object(); + Object[] i = {obj1, obj2}; + assertEquals(2, i.length); + assertEquals(obj1, i[0]); + assertEquals(obj2, i[1]); + } + + public static void testSmaliFilledNewArray() throws Exception { + Class<?> c = Class.forName("FilledNewArray"); + + { + Method m = c.getMethod("newInt", Integer.TYPE, Integer.TYPE, Integer.TYPE); + Object[] args = {new Integer(1), new Integer(2), new Integer(3)}; + int[] result = (int[])m.invoke(null, args); + assertEquals(3, result.length); + assertEquals(1, result[0]); + assertEquals(2, result[1]); + assertEquals(3, result[2]); + } + + { + Method m = c.getMethod("newRef", Object.class, Object.class); + Object[] args = {new Integer(1), new Integer(2)}; + Object[] result = (Object[])m.invoke(null, args); + assertEquals(2, result.length); + assertEquals(args[0], result[0]); + assertEquals(args[1], result[1]); + } + + { + Method m = c.getMethod("newArray", int[].class, int[].class); + Object[] args = {new int[0], new int[1]}; + Object[] result = (Object[])m.invoke(null, args); + assertEquals(2, result.length); + assertEquals(args[0], result[0]); + assertEquals(args[1], result[1]); + } + + { + Method m = c.getMethod("newIntRange", Integer.TYPE, Integer.TYPE, Integer.TYPE); + Object[] args = {new Integer(1), new Integer(2), new Integer(3)}; + int[] result = (int[])m.invoke(null, args); + assertEquals(3, result.length); + assertEquals(1, result[0]); + assertEquals(2, result[1]); + assertEquals(3, result[2]); + } + + { + Method m = c.getMethod("newRefRange", Object.class, Object.class); + Object[] args = {new Integer(1), new Integer(2)}; + Object[] result = (Object[])m.invoke(null, args); + assertEquals(2, result.length); + assertEquals(args[0], result[0]); + assertEquals(args[1], result[1]); + } + + { + Method m = c.getMethod("newArrayRange", int[].class, int[].class); + Object[] args = {new int[0], new int[1]}; + Object[] result = (Object[])m.invoke(null, args); + assertEquals(2, result.length); + assertEquals(args[0], result[0]); + assertEquals(args[1], result[1]); + } + } + + public static void testSmaliVerifyError() throws Exception { + Error error = null; + // Ensure the elements in filled-new-array must be assignable + // to the array component type. + try { + Class.forName("FilledNewArrayVerifyError"); + } catch (VerifyError e) { + error = e; + } + assertNotNull(error); + } + + public static void testSmaliFillArrayData() throws Exception { + Class<?> c = Class.forName("FillArrayData"); + { + Method m = c.getMethod("intArray", int[].class); + int[] array = new int[7]; + Object[] args = { array }; + m.invoke(null, args); + assertEquals(7, array.length); + assertEquals(1, array[0]); + assertEquals(2, array[1]); + assertEquals(3, array[2]); + assertEquals(4, array[3]); + assertEquals(5, array[4]); + assertEquals(0, array[5]); + assertEquals(0, array[6]); + + array = new int[2]; + args[0] = array; + Throwable exception = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof IndexOutOfBoundsException); + } + assertNotNull(exception); + exception = null; + // Test that nothing has been written to the array. + assertEquals(0, array[0]); + assertEquals(0, array[1]); + + args[0] = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof NullPointerException); + } + assertNotNull(exception); + } + + { + Method m = c.getMethod("shortArray", short[].class); + short[] array = new short[7]; + Object[] args = { array }; + m.invoke(null, args); + assertEquals(7, array.length); + assertEquals(1, array[0]); + assertEquals(2, array[1]); + assertEquals(3, array[2]); + assertEquals(4, array[3]); + assertEquals(5, array[4]); + assertEquals(0, array[5]); + assertEquals(0, array[6]); + + array = new short[2]; + args[0] = array; + Throwable exception = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof IndexOutOfBoundsException); + } + assertNotNull(exception); + exception = null; + // Test that nothing has been written to the array. + assertEquals(0, array[0]); + assertEquals(0, array[1]); + + args[0] = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof NullPointerException); + } + assertNotNull(exception); + } + + { + Method m = c.getMethod("longArray", long[].class); + long[] array = new long[7]; + Object[] args = { array }; + m.invoke(null, args); + assertEquals(7, array.length); + assertEquals(1L, array[0]); + assertEquals(2L, array[1]); + assertEquals(3L, array[2]); + assertEquals(4L, array[3]); + assertEquals(5L, array[4]); + assertEquals(0L, array[5]); + assertEquals(0L, array[6]); + + array = new long[2]; + args[0] = array; + Throwable exception = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof IndexOutOfBoundsException); + } + assertNotNull(exception); + exception = null; + // Test that nothing has been written to the array. + assertEquals(0, array[0]); + assertEquals(0, array[1]); + + args[0] = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof NullPointerException); + } + assertNotNull(exception); + } + + { + Method m = c.getMethod("charArray", char[].class); + char[] array = new char[7]; + Object[] args = { array }; + m.invoke(null, args); + assertEquals(7, array.length); + assertEquals(1, array[0]); + assertEquals(2, array[1]); + assertEquals(3, array[2]); + assertEquals(4, array[3]); + assertEquals(5, array[4]); + assertEquals(0, array[5]); + assertEquals(0, array[6]); + + array = new char[2]; + args[0] = array; + Throwable exception = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof IndexOutOfBoundsException); + } + assertNotNull(exception); + exception = null; + // Test that nothing has been written to the array. + assertEquals(0, array[0]); + assertEquals(0, array[1]); + + args[0] = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof NullPointerException); + } + assertNotNull(exception); + } + + { + Method m = c.getMethod("byteArray", byte[].class); + byte[] array = new byte[7]; + Object[] args = { array }; + m.invoke(null, args); + assertEquals(7, array.length); + assertEquals(1, array[0]); + assertEquals(2, array[1]); + assertEquals(3, array[2]); + assertEquals(4, array[3]); + assertEquals(5, array[4]); + assertEquals(0, array[5]); + assertEquals(0, array[6]); + + array = new byte[2]; + args[0] = array; + Throwable exception = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof IndexOutOfBoundsException); + } + assertNotNull(exception); + exception = null; + // Test that nothing has been written to the array. + assertEquals(0, array[0]); + assertEquals(0, array[1]); + + args[0] = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof NullPointerException); + } + assertNotNull(exception); + } + + { + Method m = c.getMethod("booleanArray", boolean[].class); + boolean[] array = new boolean[5]; + Object[] args = { array }; + m.invoke(null, args); + assertEquals(5, array.length); + assertEquals(false, array[0]); + assertEquals(true, array[1]); + assertEquals(true, array[2]); + assertEquals(false, array[3]); + assertEquals(false, array[4]); + + array = new boolean[2]; + args[0] = array; + Throwable exception = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof IndexOutOfBoundsException); + } + assertNotNull(exception); + exception = null; + // Test that nothing has been written to the array. + assertEquals(false, array[0]); + assertEquals(false, array[1]); + + args[0] = null; + try { + m.invoke(null, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + assertTrue(exception instanceof NullPointerException); + } + assertNotNull(exception); + } + } +} diff --git a/test/412-new-array/src/TestCase.java b/test/412-new-array/src/TestCase.java new file mode 100644 index 0000000000..ef77f71f3f --- /dev/null +++ b/test/412-new-array/src/TestCase.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Common superclass for test cases. + */ + +import java.util.Arrays; + +public abstract class TestCase { + public static void assertSame(Object expected, Object value) { + if (expected != value) { + throw new AssertionError("Objects are not the same: expected " + + String.valueOf(expected) + ", got " + String.valueOf(value)); + } + } + + public static void assertNotSame(Object expected, Object value) { + if (expected == value) { + throw new AssertionError( + "Objects are the same: " + String.valueOf(expected)); + } + } + + public static void assertEquals(String message, int expected, int actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertEquals(int expected, int actual) { + if (expected != actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertTrue(String message, boolean condition) { + if (!condition) { + throw new AssertionError(message); + } + } + + public static void assertTrue(boolean condition) { + assertTrue("Expected true", condition); + } + + public static void assertFalse(String message, boolean condition) { + if (condition) { + throw new AssertionError(message); + } + } + + public static void assertFalse(boolean condition) { + assertFalse("Expected false", condition); + } + + public static void assertEquals(Object expected, Object actual) { + if (!expected.equals(actual)) { + String msg = "Expected \"" + expected + "\" but got \"" + actual + "\""; + throw new AssertionError(msg); + } + } + + public static void assertNotEquals(int expected, int actual) { + if (expected == actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertNotEquals(Object expected, Object actual) { + if (expected.equals(actual)) { + String msg = "Objects are the same: " + String.valueOf(expected); + throw new AssertionError(msg); + } + } + + public static <T> void assertArrayEquals(T[] actual, T... expected) { + assertTrue(Arrays.equals(expected, actual)); + } + + public static void assertEquals( + String message, Object expected, Object actual) { + if (!expected.equals(actual)) { + throw new AssertionError(message); + } + } + + public static void assertEquals( + String message, long expected, long actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertEquals(long expected, long actual) { + if (expected != actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertEquals( + String message, boolean expected, boolean actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertEquals(boolean expected, boolean actual) { + if (expected != actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertEquals( + String message, float expected, float actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertEquals(float expected, float actual) { + if (expected != actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertEquals(float expected, float actual, + float tolerance) { + if ((actual < expected - tolerance) || (expected + tolerance < actual)) { + throw new AssertionError("Expected " + expected + " got " + actual + + " tolerance " + tolerance); + } + } + + public static void assertEquals( + String message, double expected, double actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertEquals(double expected, double actual) { + if (expected != actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertEquals(double expected, double actual, + double tolerance) { + if ((actual < expected - tolerance) || (expected + tolerance < actual)) { + throw new AssertionError("Expected " + expected + " got " + actual + + " tolerance " + tolerance); + } + } + + public static void assertSame( + String message, Object expected, Object actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertNull(String message, Object object) { + if (object != null) { + throw new AssertionError(message); + } + } + + public static void assertNull(Object object) { + assertNull("Expected null", object); + } + + public static void assertNotNull(String message, Object object) { + if (object == null) { + throw new AssertionError(message); + } + } + + public static void assertNotNull(Object object) { + assertNotNull("Expected non-null", object); + } + + public static void fail(String msg) { + throw new AssertionError(msg); + } +} diff --git a/test/413-regalloc-regression/expected.txt b/test/413-regalloc-regression/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/413-regalloc-regression/expected.txt diff --git a/test/413-regalloc-regression/info.txt b/test/413-regalloc-regression/info.txt new file mode 100644 index 0000000000..c706c1d4e4 --- /dev/null +++ b/test/413-regalloc-regression/info.txt @@ -0,0 +1,2 @@ +Regression test for the linear scan register allocator, that use to +fail compiling removeElementAt in x86. diff --git a/compiler/optimizing/constant_propagation.h b/test/413-regalloc-regression/src/Main.java index 0729881888..3e649f8e57 100644 --- a/compiler/optimizing/constant_propagation.h +++ b/test/413-regalloc-regression/src/Main.java @@ -14,30 +14,28 @@ * limitations under the License. */ -#ifndef ART_COMPILER_OPTIMIZING_CONSTANT_PROPAGATION_H_ -#define ART_COMPILER_OPTIMIZING_CONSTANT_PROPAGATION_H_ - -#include "nodes.h" - -namespace art { - -/** - * Optimization pass performing a simple constant propagation on the - * SSA form. - */ -class ConstantPropagation : public ValueObject { - public: - explicit ConstantPropagation(HGraph* graph) - : graph_(graph) {} - - void Run(); - - private: - HGraph* const graph_; - - DISALLOW_COPY_AND_ASSIGN(ConstantPropagation); -}; - -} // namespace art - -#endif // ART_COMPILER_OPTIMIZING_CONSTANT_PROPAGATION_H_ +public class Main { + private Object[] data; + private int size; + + public Main() { + data = new Object[4]; + size = 0; + } + + public void removeElementAt(int index) { + for (int i = index; i < size - 1; i++) { + data[i] = data[i + 1]; + } + data[--size] = null; + } + + public static void main(String[] args) { + Main main = new Main(); + main.size++; + main.removeElementAt(0); + if (main.size != 0) { + throw new Error("Unexpected size"); + } + } +} diff --git a/test/414-optimizing-arith-sub/expected.txt b/test/414-optimizing-arith-sub/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/414-optimizing-arith-sub/expected.txt diff --git a/test/414-optimizing-arith-sub/info.txt b/test/414-optimizing-arith-sub/info.txt new file mode 100644 index 0000000000..1eaa14887b --- /dev/null +++ b/test/414-optimizing-arith-sub/info.txt @@ -0,0 +1 @@ +Subtraction tests. diff --git a/test/414-optimizing-arith-sub/src/Main.java b/test/414-optimizing-arith-sub/src/Main.java new file mode 100644 index 0000000000..30e84368d0 --- /dev/null +++ b/test/414-optimizing-arith-sub/src/Main.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Note that $opt$ is a marker for the optimizing compiler to ensure +// it does compile the method. +public class Main { + + public static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void expectEquals(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void expectEquals(float expected, float result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void expectEquals(double expected, double result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void expectApproxEquals(float a, float b) { + float maxDelta = 0.0001F; + boolean aproxEquals = (a > b) ? ((a - b) < maxDelta) : ((b - a) < maxDelta); + if (!aproxEquals) { + throw new Error("Expected: " + a + ", found: " + b + ", with delta: " + maxDelta + " " + (a - b)); + } + } + + public static void expectApproxEquals(double a, double b) { + double maxDelta = 0.00001D; + boolean aproxEquals = (a > b) ? ((a - b) < maxDelta) : ((b - a) < maxDelta); + if (!aproxEquals) { + throw new Error("Expected: " + a + ", found: " + b + ", with delta: " + maxDelta + " " + (a - b)); + } + } + + public static void expectNaN(float a) { + if (a == a) { + throw new Error("Expected NaN: " + a); + } + } + + public static void expectNaN(double a) { + if (a == a) { + throw new Error("Expected NaN: " + a); + } + } + + public static void main(String[] args) { + subInt(); + subLong(); + subFloat(); + subDouble(); + } + + private static void subInt() { + expectEquals(2, $opt$Sub(5, 3)); + expectEquals(0, $opt$Sub(0, 0)); + expectEquals(-3, $opt$Sub(0, 3)); + expectEquals(3, $opt$Sub(3, 0)); + expectEquals(4, $opt$Sub(1, -3)); + expectEquals(-9, $opt$Sub(-12, -3)); + expectEquals(134217724, $opt$Sub(134217729, 5)); // (2^27 + 1) - 5 + } + + private static void subLong() { + expectEquals(2L, $opt$Sub(5L, 3L)); + expectEquals(0L, $opt$Sub(0L, 0L)); + expectEquals(-3L, $opt$Sub(0L, 3L)); + expectEquals(3L, $opt$Sub(3L, 0L)); + expectEquals(4L, $opt$Sub(1L, -3L)); + expectEquals(-9L, $opt$Sub(-12L, -3L)); + expectEquals(134217724L, $opt$Sub(134217729L, 5L)); // (2^27 + 1) - 5 + expectEquals(34359738362L, $opt$Sub(34359738369L, 7L)); // (2^35 + 1) - 7 + } + + private static void subFloat() { + expectApproxEquals(2F, $opt$Sub(5F, 3F)); + expectApproxEquals(0F, $opt$Sub(0F, 0F)); + expectApproxEquals(-3F, $opt$Sub(0F, 3F)); + expectApproxEquals(3F, $opt$Sub(3F, 0F)); + expectApproxEquals(4F, $opt$Sub(1F, -3F)); + expectApproxEquals(-9F, $opt$Sub(-12F, -3F)); + expectApproxEquals(34359738362F, $opt$Sub(34359738369F, 7F)); // (2^35 + 1) - 7 + expectApproxEquals(-0.1F, $opt$Sub(0.1F, 0.2F)); + expectApproxEquals(0.2F, $opt$Sub(-0.5F, -0.7F)); + + expectNaN($opt$Sub(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY)); + expectNaN($opt$Sub(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)); + expectNaN($opt$Sub(Float.NaN, 11F)); + expectNaN($opt$Sub(Float.NaN, -11F)); + expectNaN($opt$Sub(Float.NaN, Float.NEGATIVE_INFINITY)); + expectNaN($opt$Sub(Float.NaN, Float.POSITIVE_INFINITY)); + + expectEquals(Float.NEGATIVE_INFINITY, $opt$Sub(-Float.MAX_VALUE, Float.MAX_VALUE)); + expectEquals(Float.NEGATIVE_INFINITY, $opt$Sub(2F, Float.POSITIVE_INFINITY)); + expectEquals(Float.POSITIVE_INFINITY, $opt$Sub(Float.MAX_VALUE, -Float.MAX_VALUE)); + expectEquals(Float.POSITIVE_INFINITY, $opt$Sub(2F, Float.NEGATIVE_INFINITY)); + expectEquals(Float.POSITIVE_INFINITY, $opt$Sub(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY)); + expectEquals(Float.NEGATIVE_INFINITY, $opt$Sub(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY)); + } + + private static void subDouble() { + expectApproxEquals(2D, $opt$Sub(5D, 3D)); + expectApproxEquals(0D, $opt$Sub(0D, 0D)); + expectApproxEquals(-3D, $opt$Sub(0D, 3D)); + expectApproxEquals(3D, $opt$Sub(3D, 0D)); + expectApproxEquals(4D, $opt$Sub(1D, -3D)); + expectApproxEquals(-9D, $opt$Sub(-12D, -3D)); + expectApproxEquals(134217724D, $opt$Sub(134217729D, 5D)); // (2^27 + 1) - 5 + expectApproxEquals(34359738362D, $opt$Sub(34359738369D, 7D)); // (2^35 + 1) - 7 + expectApproxEquals(-0.1D, $opt$Sub(0.1D, 0.2D)); + expectApproxEquals(0.2D, $opt$Sub(-0.5D, -0.7D)); + + expectNaN($opt$Sub(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY)); + expectNaN($opt$Sub(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY)); + expectNaN($opt$Sub(Double.NaN, 11D)); + expectNaN($opt$Sub(Double.NaN, -11D)); + expectNaN($opt$Sub(Double.NaN, Double.NEGATIVE_INFINITY)); + expectNaN($opt$Sub(Double.NaN, Double.POSITIVE_INFINITY)); + + expectEquals(Double.NEGATIVE_INFINITY, $opt$Sub(-Double.MAX_VALUE, Double.MAX_VALUE)); + expectEquals(Double.NEGATIVE_INFINITY, $opt$Sub(2D, Double.POSITIVE_INFINITY)); + expectEquals(Double.POSITIVE_INFINITY, $opt$Sub(Double.MAX_VALUE, -Double.MAX_VALUE)); + expectEquals(Double.POSITIVE_INFINITY, $opt$Sub(2D, Double.NEGATIVE_INFINITY)); + expectEquals(Double.POSITIVE_INFINITY, $opt$Sub(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY)); + expectEquals(Double.NEGATIVE_INFINITY, $opt$Sub(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)); + } + + static int $opt$Sub(int a, int b) { + return a - b; + } + + static long $opt$Sub(long a, long b) { + return a - b; + } + + static float $opt$Sub(float a, float b) { + return a - b; + } + + static double $opt$Sub(double a, double b) { + return a - b; + } + +} diff --git a/test/800-smali/info.txt b/test/800-smali/info.txt index cfcc23095b..3022962ac5 100644 --- a/test/800-smali/info.txt +++ b/test/800-smali/info.txt @@ -1,4 +1,4 @@ Smali-based tests. -Will compile and run all the smali files in src/ and run the test cases mentioned in src/Main.java. +Will compile and run all the smali files in smali/ and run the test cases mentioned in src/Main.java. Obviously needs to run under Dalvik or ART. diff --git a/test/800-smali/src/b_17790197.smali b/test/800-smali/smali/b_17790197.smali index 7560fcf834..7560fcf834 100644 --- a/test/800-smali/src/b_17790197.smali +++ b/test/800-smali/smali/b_17790197.smali diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk index 2d139a64b9..55de1f3f35 100644 --- a/test/Android.libarttest.mk +++ b/test/Android.libarttest.mk @@ -58,8 +58,7 @@ define build-libarttest ifeq ($$(art_target_or_host),target) $(call set-target-local-clang-vars) $(call set-target-local-cflags-vars,debug) - LOCAL_SHARED_LIBRARIES += libdl libcutils - LOCAL_STATIC_LIBRARIES := libgtest + LOCAL_SHARED_LIBRARIES += libdl LOCAL_MULTILIB := both LOCAL_MODULE_PATH_32 := $(ART_TARGET_TEST_OUT)/$(ART_TARGET_ARCH_32) LOCAL_MODULE_PATH_64 := $(ART_TARGET_TEST_OUT)/$(ART_TARGET_ARCH_64) @@ -68,11 +67,7 @@ define build-libarttest 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 + LOCAL_LDLIBS := $(ART_HOST_LDLIBS) -ldl -lpthread LOCAL_IS_HOST_MODULE := true LOCAL_MULTILIB := both include $(BUILD_HOST_SHARED_LIBRARY) diff --git a/test/Android.libnativebridgetest.mk b/test/Android.libnativebridgetest.mk index dd7255a4c9..1b20e6965b 100644 --- a/test/Android.libnativebridgetest.mk +++ b/test/Android.libnativebridgetest.mk @@ -51,7 +51,7 @@ define build-libnativebridgetest ifeq ($$(art_target_or_host),target) $(call set-target-local-clang-vars) $(call set-target-local-cflags-vars,debug) - LOCAL_SHARED_LIBRARIES += libdl libcutils + LOCAL_SHARED_LIBRARIES += libdl LOCAL_STATIC_LIBRARIES := libgtest LOCAL_MULTILIB := both LOCAL_MODULE_PATH_32 := $(ART_TARGET_TEST_OUT)/$(ART_TARGET_ARCH_32) @@ -62,7 +62,7 @@ define build-libnativebridgetest LOCAL_CLANG := $(ART_HOST_CLANG) LOCAL_CFLAGS := $(ART_HOST_CFLAGS) $(ART_HOST_DEBUG_CFLAGS) LOCAL_STATIC_LIBRARIES := libcutils - LOCAL_LDLIBS += -ldl -lpthread + LOCAL_LDLIBS := $(ART_HOST_LDLIBS) -ldl -lpthread ifeq ($(HOST_OS),linux) LOCAL_LDLIBS += -lrt endif diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 9082b47811..682282532b 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -52,7 +52,7 @@ $(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call define-build-art-run-test,$ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_MODULE := art-run-tests -LOCAL_ADDITIONAL_DEPENDENCIES := $(TEST_ART_RUN_TEST_BUILD_RULES) smali dexmerger +LOCAL_ADDITIONAL_DEPENDENCIES := $(TEST_ART_RUN_TEST_BUILD_RULES) # The build system use this flag to pick up files generated by declare-make-art-run-test. LOCAL_PICKUP_FILES := $(art_run_tests_dir) @@ -66,11 +66,6 @@ TEST_ART_RUN_TEST_BUILD_RULES := ######################################################################## # General rules to build and run a run-test. -# Test rule names or of the form: -# test-art-{1: host or target}-run-test-{2: prebuild no-prebuild no-dex2oat}- -# {3: interpreter default optimizing}-{4: relocate no-relocate relocate-no-patchoat}- -# {5: trace or no-trace}-{6: gcstress gcverify cms}-{7: forcecopy checkjni jni}- -# {8: no-image or image}-{9: test name}{10: 32 or 64} TARGET_TYPES := host target PREBUILD_TYPES := prebuild ifeq ($(ART_TEST_RUN_TEST_NO_PREBUILD),true) @@ -115,6 +110,13 @@ IMAGE_TYPES := image ifeq ($(ART_TEST_RUN_TEST_NO_IMAGE),true) IMAGE_TYPES += no-image endif +RUN_TYPES := +ifeq ($(ART_TEST_RUN_TEST_DEBUG),true) + RUN_TYPES += debug +endif +ifeq ($(ART_TEST_RUN_TEST_NDEBUG),true) + RUN_TYPES += ndebug +endif ADDRESS_SIZES_TARGET := $(ART_PHONY_TEST_TARGET_SUFFIX) $(2ND_ART_PHONY_TEST_TARGET_SUFFIX) ADDRESS_SIZES_HOST := $(ART_PHONY_TEST_HOST_SUFFIX) $(2ND_ART_PHONY_TEST_HOST_SUFFIX) ALL_ADDRESS_SIZES := 64 32 @@ -122,21 +124,22 @@ ALL_ADDRESS_SIZES := 64 32 # List all run test names with number arguments agreeing with the comment above. define all-run-test-names $(foreach target, $(1), \ - $(foreach prebuild, $(2), \ - $(foreach compiler, $(3), \ - $(foreach relocate, $(4), \ - $(foreach trace, $(5), \ - $(foreach gc, $(6), \ - $(foreach jni, $(7), \ - $(foreach image, $(8), \ - $(foreach test, $(9), \ - $(foreach address_size, $(10), \ - test-art-$(target)-run-test-$(prebuild)-$(compiler)-$(relocate)-$(trace)-$(gc)-$(jni)-$(image)-$(test)$(address_size) \ - )))))))))) + $(foreach run-type, $(2), \ + $(foreach prebuild, $(3), \ + $(foreach compiler, $(4), \ + $(foreach relocate, $(5), \ + $(foreach trace, $(6), \ + $(foreach gc, $(7), \ + $(foreach jni, $(8), \ + $(foreach image, $(9), \ + $(foreach test, $(10), \ + $(foreach address_size, $(11), \ + test-art-$(target)-run-test-$(run-type)-$(prebuild)-$(compiler)-$(relocate)-$(trace)-$(gc)-$(jni)-$(image)-$(test)$(address_size) \ + ))))))))))) endef # all-run-test-names # To generate a full list or tests: -# $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES),$(COMPILER_TYPES), \ +# $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES),$(COMPILER_TYPES), \ # $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \ # $(TEST_ART_RUN_TESTS), $(ALL_ADDRESS_SIZES)) @@ -152,7 +155,7 @@ TEST_ART_TIMING_SENSITIVE_RUN_TESTS := \ # disable timing sensitive tests on "dist" builds. ifdef dist_goal - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \ + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ $(IMAGE_TYPES), $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(ALL_ADDRESS_SIZES)) endif @@ -162,7 +165,7 @@ TEST_ART_TIMING_SENSITIVE_RUN_TESTS := TEST_ART_BROKEN_RUN_TESTS := \ 004-ThreadStress -ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \ +ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ $(IMAGE_TYPES), $(TEST_ART_BROKEN_RUN_TESTS), $(ALL_ADDRESS_SIZES)) @@ -173,7 +176,7 @@ TEST_ART_BROKEN_PREBUILD_RUN_TESTS := \ 116-nodex2oat ifneq (,$(filter prebuild,$(PREBUILD_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),prebuild, \ + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),prebuild, \ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ $(IMAGE_TYPES), $(TEST_ART_BROKEN_PREBUILD_RUN_TESTS), $(ALL_ADDRESS_SIZES)) endif @@ -184,7 +187,7 @@ TEST_ART_BROKEN_NO_PREBUILD_TESTS := \ 117-nopatchoat ifneq (,$(filter no-prebuild,$(PREBUILD_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),no-prebuild, \ + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),no-prebuild, \ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ $(IMAGE_TYPES), $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(ALL_ADDRESS_SIZES)) endif @@ -197,7 +200,7 @@ TEST_ART_BROKEN_NO_RELOCATE_TESTS := \ 117-nopatchoat ifneq (,$(filter no-relocate,$(RELOCATE_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \ + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ $(COMPILER_TYPES), no-relocate,$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ $(IMAGE_TYPES), $(TEST_ART_BROKEN_NO_RELOCATE_TESTS), $(ALL_ADDRESS_SIZES)) endif @@ -210,7 +213,7 @@ TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \ 114-ParallelGC ifneq (,$(filter gcstress,$(GC_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \ + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),gcstress,$(JNI_TYPES), \ $(IMAGE_TYPES), $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(ALL_ADDRESS_SIZES)) endif @@ -218,7 +221,7 @@ endif TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := # 115-native-bridge setup is complicated. Need to implement it correctly for the target. -ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(PREBUILD_TYPES),$(COMPILER_TYPES), \ +ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES),$(COMPILER_TYPES), \ $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES),115-native-bridge, \ $(ALL_ADDRESS_SIZES)) @@ -232,26 +235,100 @@ TEST_ART_BROKEN_FALLBACK_RUN_TESTS := \ 119-noimage-patchoat ifneq (,$(filter no-dex2oat,$(PREBUILD_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),no-dex2oat, \ + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),no-dex2oat, \ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \ $(TEST_ART_BROKEN_FALLBACK_RUN_TESTS),$(ALL_ADDRESS_SIZES)) endif ifneq (,$(filter no-image,$(IMAGE_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \ + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),no-image, \ $(TEST_ART_BROKEN_FALLBACK_RUN_TESTS),$(ALL_ADDRESS_SIZES)) endif ifneq (,$(filter relocate-no-patchoat,$(RELOCATE_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \ + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ $(COMPILER_TYPES), relocate-no-patchoat,$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ $(IMAGE_TYPES),$(TEST_ART_BROKEN_FALLBACK_RUN_TESTS),$(ALL_ADDRESS_SIZES)) endif TEST_ART_BROKEN_FALLBACK_RUN_TESTS := +# The following tests use libarttest.so, which is linked against libartd.so, so will +# not work when libart.so is the one loaded. +# TODO: Find a way to run these tests in ndebug mode. +TEST_ART_BROKEN_NDEBUG_TESTS := \ + 004-JniTest \ + 004-ReferenceMap \ + 004-SignalTest \ + 004-StackWalk \ + 004-UnsafeTest \ + 115-native-bridge \ + 116-nodex2oat \ + 117-nopatchoat \ + 118-noimage-dex2oat \ + 119-noimage-patchoat \ + +ifneq (,$(filter ndebug,$(RUN_TYPES))) + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),ndebug,$(PREBUILD_TYPES), \ + $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \ + $(TEST_ART_BROKEN_NDEBUG_TESTS),$(ALL_ADDRESS_SIZES)) +endif + +TEST_ART_BROKEN_NDEBUG_TESTS := + +# Known broken tests for the default compiler (Quick). +TEST_ART_BROKEN_DEFAULT_RUN_TESTS := \ + 412-new-array + +ifneq (,$(filter default,$(COMPILER_TYPES))) + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ + default,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ + $(IMAGE_TYPES),$(TEST_ART_BROKEN_DEFAULT_RUN_TESTS),$(ALL_ADDRESS_SIZES)) +endif + +TEST_ART_BROKEN_DEFAULT_RUN_TESTS := + +# Known broken tests for the arm64 optimizing compiler backend. +TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS := \ + 003-omnibus-opcodes \ + 006-args \ + 011-array-copy \ + 018-stack-overflow \ + 036-finalizer \ + 044-proxy \ + 070-nio-buffer \ + 072-precise-gc \ + 082-inline-execute \ + 083-compiler-regressions \ + 093-serialization \ + 096-array-copy-concurrent-gc \ + 100-reflect2 \ + 106-exceptions2 \ + 107-int-math2 \ + 121-modifiers \ + 122-npe \ + 123-compiler-regressions-mt \ + 405-optimizing-long-allocator \ + 407-arrays \ + 410-floats \ + 411-optimizing-arith \ + 412-new-array \ + 413-regalloc-regression \ + 414-optimizing-arith-sub \ + 700-LoadArgRegs \ + 800-smali + +ifneq (,$(filter optimizing,$(COMPILER_TYPES))) + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ + optimizing,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ + $(IMAGE_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS),64) +endif + +TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS := + + # Clear variables ahead of appending to them when defining tests. $(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=)) $(foreach target, $(TARGET_TYPES), \ @@ -281,6 +358,9 @@ $(foreach target, $(TARGET_TYPES), \ $(foreach target, $(TARGET_TYPES), \ $(foreach address_size, $(ALL_ADDRESS_SIZES), \ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(address_size))_RULES :=))) +$(foreach target, $(TARGET_TYPES), \ + $(foreach run_type, $(RUN_TYPES), \ + $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(run_type))_RULES :=))) # We need dex2oat and dalvikvm on the target as well as the core image. TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUT) $(2ND_TARGET_CORE_IMG_OUT) @@ -314,10 +394,10 @@ ART_TEST_HOST_RUN_TEST_DEPENDENCIES += \ endif # Create a rule to build and run a tests following the form: -# test-art-{1: host or target}-run-test-{2: prebuild no-prebuild no-dex2oat}- -# {3: interpreter default optimizing}-{4: relocate no-relocate relocate-no-patchoat}- -# {5: trace or no-trace}-{6: gcstress gcverify cms}-{7: forcecopy checkjni jni}- -# {8: no-image image}-{9: test name}{10: 32 or 64} +# test-art-{1: host or target}-run-test-{2: debug ndebug}-{3: prebuild no-prebuild no-dex2oat}- +# {4: interpreter default optimizing}-{5: relocate no-relocate relocate-no-patchoat}- +# {6: trace or no-trace}-{7: gcstress gcverify cms}-{8: forcecopy checkjni jni}- +# {9: no-image image}-{10: test name}{11: 32 or 64} define define-test-art-run-test run_test_options := prereq_rule := @@ -340,119 +420,129 @@ define define-test-art-run-test $$(error found $(1) expected $(TARGET_TYPES)) endif endif - ifeq ($(2),prebuild) + ifeq ($(2),debug) + test_groups += ART_RUN_TEST_$$(uc_host_or_target)_DEBUG_RULES + else + ifeq ($(2),ndebug) + test_groups += ART_RUN_TEST_$$(uc_host_or_target)_RELEASE_RULES + run_test_options += -O + else + $$(error found $(2) expected $(RUN_TYPES)) + endif + endif + ifeq ($(3),prebuild) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_PREBUILD_RULES run_test_options += --prebuild else - ifeq ($(2),no-prebuild) + ifeq ($(3),no-prebuild) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_PREBUILD_RULES run_test_options += --no-prebuild else - ifeq ($(2),no-dex2oat) + ifeq ($(3),no-dex2oat) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_DEX2OAT_RULES run_test_options += --no-prebuild --no-dex2oat else - $$(error found $(2) expected $(PREBUILD_TYPES)) + $$(error found $(3) expected $(PREBUILD_TYPES)) endif endif endif - ifeq ($(3),optimizing) + ifeq ($(4),optimizing) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_OPTIMIZING_RULES run_test_options += -Xcompiler-option --compiler-backend=Optimizing else - ifeq ($(3),interpreter) + ifeq ($(4),interpreter) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_INTERPRETER_RULES run_test_options += --interpreter else - ifeq ($(3),default) + ifeq ($(4),default) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_DEFAULT_RULES else - $$(error found $(3) expected $(COMPILER_TYPES)) + $$(error found $(4) expected $(COMPILER_TYPES)) endif endif endif - ifeq ($(4),relocate) + ifeq ($(5),relocate) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_RELOCATE_RULES run_test_options += --relocate else - ifeq ($(4),no-relocate) + ifeq ($(5),no-relocate) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_RELOCATE_RULES run_test_options += --no-relocate else - ifeq ($(4),relocate-no-patchoat) + ifeq ($(5),relocate-no-patchoat) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_RELOCATE_NO_PATCHOAT_RULES run_test_options += --relocate --no-patchoat else - $$(error found $(4) expected $(RELOCATE_TYPES)) + $$(error found $(5) expected $(RELOCATE_TYPES)) endif endif endif - ifeq ($(5),trace) + ifeq ($(6),trace) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_TRACE_RULES run_test_options += --trace else - ifeq ($(5),no-trace) + ifeq ($(6),no-trace) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_TRACE_RULES else - $$(error found $(5) expected $(TRACE_TYPES)) + $$(error found $(6) expected $(TRACE_TYPES)) endif endif - ifeq ($(6),gcverify) + ifeq ($(7),gcverify) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_GCVERIFY_RULES run_test_options += --gcverify else - ifeq ($(6),gcstress) + ifeq ($(7),gcstress) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_GCSTRESS_RULES run_test_options += --gcstress else - ifeq ($(6),cms) + ifeq ($(7),cms) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_CMS_RULES else - $$(error found $(6) expected $(GC_TYPES)) + $$(error found $(7) expected $(GC_TYPES)) endif endif endif - ifeq ($(7),forcecopy) + ifeq ($(8),forcecopy) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_FORCECOPY_RULES run_test_options += --runtime-option -Xjniopts:forcecopy ifneq ($$(ART_TEST_JNI_FORCECOPY),true) skip_test := true endif else - ifeq ($(7),checkjni) + ifeq ($(8),checkjni) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_CHECKJNI_RULES run_test_options += --runtime-option -Xcheck:jni else - ifeq ($(7),jni) + ifeq ($(8),jni) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_JNI_RULES else - $$(error found $(7) expected $(JNI_TYPES)) + $$(error found $(8) expected $(JNI_TYPES)) endif endif endif - ifeq ($(8),no-image) + ifeq ($(9),no-image) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_IMAGE_RULES run_test_options += --no-image else - ifeq ($(8),image) + ifeq ($(9),image) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_IMAGE_RULES else - $$(error found $(8) expected $(IMAGE_TYPES)) + $$(error found $(9) expected $(IMAGE_TYPES)) endif endif - # $(9) is the test name - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_$(call name-to-var,$(9))_RULES - ifeq ($(10),64) + # $(10) is the test name + test_groups += ART_RUN_TEST_$$(uc_host_or_target)_$(call name-to-var,$(10))_RULES + ifeq ($(11),64) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_64_RULES run_test_options += --64 else - ifeq ($(10),32) + ifeq ($(11),32) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_32_RULES else - $$(error found $(10) expected $(ALL_ADDRESS_SIZES)) + $$(error found $(11) expected $(ALL_ADDRESS_SIZES)) endif endif - run_test_rule_name := test-art-$(1)-run-test-$(2)-$(3)-$(4)-$(5)-$(6)-$(7)-$(8)-$(9)$(10) + run_test_rule_name := test-art-$(1)-run-test-$(2)-$(3)-$(4)-$(5)-$(6)-$(7)-$(8)-$(9)-$(10)$(11) run_test_options := --output-path $(ART_HOST_TEST_DIR)/run-test-output/$$(run_test_rule_name) \ $$(run_test_options) $$(run_test_rule_name): PRIVATE_RUN_TEST_OPTIONS := $$(run_test_options) @@ -462,7 +552,7 @@ $$(run_test_rule_name): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $(HOST_OUT_EXECUTAB DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) \ SMALI=$(abspath $(HOST_OUT_EXECUTABLES)/smali) \ DXMERGER=$(abspath $(HOST_OUT_EXECUTABLES)/dexmerger) \ - art/test/run-test $$(PRIVATE_RUN_TEST_OPTIONS) $(9) \ + art/test/run-test $$(PRIVATE_RUN_TEST_OPTIONS) $(10) \ && $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@) $$(hide) (echo $(MAKECMDGOALS) | grep -q $$@ && \ echo "run-test run as top-level target, removing test directory $(ART_HOST_TEST_DIR)" && \ @@ -480,16 +570,17 @@ endef # define-test-art-run-test $(foreach target, $(TARGET_TYPES), \ $(foreach test, $(TEST_ART_RUN_TESTS), \ - $(foreach address_size, $(ADDRESS_SIZES_$(call name-to-var,$(target))), \ - $(foreach prebuild, $(PREBUILD_TYPES), \ - $(foreach compiler, $(COMPILER_TYPES), \ - $(foreach relocate, $(RELOCATE_TYPES), \ - $(foreach trace, $(TRACE_TYPES), \ - $(foreach gc, $(GC_TYPES), \ - $(foreach jni, $(JNI_TYPES), \ - $(foreach image, $(IMAGE_TYPES), \ - $(eval $(call define-test-art-run-test,$(target),$(prebuild),$(compiler),$(relocate),$(trace),$(gc),$(jni),$(image),$(test),$(address_size))) \ - )))))))))) + $(foreach run_type, $(RUN_TYPES), \ + $(foreach address_size, $(ADDRESS_SIZES_$(call name-to-var,$(target))), \ + $(foreach prebuild, $(PREBUILD_TYPES), \ + $(foreach compiler, $(COMPILER_TYPES), \ + $(foreach relocate, $(RELOCATE_TYPES), \ + $(foreach trace, $(TRACE_TYPES), \ + $(foreach gc, $(GC_TYPES), \ + $(foreach jni, $(JNI_TYPES), \ + $(foreach image, $(IMAGE_TYPES), \ + $(eval $(call define-test-art-run-test,$(target),$(run_type),$(prebuild),$(compiler),$(relocate),$(trace),$(gc),$(jni),$(image),$(test),$(address_size))) \ + ))))))))))) define-test-art-run-test := # Define a phony rule whose purpose is to test its prerequisites. @@ -509,6 +600,9 @@ $(foreach target, $(TARGET_TYPES), \ $(foreach prebuild, $(PREBUILD_TYPES), $(eval \ $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(prebuild),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(prebuild))_RULES))))) $(foreach target, $(TARGET_TYPES), \ + $(foreach run-type, $(RUN_TYPES), $(eval \ + $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(run-type),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(run-type))_RULES))))) +$(foreach target, $(TARGET_TYPES), \ $(foreach compiler, $(COMPILER_TYPES), $(eval \ $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(compiler),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(compiler))_RULES))))) $(foreach target, $(TARGET_TYPES), \ @@ -562,6 +656,9 @@ $(foreach target, $(TARGET_TYPES), \ $(foreach target, $(TARGET_TYPES), \ $(foreach address_size, $(ALL_ADDRESS_SIZES), \ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(address_size))_RULES :=))) +$(foreach target, $(TARGET_TYPES), \ + $(foreach run_type, $(RUN_TYPES), \ + $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(run_type))_RULES :=))) define-test-art-run-test-group := TARGET_TYPES := PREBUILD_TYPES := @@ -574,6 +671,7 @@ IMAGE_TYPES := ADDRESS_SIZES_TARGET := ADDRESS_SIZES_HOST := ALL_ADDRESS_SIZES := +RUN_TYPES := include $(LOCAL_PATH)/Android.libarttest.mk include art/test/Android.libnativebridgetest.mk diff --git a/test/etc/default-build b/test/etc/default-build index faafc1fb29..009736b097 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -26,7 +26,12 @@ fi if [ ${NEED_DEX} = "true" ]; then ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes - zip $TEST_NAME.jar classes.dex +fi + +if [ -r smali ]; then + # Compile Smali classes + ${SMALI} -JXmx256m --output smali_classes.dex `find smali -name '*.smali'` + ${DXMERGER} classes.dex classes.dex smali_classes.dex fi if [ -r src-ex ]; then @@ -43,3 +48,5 @@ if [ -r src-ex ]; then mv classes-1.dex classes.dex fi fi + +zip $TEST_NAME.jar classes.dex diff --git a/test/etc/reference-run-test-classes b/test/etc/reference-run-test-classes deleted file mode 100755 index 6f10f5a82c..0000000000 --- a/test/etc/reference-run-test-classes +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/sh -# -# Run the code in a classes directory on a host-local reference virtual -# machine. The jar should contain a top-level class named Main to run. -# -# Options: -# --quiet -- don't chatter -# --debug -- wait for debugger to attach -# --no-verify -- turn off verification (on by default) -# --dev -- development mode - -msg() { - if [ "$QUIET" = "n" ]; then - echo "$@" - fi -} - -DEBUG="n" -QUIET="n" -VERIFY="y" - -while true; do - if [ "x$1" = "x--quiet" ]; then - QUIET="y" - shift - elif [ "x$1" = "x--debug" ]; then - DEBUG="y" - shift - elif [ "x$1" = "x--no-verify" ]; then - VERIFY="n" - shift - elif [ "x$1" = "x--dev" ]; then - # not used; ignore - shift - elif [ "x$1" = "x--" ]; then - shift - break - elif expr "x$1" : "x--" >/dev/null 2>&1; then - echo "unknown $0 option: $1" 1>&2 - exit 1 - else - break - fi -done - -if [ "$VERIFY" = "y" ]; then - VERIFY_ARG="-Xverify:all" - msg "Performing verification" -else - VERIFY_ARG="-Xverify:none" - msg "Skipping verification" -fi - -if [ "$DEBUG" = "y" ]; then - PORT=8000 - msg "Waiting for jdb to connect:" - msg " jdb -attach localhost:$PORT" - DEBUG_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y" -fi - -${JAVA} ${DEBUG_OPTS} ${VERIFY_ARG} -classpath classes Main "$@" diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 9a32ec7a32..82d47d7451 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -32,6 +32,7 @@ PREBUILD="y" QUIET="n" RELOCATE="y" USE_GDB="n" +USE_JVM="n" VERIFY="y" ZYGOTE="" MAIN="" @@ -105,6 +106,9 @@ while true; do elif [ "x$1" = "x--interpreter" ]; then INTERPRETER="y" shift + elif [ "x$1" = "x--jvm" ]; then + USE_JVM="y" + shift elif [ "x$1" = "x--invoke-with" ]; then shift if [ "x$1" = "x" ]; then @@ -161,19 +165,17 @@ if [ "$ZYGOTE" = "" ]; then if [ "$VERIFY" = "y" ]; then DEX_VERIFY="" + JVM_VERIFY_ARG="-Xverify:all" msg "Performing verification" else DEX_VERIFY="-Xverify:none" + JVM_VERIFY_ARG="-Xverify:none" msg "Skipping verification" fi fi msg "------------------------------" -if [ "$HAVE_IMAGE" = "n" ]; then - BOOT_OPT="-Ximage:/system/non-existant/core.art" -fi - if [ "$DEBUGGER" = "y" ]; then # Use this instead for ddms and connect by running 'ddms': # DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_android_adb,server=y,suspend=y" @@ -188,6 +190,17 @@ if [ "$DEBUGGER" = "y" ]; then DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y" fi +if [ "$USE_JVM" = "y" ]; then + ${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -classpath classes $MAIN "$@" + exit +fi + + +if [ "$HAVE_IMAGE" = "n" ]; then + BOOT_OPT="-Ximage:/system/non-existant/core.art" +fi + + if [ "$USE_GDB" = "y" ]; then if [ "$HOST" = "n" ]; then GDB="$GDB_SERVER :5039" @@ -341,12 +354,9 @@ else if [ "$USE_GDB" = "y" ]; then # When running under gdb, we cannot do piping and grepping... - LD_PRELOAD=libsigchain.so $cmdline "$@" + $cmdline "$@" else - # If we are execing /bin/false we might not be on the same ISA as libsigchain.so - # ld.so will helpfully warn us of this. Unfortunately this messes up our error - # checking so we will just filter out the error with a grep. - LD_PRELOAD=libsigchain.so $cmdline "$@" 2>&1 | grep -v -E "^ERROR: ld\.so: object '.+\.so' from LD_PRELOAD cannot be preloaded.*: ignored\.$" + $cmdline "$@" 2>&1 # Add extra detail if time out is enabled. if [ ${PIPESTATUS[0]} = 124 ] && [ "$TIME_OUT" = "y" ]; then echo -e "\e[91mTEST TIMED OUT!\e[0m" >&2 diff --git a/test/run-test b/test/run-test index 3e98d6d09a..2ef3ab189a 100755 --- a/test/run-test +++ b/test/run-test @@ -17,6 +17,7 @@ # Set up prog to be the path of this script, including following symlinks, # and set up progdir to be the fully-qualified pathname of its directory. prog="$0" +args="$@" while [ -h "${prog}" ]; do newProg=`/bin/ls -ld "${prog}"` newProg=`expr "${newProg}" : ".* -> \(.*\)$"` @@ -106,6 +107,7 @@ while true; do runtime="jvm" prebuild_mode="no" NEED_DEX="false" + run_args="${run_args} --jvm" shift elif [ "x$1" = "x-O" ]; then lib="libart.so" @@ -270,7 +272,6 @@ function guess_arch_name() { if [ "$target_mode" = "no" ]; then if [ "$runtime" = "jvm" ]; then - RUN="${progdir}/etc/reference-run-test-classes" if [ "$prebuild_mode" = "yes" ]; then echo "--prebuild with --jvm is unsupported"; exit 1; @@ -468,9 +469,6 @@ if echo "$test_dir" | grep 089; then file_size_limit=5120 elif echo "$test_dir" | grep 083; then file_size_limit=5120 -elif echo "$test_dir" | grep 115; then -# Native bridge test copies libarttest.so into its directory, which needs 2MB already. - file_size_limit=5120 fi if ! ulimit -S "$file_size_limit"; then echo "ulimit file size setting failed" @@ -526,9 +524,10 @@ else "./${run}" $run_args "$@" >"$output" 2>&1 else cp "$build_output" "$output" - echo "Failed to build in tmpdir=${tmp_dir} from oldwd=${oldwd} and cwd=`pwd`" - echo "Non-canonical tmpdir was ${noncanonical_tmp_dir}" - echo "build exit status: $build_exit" >>"$output" + echo "Failed to build in tmpdir=${tmp_dir} from oldwd=${oldwd} and cwd=`pwd`" >> "$output" + echo "Non-canonical tmpdir was ${noncanonical_tmp_dir}" >> "$output" + echo "Args: ${args}" >> "output" + echo "build exit status: $build_exit" >> "$output" fi ./$check_cmd "$expected" "$output" if [ "$?" = "0" ]; then @@ -53,6 +53,12 @@ while true; do elif [ "$1" = "--64" ]; then DALVIKVM=dalvikvm64 shift + elif [ "$1" = "--perf" ]; then + PERF="record" + shift + elif [ "$1" = "--perf-report" ]; then + PERF="report" + shift elif expr "$1" : "--" >/dev/null 2>&1; then echo "unknown option: $1" 1>&2 exit 1 @@ -68,6 +74,11 @@ ANDROID_DATA=$PWD/android-data$$ LIBDIR=$(find_libdir) LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBDIR + +if [ z"$PERF" != z ]; then + invoke_with="perf record -o $ANDROID_DATA/perf.data -e cycles:u $invoke_with" +fi + mkdir -p $ANDROID_DATA/dalvik-cache/{arm,arm64,x86,x86_64} ANDROID_DATA=$ANDROID_DATA \ ANDROID_ROOT=$ANDROID_ROOT \ @@ -75,7 +86,18 @@ ANDROID_DATA=$ANDROID_DATA \ $invoke_with $ANDROID_ROOT/bin/$DALVIKVM $lib \ -XXlib:$LIBART \ -Ximage:$ANDROID_ROOT/framework/core.art \ - "$@" + -Xcompiler-option --include-debug-symbols \ + "$@" + EXIT_STATUS=$? -rm -rf $ANDROID_DATA + +if [ z"$PERF" != z ]; then + if [ z"$PERF" = zreport ]; then + perf report -i $ANDROID_DATA/perf.data + fi + echo "Perf data saved in: $ANDROID_DATA/perf.data" +else + rm -rf $ANDROID_DATA +fi + exit $EXIT_STATUS |