diff options
168 files changed, 3875 insertions, 1833 deletions
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index 14edb716bf..0e930ee207 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 @@ -131,6 +131,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 +180,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 +193,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 +204,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,27 +223,22 @@ 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 += -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_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 endif @@ -230,25 +257,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 +268,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 +311,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 +333,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.executable.mk b/build/Android.executable.mk index e36b006173..81f329751f 100644 --- a/build/Android.executable.mk +++ b/build/Android.executable.mk @@ -65,13 +65,18 @@ define build-art-executable endif LOCAL_CFLAGS := $(ART_EXECUTABLES_CFLAGS) - LOCAL_LDFLAGS := -Wl,--version-script,art/sigchainlib/version-script.txt -Wl,--export-dynamic + # 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) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 3e100e95ca..38d3f1cee5 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -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) @@ -377,7 +374,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/compiler/Android.mk b/compiler/Android.mk index edc5bd0f53..f41357604f 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -223,6 +223,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..fba0863697 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 { @@ -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.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/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc index eb9891606c..0fb5e48851 100644 --- a/compiler/dex/local_value_numbering.cc +++ b/compiler/dex/local_value_numbering.cc @@ -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/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..8dded79aa2 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)), @@ -2224,7 +2225,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 +2240,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 +2262,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 +2375,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..80303f6752 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? @@ -1015,6 +1013,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 +1152,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 +1252,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_; @@ -1306,7 +1313,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 84c056daf3..00528e5f4b 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -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,100 +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. - // TODO: Rewrite the pass to converge first before doing any modifications so that - // we don't lose the MIR_IGNORE_NULL_CHECK here if previously set by some other pass. - mir->optimization_flags &= ~MIR_IGNORE_NULL_CHECK; - // 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); } } @@ -947,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); } } } @@ -1016,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; } @@ -1036,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; + } + } } /* @@ -1100,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; } } } @@ -1148,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; } @@ -1163,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 { @@ -1172,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; } 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_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/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/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..e1b5984c7b 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), @@ -920,6 +920,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 +1116,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 +1128,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 +1143,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) { @@ -2002,6 +2006,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..3d59ef1e80 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; diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index eb3de975d2..3a50bfdb83 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -28,7 +28,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,7 +59,8 @@ 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) #endif @@ -77,7 +78,8 @@ 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 #endif @@ -94,7 +96,8 @@ 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) #endif @@ -197,6 +200,11 @@ class CompilerOptions { return include_patch_information_; } + // Should the code be compiled as position independent? + bool GetCompilePic() const { + return compile_pic_; + } + private: CompilerFilter compiler_filter_; size_t huge_method_threshold_; @@ -212,6 +220,7 @@ class CompilerOptions { bool implicit_null_checks_; bool implicit_so_checks_; bool implicit_suspend_checks_; + bool compile_pic_; #ifdef ART_SEA_IR_MODE bool sea_ir_mode_; #endif 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/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..2648d4d670 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -713,6 +713,16 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 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::ADD_LONG_2ADDR: { Binop_12x<HAdd>(instruction, Primitive::kPrimLong); break; @@ -738,6 +748,16 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 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::ADD_INT_LIT16: { Binop_22s<HAdd>(instruction, false); break; @@ -748,6 +768,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 +783,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())); diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 29dbd8b33d..408e13e36d 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -36,7 +36,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 +54,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 +76,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 +273,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) { diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 4eba791723..7aaf99108f 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; @@ -167,7 +161,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 +198,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 9be780216a..a2cf670b0f 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_); @@ -205,6 +220,7 @@ void CodeGeneratorARM::RestoreCoreRegister(Location stack_location, uint32_t reg 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 +236,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 +287,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 +310,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 +334,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 +360,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 { @@ -641,36 +662,51 @@ void LocationsBuilderARM::VisitIf(HIf* if_instr) { 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())); } } @@ -810,6 +846,7 @@ void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) { } void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant) { + // Will be generated at use site. } void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) { @@ -1107,6 +1144,82 @@ void InstructionCodeGeneratorARM::VisitSub(HSub* sub) { } } +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(), Location::kDiesAtEntry); + locations->SetInAt(1, Location::RequiresRegister(), Location::kDiesAtEntry); + locations->SetOut(Location::RequiresRegister()); + break; + } + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented 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::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented mul type " << mul->GetResultType(); + } +} + void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); @@ -1349,7 +1462,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(); @@ -1595,7 +1708,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); diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 874db0fd54..57b289c801 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -140,7 +140,7 @@ 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; @@ -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_x86.cc b/compiler/optimizing/code_generator_x86.cc index 34fa46efd0..041acdf91e 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_)); } } @@ -177,6 +192,7 @@ void CodeGeneratorX86::RestoreCoreRegister(Location stack_location, uint32_t reg 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 +207,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 +258,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 +293,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 +307,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) { @@ -577,42 +594,60 @@ void LocationsBuilderX86::VisitIf(HIf* if_instr) { 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())); } } @@ -747,6 +782,7 @@ void LocationsBuilderX86::VisitIntConstant(HIntConstant* constant) { } void InstructionCodeGeneratorX86::VisitIntConstant(HIntConstant* constant) { + // Will be generated at use site. } void LocationsBuilderX86::VisitLongConstant(HLongConstant* constant) { @@ -1083,6 +1119,113 @@ void InstructionCodeGeneratorX86::VisitSub(HSub* sub) { } } +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::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented 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::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented mul type " << mul->GetResultType(); + } +} + void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); @@ -1356,7 +1499,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(); @@ -1658,7 +1801,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..db8b9abd91 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -142,7 +142,7 @@ 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; @@ -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 059140d9bf..5fa930512b 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_); @@ -186,6 +201,7 @@ void CodeGeneratorX86_64::RestoreCoreRegister(Location stack_location, uint32_t 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 +282,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 +298,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) { @@ -479,40 +495,59 @@ void LocationsBuilderX86_64::VisitIf(HIf* if_instr) { 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())); } } @@ -679,6 +714,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 +724,7 @@ void LocationsBuilderX86_64::VisitLongConstant(HLongConstant* constant) { } void InstructionCodeGeneratorX86_64::VisitLongConstant(HLongConstant* constant) { + // Will be generated at use site. } void LocationsBuilderX86_64::VisitReturnVoid(HReturnVoid* ret) { @@ -1043,6 +1080,70 @@ void InstructionCodeGeneratorX86_64::VisitSub(HSub* sub) { } } +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::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented 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::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented mul type " << mul->GetResultType(); + } +} + void LocationsBuilderX86_64::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); @@ -1233,7 +1334,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(); @@ -1505,7 +1606,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); diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 288f3f61f9..5ac0189b55 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -144,7 +144,7 @@ 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; @@ -188,7 +188,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..8bb12de387 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -349,4 +349,49 @@ 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); \ + } + +MUL_TEST(INT, MulInt); +MUL_TEST(LONG, MulLong); +// MUL_TEST(FLOAT, Float); +// MUL_TEST(DOUBLE, Double); + +TEST(CodegenTest, ReturnMulIntLit8) { + 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); +} + +TEST(CodegenTest, ReturnMulIntLit16) { + 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); +} + + } // namespace art diff --git a/compiler/optimizing/constant_propagation_test.cc b/compiler/optimizing/constant_propagation_test.cc index 342777a49c..ff44805ed2 100644 --- a/compiler/optimizing/constant_propagation_test.cc +++ b/compiler/optimizing/constant_propagation_test.cc @@ -62,7 +62,7 @@ static void TestCode(const uint16_t* data, ASSERT_EQ(expected_after_dce, actual_after_dce); SSAChecker ssa_checker(&allocator, graph); - ssa_checker.VisitInsertionOrder(); + ssa_checker.Run(); ASSERT_TRUE(ssa_checker.IsValid()); } diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc index fe2adc77d0..5655544427 100644 --- a/compiler/optimizing/dead_code_elimination.cc +++ b/compiler/optimizing/dead_code_elimination.cc @@ -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_test.cc b/compiler/optimizing/dead_code_elimination_test.cc index 245bcb21d5..3e0ba3aee3 100644 --- a/compiler/optimizing/dead_code_elimination_test.cc +++ b/compiler/optimizing/dead_code_elimination_test.cc @@ -47,7 +47,7 @@ static void TestCode(const uint16_t* data, ASSERT_EQ(actual_after, expected_after); SSAChecker ssa_checker(&allocator, graph); - ssa_checker.VisitInsertionOrder(); + ssa_checker.Run(); ASSERT_TRUE(ssa_checker.IsValid()); } diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 589b44a167..9f4029785a 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->Dominates(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->Dominates(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..862f1b600b 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -29,6 +29,9 @@ class GraphChecker : public HGraphVisitor { allocator_(allocator), errors_(allocator, 0) {} + // Check the whole graph (in insertion order). + virtual void Run() { VisitInsertionOrder(); } + // Check `block`. virtual void VisitBasicBlock(HBasicBlock* block) OVERRIDE; @@ -65,6 +68,14 @@ class SSAChecker : public GraphChecker { SSAChecker(ArenaAllocator* allocator, HGraph* graph) : GraphChecker(allocator, graph) {} + // 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; // Loop-related checks from block `loop_header`. 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/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/nodes.cc b/compiler/optimizing/nodes.cc index a058dea6b4..aee21770b7 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -553,6 +553,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); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 677a4f8591..ec26c4a4dc 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -502,11 +502,12 @@ class HBasicBlock : public ArenaObject { M(NullCheck, Instruction) \ M(Temporary, Instruction) \ M(SuspendCheck, Instruction) \ + M(Mul, BinaryOperation) \ #define FOR_EACH_INSTRUCTION(M) \ FOR_EACH_CONCRETE_INSTRUCTION(M) \ M(Constant, Instruction) \ - M(BinaryOperation, Instruction) \ + M(BinaryOperation, Instruction) \ M(Invoke, Instruction) #define FORWARD_DECLARATION(type, super) class H##type; @@ -650,6 +651,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) { @@ -1555,6 +1557,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> { @@ -1642,6 +1660,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); @@ -1802,6 +1822,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 +1978,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. diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h index d4c233a7f8..0c3a9b3818 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; @@ -189,7 +187,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/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc index 6da375aef0..e793c6b12c 100644 --- a/compiler/trampolines/trampoline_compiler.cc +++ b/compiler/trampolines/trampoline_compiler.cc @@ -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/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index 75823e336b..db7151c3c1 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -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); } diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 1d9eba446a..4ffb6b5663 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -436,6 +436,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); diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index 7a48b638e7..69a5fa0dba 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -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"); diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk index b7e649ae38..0bab429e83 100644 --- a/dalvikvm/Android.mk +++ b/dalvikvm/Android.mk @@ -55,7 +55,11 @@ LOCAL_SRC_FILES := dalvikvm.cc ../sigchainlib/sigchain.cc LOCAL_CFLAGS := $(dalvikvm_cflags) LOCAL_C_INCLUDES := art/runtime LOCAL_SHARED_LIBRARIES := libnativehelper -LOCAL_LDFLAGS := -ldl -lpthread -Wl,--export-dynamic +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 7be4349067..326fa729b2 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -251,13 +251,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, @@ -482,7 +492,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) @@ -527,7 +537,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) { @@ -556,7 +566,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_; @@ -728,38 +738,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) { @@ -848,17 +826,19 @@ 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; - // 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; @@ -961,9 +941,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") { @@ -975,6 +966,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)) { @@ -1159,6 +1152,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. @@ -1225,7 +1225,8 @@ static int dex2oat(int argc, char** argv) { include_debug_symbols, implicit_null_checks, implicit_so_checks, - implicit_suspend_checks + implicit_suspend_checks, + compile_pic #ifdef ART_SEA_IR_MODE , compiler_options.sea_ir_ = true; @@ -1262,6 +1263,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) { @@ -1282,8 +1284,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, @@ -1291,7 +1292,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/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 4ed428c200..c0c96e5ac3 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -27,6 +27,7 @@ #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 +514,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 +532,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) { @@ -758,6 +758,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; @@ -800,7 +801,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 f37b4ea505..d9b41397ae 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -218,11 +218,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 @@ -317,7 +317,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 +328,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 @@ -400,6 +423,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) @@ -408,18 +434,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) @@ -434,11 +467,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) @@ -495,6 +523,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/arm/asm_support_arm.S b/runtime/arch/arm/asm_support_arm.S index a3e3b21c7e..fb6458c9d4 100644 --- a/runtime/arch/arm/asm_support_arm.S +++ b/runtime/arch/arm/asm_support_arm.S @@ -42,18 +42,6 @@ .fnstart .endm -.macro ENTRY_NO_HIDE name - .thumb_func - .type \name, #function - .global \name - /* Cache alignment for function entry */ - .balign 16 -\name: - .cfi_startproc - .fnstart -.endm - - .macro ARM_ENTRY name .arm .type \name, #function @@ -68,19 +56,6 @@ .fnstart .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 -.endm - .macro END name .fnend .cfi_endproc 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..a34db6c6c7 100644 --- a/runtime/arch/arm/portable_entrypoints_arm.S +++ b/runtime/arch/arm/portable_entrypoints_arm.S @@ -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..3d619be0cd 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -919,7 +919,7 @@ END art_quick_resolution_trampoline /* * Called to do a generic JNI down-call */ -ENTRY_NO_HIDE art_quick_generic_jni_trampoline +ENTRY art_quick_generic_jni_trampoline SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME str r0, [sp, #0] // Store native ArtMethod* to bottom of stack. @@ -1014,7 +1014,7 @@ ENTRY_NO_HIDE art_quick_generic_jni_trampoline END art_quick_generic_jni_trampoline .extern artQuickToInterpreterBridge -ENTRY_NO_HIDE art_quick_to_interpreter_bridge +ENTRY art_quick_to_interpreter_bridge SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME mov r1, r9 @ pass Thread::Current mov r2, sp @ pass SP 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/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..ab9bf2d34e 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1438,7 +1438,7 @@ END art_quick_resolution_trampoline /* * Called to do a generic JNI down-call */ -ENTRY_NO_HIDE art_quick_generic_jni_trampoline +ENTRY art_quick_generic_jni_trampoline SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL str x0, [sp, #0] // Store native ArtMethod* to bottom of stack. @@ -1534,7 +1534,7 @@ 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 +ENTRY art_quick_to_interpreter_bridge SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME // Set up frame and save arguments. // x0 will contain mirror::ArtMethod* method. 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/portable_entrypoints_mips.S b/runtime/arch/mips/portable_entrypoints_mips.S index 7545ce0d6c..a171a1d6cc 100644 --- a/runtime/arch/mips/portable_entrypoints_mips.S +++ b/runtime/arch/mips/portable_entrypoints_mips.S @@ -131,3 +131,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/stub_test.cc b/runtime/arch/stub_test.cc index 6b74a1b1d2..c9b9f04d69 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__( diff --git a/runtime/arch/x86/asm_support_x86.S b/runtime/arch/x86/asm_support_x86.S index efbbfb3f10..78b97e5cbe 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) 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..70c0ae2d94 100644 --- a/runtime/arch/x86/portable_entrypoints_x86.S +++ b/runtime/arch/x86/portable_entrypoints_x86.S @@ -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..a158e6db1f 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1140,7 +1140,7 @@ DEFINE_FUNCTION art_quick_resolution_trampoline DELIVER_PENDING_EXCEPTION END_FUNCTION art_quick_resolution_trampoline -DEFINE_FUNCTION_NO_HIDE art_quick_generic_jni_trampoline +DEFINE_FUNCTION 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. @@ -1220,7 +1220,7 @@ DEFINE_FUNCTION_NO_HIDE art_quick_generic_jni_trampoline DELIVER_PENDING_EXCEPTION END_FUNCTION art_quick_generic_jni_trampoline -DEFINE_FUNCTION_NO_HIDE art_quick_to_interpreter_bridge +DEFINE_FUNCTION art_quick_to_interpreter_bridge SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME // save frame mov %esp, %edx // remember SP PUSH eax // alignment padding 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/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..648a99a0ec 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1266,7 +1266,7 @@ END_FUNCTION art_quick_resolution_trampoline /* * Called to do a generic JNI down-call */ -DEFINE_FUNCTION_NO_HIDE art_quick_generic_jni_trampoline +DEFINE_FUNCTION 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. @@ -1453,7 +1453,7 @@ 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 +DEFINE_FUNCTION art_quick_to_interpreter_bridge SETUP_REF_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 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/logging.cc b/runtime/base/logging.cc index b2ad1d06d6..5af597b89b 100644 --- a/runtime/base/logging.cc +++ b/runtime/base/logging.cc @@ -85,7 +85,7 @@ void InitLogging(char* argv[]) { } 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]); diff --git a/runtime/base/macros.h b/runtime/base/macros.h index f5a38bbf35..c80d35e42b 100644 --- a/runtime/base/macros.h +++ b/runtime/base/macros.h @@ -63,6 +63,11 @@ struct CompileAssert { #define COMPILE_ASSERT(expr, msg) \ typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] // NOLINT +// 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) \ diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index cbcd408b6d..70b6f7e295 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -83,18 +83,25 @@ 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_; }; diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index cf3a581668..bbbb9e0b81 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -32,6 +32,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 +116,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 +143,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 +237,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 +255,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 +438,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 +527,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 +1622,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 +2491,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 +2645,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 +2671,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 +2691,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 +2707,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 +2729,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 +3181,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 +4119,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 +4132,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 +4151,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 +4248,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 +4282,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 +4301,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()) { @@ -5531,6 +5503,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 +5619,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..ea3da648fc 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -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 971ff89036..b676c62ee1 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('='); 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..676cd522e6 100644 --- a/runtime/elf_utils.h +++ b/runtime/elf_utils.h @@ -67,11 +67,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: 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/quick_instrumentation_entrypoints.cc b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc index 42ace40637..bb0e5e31c8 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" @@ -38,8 +39,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); diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 96903db414..224756bbc2 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" @@ -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. diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc index 02b8a5b605..41af88e638 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; } }; 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..1714134d25 100644 --- a/runtime/exception_test.cc +++ b/runtime/exception_test.cc @@ -175,7 +175,7 @@ TEST_F(ExceptionTest, StackTraceElement) { 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_)); @@ -194,7 +194,7 @@ TEST_F(ExceptionTest, StackTraceElement) { // 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 + method_g_->ToNativeQuickPc(dex_pc)); // return pc } else { // Create/push fake 20-byte shadow frame for method g fake_stack.push_back(0); diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc index fede2f8ec3..62e06090be 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"; 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/heap.h b/runtime/gc/heap.h index c09dca8c4d..ba85c55c96 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" 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..39d82cc8a3 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -118,7 +118,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/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..6c6058f81e 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -25,6 +25,7 @@ #include "debugger.h" #include "dex_file-inl.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" @@ -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()); @@ -669,11 +670,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 +793,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 +875,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; } } 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/java_vm_ext.cc b/runtime/java_vm_ext.cc index 1444d9762a..fed8bf07be 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/java_vm_ext.cc @@ -21,6 +21,7 @@ #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 +689,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..70754f28ec 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++; } } diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 231e9e56b0..3144ce1643 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -70,7 +70,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 +457,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 +484,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 +616,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 +628,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 +641,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..5c72e5567d 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,6 +283,36 @@ 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); @@ -294,6 +326,31 @@ bool ArtMethod::IsEntrypointInterpreter() { } } +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())) { @@ -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..1dbfe5de8b 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,19 @@ 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. 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 +384,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 +430,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.h b/runtime/mirror/class.h index 2d4912191b..4383993fde 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -507,7 +507,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); } 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/oat.cc b/runtime/oat.cc index 6810d73cbb..a237bf6a7e 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -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/parsed_options.cc b/runtime/parsed_options.cc index d82002683a..dcca9d3164 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -205,7 +205,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 +501,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; @@ -536,7 +536,7 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize } } else if (StartsWith(option, "-verbose-methods:")) { gLogVerbosity.compiler = false; - Split(option.substr(strlen("-verbose-methods:")), ',', gVerboseMethods); + 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..1d06d352a9 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" @@ -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..8e578374c0 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. } @@ -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; 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..adf0994acb 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -43,10 +43,12 @@ #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 "base/unix_file/fd_file.h" #include "atomic.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 +94,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), @@ -193,6 +194,7 @@ 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; @@ -645,6 +647,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 +804,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); @@ -911,7 +915,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 +1217,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 +1237,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 +1455,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 7bffc335ef..f3bea17d6d 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -483,10 +483,6 @@ class Runtime { return target_sdk_version_; } - static const char* GetDefaultInstructionSetFeatures() { - return kDefaultInstructionSetFeatures; - } - private: static void InitPlatformSignalHandlers(); @@ -506,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; diff --git a/runtime/stack.cc b/runtime/stack.cc index 008941fcee..b4e85e2c23 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); } } @@ -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/thread.cc b/runtime/thread.cc index fd37703949..efe27eefde 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -2108,7 +2108,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) { @@ -2137,7 +2137,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..32ed758ef7 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. diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 35411e2660..646830acc6 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -34,6 +34,7 @@ #include "monitor.h" #include "scoped_thread_state_change.h" #include "thread.h" +#include "trace.h" #include "utils.h" #include "well_known_classes.h" @@ -877,6 +878,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..91a37fddaf 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -706,9 +706,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/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc index c5015e8d0f..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) { @@ -147,7 +149,20 @@ extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) } } -// 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); + } +} 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 @@ -179,9 +194,7 @@ extern "C" int sigaction(int signal, const struct sigaction* new_action, struct 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); } @@ -287,5 +300,6 @@ extern "C" void InitializeSignalChain() { } initialized = true; } + } // namespace art diff --git a/sigchainlib/sigchain.h b/sigchainlib/sigchain.h index 0de0d080e8..79b76a74da 100644 --- a/sigchainlib/sigchain.h +++ b/sigchainlib/sigchain.h @@ -19,6 +19,8 @@ #include <signal.h> +namespace art { + extern "C" void InitializeSignalChain(); extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction); @@ -27,4 +29,8 @@ extern "C" void UnclaimSignalChain(int signal); extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context); +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 index 7176f052cb..fbc8c3fb5c 100644 --- a/sigchainlib/sigchain_dummy.cc +++ b/sigchainlib/sigchain_dummy.cc @@ -57,3 +57,8 @@ 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 index 8030da4d65..ce1505490b 100644 --- a/sigchainlib/version-script.txt +++ b/sigchainlib/version-script.txt @@ -4,6 +4,7 @@ global: UnclaimSignalChain; InvokeUserSignalHandler; InitializeSignalChain; + EnsureFrontOfChain; sigaction; signal; sigprocmask; 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 4ebbdc57f3..e3cf4eedbc 100644 --- a/test/036-finalizer/src/Main.java +++ b/test/036-finalizer/src/Main.java @@ -125,18 +125,28 @@ 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; } } } @@ -149,11 +159,21 @@ public class Main { } private static void runFinalizationTest() { - int count = 1024; - allocFinalizableObjects(count); + 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 6ee7dce503..32fbf2d103 100644 --- a/test/074-gc-thrash/src/Main.java +++ b/test/074-gc-thrash/src/Main.java @@ -255,54 +255,55 @@ 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) { diff --git a/test/124-missing-classes/build b/test/124-missing-classes/build new file mode 100644 index 0000000000..62e57c86da --- /dev/null +++ b/test/124-missing-classes/build @@ -0,0 +1,28 @@ +#!/bin/bash +# +# Copyright (C) 2012 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Stop if something fails. +set -e + +mkdir classes + +# Some classes are available at compile time... +${JAVAC} -d classes `find src -name '*.java'` + +# ...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/test/124-missing-classes/src/MissingClass.java b/test/124-missing-classes/src/MissingClass.java new file mode 100644 index 0000000000..33aaa5625f --- /dev/null +++ b/test/124-missing-classes/src/MissingClass.java @@ -0,0 +1,20 @@ +/* + * 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 MissingClass { + public static final class MissingInnerClass { + } +} 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..74c47a606c --- /dev/null +++ b/test/411-optimizing-arith/src/Main.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// 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 main(String[] args) { + mul(); + } + + public static void mul() { + expectEquals(15, $opt$Mul(5, 3)); + 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 + + expectEquals(15L, $opt$Mul(5L, 3L)); + 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) * 11); + expectEquals(240518168583L, $opt$Mul(34359738369L, 7L)); // (2^35 + 1) * 7 + } + + static int $opt$Mul(int a, int b) { + return a * b; + } + + static long $opt$Mul(long a, long b) { + return a * b; + } + +} diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk index 2d139a64b9..fd9503896d 100644 --- a/test/Android.libarttest.mk +++ b/test/Android.libarttest.mk @@ -69,7 +69,7 @@ define build-libarttest 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.libnativebridgetest.mk b/test/Android.libnativebridgetest.mk index dd7255a4c9..5e2493c8bf 100644 --- a/test/Android.libnativebridgetest.mk +++ b/test/Android.libnativebridgetest.mk @@ -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..e066a387fa 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) |